Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
commit
6ccf1792d7
2580 files changed
+1040202
No files matched your search
@@ -0,0 +1,493 @@
|
||||
# Multiple Platforms, Servers and Instances: White-Label Guide
|
||||
|
||||
How to set up and maintain a branded platform instance (Timebank.cc, Uuro or any other) running this
|
||||
application. One instance runs per server; every instance runs the same code from git and differs only in
|
||||
its `.env`, its platform folder `platform/` (config, theme, logos, favicons, text and template overrides; outside
|
||||
git) and its database content.
|
||||
|
||||
The platform folder is the recommended place for everything platform-specific. The older way, gitignored files in
|
||||
`config/` and uploaded branding files in `storage/`, keeps working; the platform folder wins where both exist.
|
||||
|
||||
| Instance | Server | Platform files (2026-10-19) |
|
||||
|---|---|---|
|
||||
| Timebank.cc | production | the older way: `config/timebank_cc.php`, `config/themes.php`; its logos and favicons are the defaults in the code. Moving it into `platform/` is planned (`php artisan platform:init --from-current`). |
|
||||
| Uuro | staging | its own branch `uuro` until its Laravel 11 upgrade; then everything in `platform/` (runbook: [RUNBOOK_LARAVEL_10_TO_11.md](RUNBOOK_LARAVEL_10_TO_11.md)). |
|
||||
|
||||
Last checked against the code: 2026-10-19 (branch `laravel-11`, cd982cdd).
|
||||
|
||||
---
|
||||
|
||||
## Contents
|
||||
|
||||
1. [Checklist for a new platform instance](#1-checklist-for-a-new-platform-instance)
|
||||
2. [What makes an instance different](#2-what-makes-an-instance-different)
|
||||
3. [The platform folder](#3-the-platform-folder)
|
||||
4. [Platform configuration](#4-platform-configuration)
|
||||
5. [Theme](#5-theme)
|
||||
6. [Logos and favicons](#6-logos-and-favicons)
|
||||
7. [Platform name in texts and translations](#7-platform-name-in-texts-and-translations)
|
||||
8. [Platform-specific pages and switches](#8-platform-specific-pages-and-switches)
|
||||
9. [Footer](#9-footer)
|
||||
10. [Custom content pages](#10-custom-content-pages)
|
||||
11. [Keeping an instance up to date](#11-keeping-an-instance-up-to-date)
|
||||
12. [Troubleshooting](#12-troubleshooting)
|
||||
13. [Quick reference](#13-quick-reference)
|
||||
14. [Related documentation](#14-related-documentation)
|
||||
|
||||
---
|
||||
|
||||
## 1. Checklist for a new platform instance
|
||||
|
||||
The server itself (PHP, MySQL, Redis, Elasticsearch, Reverb, mail) is set up with
|
||||
[SETUP_GUIDE.md](SETUP_GUIDE.md) and [PRODUCTION_DEPLOYMENT.md](PRODUCTION_DEPLOYMENT.md). Then, for the
|
||||
platform:
|
||||
|
||||
1. **`.env`**: set at least
|
||||
- `APP_NAME`, `APP_URL`
|
||||
- `TIMEBANK_CONFIG=<platform>`: the platform's name, e.g. `myorg`. `timebank_config()` reads the platform
|
||||
config under this name; see [section 4](#4-platform-configuration).
|
||||
- `TIMEBANK_THEME=<theme>`: `timebank_cc`, `uuro`, `vegetable` or `yellow`; not needed when the platform folder
|
||||
has a `theme.php` of its own
|
||||
- `DEPLOY_ENVIRONMENT=server`, `DEPLOY_APP_DIR`, `DEPLOY_SERVER_TYPE`, `DEPLOY_WS_URL`
|
||||
- `DB_DEPLOY_USERNAME` / `DB_DEPLOY_PASSWORD`: a MySQL user that may alter and restore the database
|
||||
- `MAIL_*`, `MAIL_SUPPORT_ADDRESS`, `MAIL_BOUNCE_ADDRESS`
|
||||
2. **Platform folder** ([section 3](#3-the-platform-folder)), in the application root:
|
||||
|
||||
```bash
|
||||
php artisan platform:init # platform/ from platform.example/
|
||||
cp config/timebank-default.php.example platform/config.php # the platform config, from the template
|
||||
```
|
||||
|
||||
3. **Platform config** `platform/config.php`: names, currency and terms per language, mail addresses,
|
||||
account limits, the switches in [section 8](#8-platform-specific-pages-and-switches), and the footer. The
|
||||
template uses `Example Timebank` and `@example.org` as placeholders: replace all of them.
|
||||
4. **Theme**: pick a built-in one with `TIMEBANK_THEME`, or make your own in `platform/theme.php`
|
||||
([section 5](#5-theme)).
|
||||
5. **Branding files** in `platform/public/`: `mail-logo.png`, `mail-logo-inverted.png`, the favicons and
|
||||
`og-image.png` ([section 6](#6-logos-and-favicons)); a logo of your own in `platform/views/logos/<theme>.blade.php`.
|
||||
Without them, emails get a generated text logo with the platform name, and the site uses the default favicons.
|
||||
6. **First deploy**: `./deploy.sh -e server`. It creates the config files the code still needs from their
|
||||
templates (`config/themes.php` and the two template configs; not `config/<platform>.php` when
|
||||
`platform/config.php` exists), merges new keys into `platform/config.php` and links `platform/public` at
|
||||
`/platform`.
|
||||
7. **Privacy policy**: put your policy in `references/gdpr/<platform>/<YYYY-MM-DD>/privacy-policy-FULL-<locale>.md`
|
||||
(the download link on the privacy page reads the newest dated folder). Without it the download returns 404.
|
||||
8. **Content**: static pages (FAQ, principles, getting started and so on) are posts in the database; create
|
||||
them in the admin area ([section 10](#10-custom-content-pages)).
|
||||
9. **Background services**: queue workers with all queues, Reverb and the scheduler
|
||||
([QUEUE_WORKERS_SETUP.md](QUEUE_WORKERS_SETUP.md), [WEBSOCKET_SETUP.md](WEBSOCKET_SETUP.md)), search
|
||||
([ELASTICSEARCH_SETUP.md](ELASTICSEARCH_SETUP.md)), bounce handling ([BOUNCE_SETUP.md](BOUNCE_SETUP.md)).
|
||||
10. **Check**: open the site with your theme, log in, switch profiles, send a test mail
|
||||
([EMAIL-TESTING-GUIDE.md](EMAIL-TESTING-GUIDE.md)), and look for the old brand: no "Timebank.cc",
|
||||
no `example.org` addresses, no city pages you do not want. `php artisan platform:check` reports which config is
|
||||
used and whether `/platform` is linked.
|
||||
11. **Off-site copy**: put `platform/` in a private git repository of its own (no secrets in it: those stay in
|
||||
`.env`); the server's backups stay on the server.
|
||||
|
||||
---
|
||||
|
||||
## 2. What makes an instance different
|
||||
|
||||
| Layer | In the platform folder (recommended) | The older way (still works) | In git |
|
||||
|---|---|---|---|
|
||||
| Environment, passwords | `.env` | `.env` | no |
|
||||
| Platform behaviour, names, mail, limits, footer, switches | `platform/config.php` | `config/<TIMEBANK_CONFIG>.php` | no; templates `config/*.php.example` are |
|
||||
| Theme colours, fonts, logo references | `platform/theme.php` | an entry in `config/themes.php`, chosen with `TIMEBANK_THEME` | no; template `config/themes.php.example` is |
|
||||
| Website logo (SVG) | `platform/views/logos/<theme>.blade.php` | `resources/views/logos/<theme>.blade.php` | the older way is |
|
||||
| Email/PDF logos, favicons, preview image | `platform/public/` | `storage/app/public/app-images/`, `public/<favicon_dir>`, `public/<og_image>` | no |
|
||||
| Texts and templates | `platform/lang/`, `platform/views/` (overrides) | none: shared by all instances | no |
|
||||
| Static page content, posts, categories | database | database | no |
|
||||
| Privacy policy | `references/gdpr/<platform>/` | the same | yes |
|
||||
|
||||
Everything else, the application code and the translations, is shared by all instances; a platform changes
|
||||
texts and templates through overrides in its platform folder, never in the shared files.
|
||||
|
||||
**Config protection.** The platform folder and the active config files are gitignored, so a `git pull` never
|
||||
overwrites them.
|
||||
`deploy.sh` creates a missing one from its `.example` template, and `php artisan config:merge` copies keys
|
||||
that are new in the template into your file without changing your values. Details:
|
||||
[CONFIG_MANAGEMENT.md](CONFIG_MANAGEMENT.md).
|
||||
|
||||
---
|
||||
|
||||
## 3. The platform folder
|
||||
|
||||
Everything that makes an installation one platform can live in one folder, `platform/` in the project root
|
||||
(`PLATFORM_PATH` in `.env` moves it). It is outside git (`.gitignore`), so deploys, upgrades and rollbacks never
|
||||
touch it, and the same code from `main` runs on every server. Every part is optional: without the folder, or
|
||||
without one of its parts, the installation runs on `config/` and the files in the code as described in the
|
||||
sections below. Loaded by `App\Providers\PlatformServiceProvider`.
|
||||
|
||||
| Path | What | Behaviour |
|
||||
|---|---|---|
|
||||
| `config.php` | The platform config | Replaces `config/<TIMEBANK_CONFIG>.php`; `config:merge` adds new keys to it |
|
||||
| `theme.php` | One theme, with its `id` | Becomes the active theme (instead of `TIMEBANK_THEME`) |
|
||||
| `lang/<locale>.json`, `lang/<locale>/<group>.php`, `lang/vendor/<package>/...` | Changed texts, only the keys you change | Merged key by key over the code's texts; placeholders keep working |
|
||||
| `views/...`, `views/mail/...`, `views/vendor/<package>/...` | Changed templates: pages, emails, the email layout, the logo, package views | Replace the whole template |
|
||||
| `public/` | Favicons, `mail-logo.png`, `mail-logo-inverted.png` | Served at `/platform/` after `php artisan platform:link` (deploy.sh runs it); used before the theme's files |
|
||||
|
||||
The tracked skeleton `platform.example/` (with a README per path) is the starting point.
|
||||
|
||||
```bash
|
||||
php artisan platform:init # create platform/ from platform.example/
|
||||
php artisan platform:init --from-current # also copy config/<name>.php and the active theme into it
|
||||
php artisan platform:override emails.layouts.html # copy a template to change it (also mail::html.header,
|
||||
# wirechat::components.divider)
|
||||
php artisan platform:link # serve platform/public at /platform
|
||||
php artisan platform:check # read-only: which config is used, stale overrides
|
||||
```
|
||||
|
||||
**Texts or templates?** 31 of the 34 email templates take their wording from translation strings, so a text
|
||||
override changes almost every email. Override a template only for another structure or layout.
|
||||
|
||||
**Overrides go stale.** A translation key is the English source sentence: when the code rewords it, an override
|
||||
of the old sentence is no longer used. A copied template misses later fixes to its original.
|
||||
`php artisan platform:check` reports both: keys the code no longer has (keys that the platform's own templates
|
||||
use count as its own texts) and templates whose original changed since `platform:override` copied it (the first
|
||||
line holds the original's fingerprint). Run it after every update.
|
||||
|
||||
**Moving an existing installation into the folder.** `php artisan platform:init --from-current`, copy the brand
|
||||
files it lists into `platform/public/`, `php artisan platform:link`, check the site. `config/<name>.php` stays as a
|
||||
fallback until you remove it; `platform:check` warns while both exist.
|
||||
|
||||
**Good practice.**
|
||||
- No secrets in the folder: passwords and keys stay in `.env`. Then the folder can be a private git repository of
|
||||
its own, which gives history and an off-site copy (the server backups stay on the server). The storage backup
|
||||
(`scripts/backup-storage.sh`) also archives it, in `backups/platform/`.
|
||||
- The web server only reads the folder; normal file permissions are enough.
|
||||
- After a change: `php artisan view:clear` for templates; restart the queue workers and Reverb after a config
|
||||
change (they read the config when they start).
|
||||
- Tests never read a developer's own platform folder (`PLATFORM_PATH` in `phpunit.xml` points elsewhere): they run
|
||||
on `config/<TIMEBANK_CONFIG>.php`. A developer whose config moved into `platform/` therefore keeps that file too
|
||||
(92 tests failed on the template alone, 2026-10-19: they depend on the developer's config values).
|
||||
|
||||
---
|
||||
|
||||
## 4. Platform configuration
|
||||
|
||||
**File**: `platform/config.php`; without it, `config/<name>.php`, where `<name>` is `TIMEBANK_CONFIG` in `.env`.
|
||||
Either way the config is read under that name, so code uses `timebank_config('key')`. Any name works:
|
||||
`timebank_cc` and `timebank-default` exist as templates, and a platform can use its own (`uuro`, `myorg`):
|
||||
|
||||
- Start `platform/config.php` from `config/timebank-default.php.example`. Without a platform folder, `deploy.sh`
|
||||
creates `config/<name>.php` from that template when it is missing, and adds it to `.git/info/exclude` on that
|
||||
server, so deploys do not report it as an uncommitted change.
|
||||
- A development machine keeps `config/<name>.php` even with a platform folder: the tests read that file and never the
|
||||
platform folder.
|
||||
- `php artisan config:merge <name>` (and `--all`) adds new keys to the platform config (to `platform/config.php`
|
||||
when it exists) from `config/<name>.php.example` if that exists, otherwise from
|
||||
`config/timebank-default.php.example`. Lists (footer links, payment types, rules) are left as they are.
|
||||
|
||||
Do not copy the active `config/timebank-default.php` of another server: it may be outdated or hold that
|
||||
server's settings. Start from the template.
|
||||
|
||||
**Main sections** (see the comments in the template for every key):
|
||||
|
||||
| Key | What |
|
||||
|---|---|
|
||||
| `platform_translations.<locale>.*` | `platform_name`, `platform_name_legal`, `platform_name_short`, `platform_slogan`, currency name, plural, symbol and position, thousand separator, `platform_user(s)`, `platform_principles`, for each language (en, nl, de, es, fr) |
|
||||
| `mail.*` | `system_admin`, `content_admin`, `payments`, `chat_messenger`, `support` addresses and names; `platform_sign_off_signature` per language |
|
||||
| `accounts.<type>` and `profiles.<type>` | Account limits (`limit_min`, `limit_max` in minutes) and the transaction types an account may receive, per profile type |
|
||||
| `permissions.<type>` | Which transaction types each profile type may make |
|
||||
| `account_info.<type>.*` | Which balances and totals are shown publicly (`*_public`), to the owner (`*_self`) and to privileged viewers (`*_privileged`, see `privileged_viewer_types`) |
|
||||
| `local_pages`, `android_app` | Platform-specific pages, see [section 8](#8-platform-specific-pages-and-switches) |
|
||||
| `footer` | Footer sections and links, see [section 9](#9-footer) |
|
||||
| `seo` | `allow_indexing_guest`, `allow_indexing_auth` |
|
||||
| `profile_inactive`, `profile_incomplete`, `delete_profile` | See [PROFILE_INACTIVE_CONFIG.md](PROFILE_INACTIVE_CONFIG.md), [PROFILE_INCOMPLETE_CONFIG.md](PROFILE_INCOMPLETE_CONFIG.md), [PROFILE_AUTO_DELETE.md](PROFILE_AUTO_DELETE.md) |
|
||||
| `main_search_bar`, `search_optimization` | Search behaviour and boosts, see [SEARCH_REFERENCE.md](SEARCH_REFERENCE.md) |
|
||||
| `cert_monitor`, `health_monitor` | Certificate expiry warnings and the health check, mostly set through `.env` |
|
||||
|
||||
**Reading values in code**:
|
||||
|
||||
```php
|
||||
timebank_config('accounts.user.limit_max'); // the active platform's value
|
||||
timebank_config('local_pages.the-hague', true); // with a default for a missing key
|
||||
config('app.timebank_config'); // the platform config name itself
|
||||
platform_name(); platform_currency_symbol(); // wording helpers, current locale
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Theme
|
||||
|
||||
**File**: `platform/theme.php`, one theme with its own `id`, which becomes the active theme. Without it: the theme
|
||||
named by `TIMEBANK_THEME` in `config/themes.php` (template `config/themes.php.example`).
|
||||
|
||||
Themes are applied at runtime through CSS variables, so **one asset build serves every theme**: switching
|
||||
`TIMEBANK_THEME` needs no `npm run build`. Only `VITE_*` values in `.env` (such as the Reverb host) are
|
||||
compiled into the build.
|
||||
|
||||
**A theme of your own** belongs in `platform/theme.php`: copy a whole theme block from `config/themes.php.example`
|
||||
(every colour key is needed: a missing one leaves its CSS variable empty), give it an `id`, and change the name,
|
||||
colours, typography and logos. `php artisan platform:init --from-current` writes the active theme there for you.
|
||||
Its logo view may live in the platform folder too (`platform/views/logos/mybrand.blade.php`): nothing of the theme
|
||||
needs to be in the code.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
return [
|
||||
'id' => 'mybrand',
|
||||
'name' => 'My Brand',
|
||||
'description' => '...',
|
||||
'logos' => [
|
||||
'svg_inline' => 'logos.mybrand', // platform/views/logos/mybrand.blade.php
|
||||
'email_logo' => null, // platform/public/mail-logo.png is used first
|
||||
'email_logo_inverted' => null, // platform/public/mail-logo-inverted.png
|
||||
'favicon_dir' => null, // platform/public/ is used first
|
||||
'og_image' => null, // platform/public/og-image.png is used first
|
||||
],
|
||||
'colors' => [ 'brand' => '#...', 'logo' => '#...', 'primary' => [50 => '#...', /* ... */ 900 => '#...'], /* ... */ ],
|
||||
'typography' => [ 'font_family_body' => '...', 'font_family_heading' => '...', /* ... */ ],
|
||||
];
|
||||
```
|
||||
|
||||
The older way still works: the same block under a new key in `config/themes.php`, selected with `TIMEBANK_THEME`.
|
||||
|
||||
A new theme that should be available to every installation belongs in `config/themes.php.example` plus a
|
||||
logo view in `resources/views/logos/`, committed to git.
|
||||
|
||||
**In views and code**, always use theme classes and helpers, never fixed colours: `bg-theme-brand`,
|
||||
`text-theme-primary`, `border-theme-border`, `theme_color('primary.500')`, `theme_font('font_family_body')`.
|
||||
UI patterns: [STYLE_GUIDE.md](STYLE_GUIDE.md), [THEME_CLASS_CONVERSION_GUIDE.md](THEME_CLASS_CONVERSION_GUIDE.md).
|
||||
Technical details: [THEME_IMPLEMENTATION.md](THEME_IMPLEMENTATION.md).
|
||||
|
||||
---
|
||||
|
||||
## 6. Logos and favicons
|
||||
|
||||
Each asset is looked up in this order: the platform folder, the theme setting, the fallback.
|
||||
|
||||
| Asset | Platform folder | Theme setting (the older way) | Fallback when missing |
|
||||
|---|---|---|---|
|
||||
| Website logo (inline SVG) | `platform/views/logos/<theme>.blade.php` | `resources/views/logos/<theme>.blade.php` (in git; see `resources/views/logos/README.md`) | none: required for a theme |
|
||||
| Email and PDF logo | `platform/public/mail-logo.png` | `storage/app/public/<logos.email_logo>`, e.g. `app-images/uuro_mail_logo.png` | a generated PNG with the platform name in the theme's logo colour; the `timebank_cc` theme falls back to `app_mail_logo.png` |
|
||||
| Email logo for dark headers (newsletter) | `platform/public/mail-logo-inverted.png` | `storage/app/public/<logos.email_logo_inverted>` | the generated logo in white; `timebank_cc`: `app_mail_logo_inverted.png` |
|
||||
| Favicons: `favicon.ico`, `favicon-32x32.png`, `favicon-16x16.png`, `apple-touch-icon.png` | `platform/public/` | `public/<logos.favicon_dir>/` | the files in `public/` |
|
||||
| Optional icons: `favicon.svg`, `favicon-96x96.png`, `site.webmanifest` | `platform/public/` | `public/<logos.favicon_dir>/` | the file in `public/`, else not linked |
|
||||
| Preview image for shared links (`og:image`, about 1200×630) | `platform/public/og-image.png` | `public/<logos.og_image>` | no preview image; the other Open Graph tags (title, site name, URL, locale) are always there |
|
||||
|
||||
Files in `platform/public/` are served at `/platform/...` once `php artisan platform:link` has run (`deploy.sh`
|
||||
runs it). The layouts include one partial for the title, the icons and the Open Graph tags:
|
||||
`resources/views/layouts/partials/brand-head.blade.php`.
|
||||
|
||||
**Email logo**: a PNG of about 900×700 pixels with a transparent background works best (emails show it about
|
||||
120 pixels wide). Put it in `platform/public/mail-logo.png` (or, the older way, upload it to
|
||||
`storage/app/public/app-images/` and name it in the theme). Code reads it
|
||||
through `theme_email_logo_url()` (emails) and `theme_email_logo_file()` (PDFs), with `true` for the inverted logo; the generated fallback is
|
||||
`App\Services\GeneratedLogo`, cached in `storage/app/public/app-images/generated/`.
|
||||
|
||||
**Favicons**: put the files in `platform/public/` (or, the older way, in
|
||||
`storage/app/public/app-images/favicons/<theme>/` with `'favicon_dir' => 'storage/app-images/favicons/<theme>'` in
|
||||
the theme). Layouts read them through `theme_favicon('favicon.ico')` and, for the optional ones,
|
||||
`theme_favicon_optional()`; the preview image through `theme_og_image()`.
|
||||
|
||||
The platform folder, uploaded logos (`*_mail_logo*.png`) and favicons are gitignored, so they do not appear as
|
||||
changes in `git status` and survive every deploy.
|
||||
|
||||
---
|
||||
|
||||
## 7. Platform name in texts and translations
|
||||
|
||||
Translations are in `resources/lang/<locale>.json` (and `resources/lang/<locale>/*.php`), shared by all
|
||||
platforms. Strings that mention the platform use placeholders, replaced from `platform_translations`:
|
||||
|
||||
| Placeholder | Replaced by |
|
||||
|---|---|
|
||||
| `@PLATFORM_NAME@`, `@PLATFORM_NAME_SHORT@`, `@PLATFORM_NAME_LEGAL@`, `@PLATFORM_SLOGAN@` | names |
|
||||
| `@PLATFORM_USER@`, `@PLATFORM_USERS@`, `@PLATFORM_PRINCIPLES@` | terms |
|
||||
| `@PLATFORM_CURRENCY_NAME@`, `@PLATFORM_CURRENCY_NAME_PLURAL@`, `@PLATFORM_CURRENCY_SYMBOL@` | currency |
|
||||
|
||||
In code, render such a string with `trans_with_platform('Why @PLATFORM_NAME_SHORT@?')`, not with `__()`
|
||||
(which leaves the placeholder in place). Never write a brand name into a translation key or a view. The
|
||||
wording helpers `platform_name()`, `platform_users()`, `platform_currency_symbol()` and so on return the
|
||||
values directly. Translation workflow: `references/translations/`.
|
||||
|
||||
A platform that wants other wording overrides single texts in its platform folder (`platform/lang/`,
|
||||
[section 3](#3-the-platform-folder)) instead of changing the shared files.
|
||||
|
||||
---
|
||||
|
||||
## 8. Platform-specific pages and switches
|
||||
|
||||
Some pages only make sense for one platform. They are switched in the platform config:
|
||||
|
||||
```php
|
||||
'local_pages' => [ // static pages about Timebank.cc's local groups
|
||||
'the-hague' => false,
|
||||
'lekkernassuh' => false,
|
||||
'amst-brus-lisb' => false,
|
||||
],
|
||||
'android_app' => [
|
||||
'enabled' => false, // the Android app page and the app's update check (/app/version)
|
||||
'apk_file' => 'app-{version}.apk', // in storage/app/public/apk/; {version} is the version name
|
||||
],
|
||||
```
|
||||
|
||||
A page that is off has no route (404), and footer links to it are left out automatically. The default
|
||||
template has all of them off; the Timebank.cc template has them on. A key missing from an older config keeps
|
||||
the old behaviour (on) until `config:merge` adds it.
|
||||
|
||||
For an Android app of your own, see [ANDROID_APP_UPDATE_FLOW.md](ANDROID_APP_UPDATE_FLOW.md);
|
||||
`scripts/deploy-apk.sh` uploads an APK to another server with `APK_REMOTE_HOST=... APK_REMOTE_APP_DIR=...`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Footer
|
||||
|
||||
The footer is built from `footer` in the platform config:
|
||||
|
||||
```php
|
||||
'footer' => [
|
||||
'sections' => [
|
||||
[
|
||||
'title' => 'Who we are', // translation key
|
||||
'order' => 1, // left to right
|
||||
'visible' => true,
|
||||
'links' => [
|
||||
['route' => 'static-faq', 'title' => 'FAQ', 'order' => 1, 'visible' => true],
|
||||
['url' => 'mailto:info@example.org', 'title' => 'Email us', 'order' => 2, 'visible' => true],
|
||||
['route' => 'static-messenger', 'title' => 'Chat messenger', 'order' => 3, 'visible' => true,
|
||||
'auth_required' => true], // only for logged-in users
|
||||
],
|
||||
],
|
||||
],
|
||||
'tagline' => 'Your time is currency', // translation key
|
||||
],
|
||||
```
|
||||
|
||||
- `route` is a route name from `routes/web.php`; a route that does not exist on this platform (e.g. a
|
||||
switched-off local page) is skipped instead of causing an error.
|
||||
- `url` is for external or `mailto:` links.
|
||||
- `title` values are translation keys: add them to `resources/lang/*.json` for every language.
|
||||
|
||||
---
|
||||
|
||||
## 10. Custom content pages
|
||||
|
||||
Static pages show posts from the database: each page view (e.g. `resources/views/static/faq.blade.php`)
|
||||
embeds `@livewire('static-post', ['type' => 'SiteContents\Static\Faq', ...])`, and admins write the content
|
||||
as posts in that category. Limits per page are in `posts.static.<page>` of the platform config.
|
||||
|
||||
**A new page** (a code change, so for every platform):
|
||||
|
||||
1. Category type in `database/seeders/ExtendCategoriesSeeder.php`, e.g. `SiteContents\Static\MyPage`, and
|
||||
its names and slugs in all 5 languages in `database/seeders/ExtendCategoryTranslationsSeeder.php`.
|
||||
2. A view `resources/views/static/my-page.blade.php`, modelled on an existing static page.
|
||||
3. A route in `routes/web.php`, in the localized group:
|
||||
`Route::view(LaravelLocalization::transRoute('routes.static.my-page'), 'static.my-page')->name('static-my-page');`
|
||||
with the URL slug per language in `resources/lang/<locale>/routes.php`.
|
||||
4. Optional `posts.static.my-page` limits in both config templates, and a footer link.
|
||||
5. `deploy.sh` runs both seeders; locally: `php artisan db:seed --class=ExtendCategoriesSeeder` and
|
||||
`--class=ExtendCategoryTranslationsSeeder`.
|
||||
|
||||
To change seeded content on every installation (rename a category type or slug), add a step to
|
||||
`app/Console/Commands/DatabaseUpdate.php` (`php artisan database:update`, run by `deploy.sh`).
|
||||
|
||||
---
|
||||
|
||||
## 11. Keeping an instance up to date
|
||||
|
||||
- **Routine deploys**: `./deploy.sh -e server`. It pulls `main`, offers to merge new config keys, installs
|
||||
packages, migrates, re-indexes search and builds assets. Answer `y` and then `yes` per file to the config
|
||||
merge; it adds new keys and keeps your values (backups in `storage/config-backups/`).
|
||||
- **Check for new settings at any time**: `php artisan config:merge --all --dry-run`. Lists (footer
|
||||
sections and links, payment types, validation rules) are the platform's own choice: a list the platform config
|
||||
has is never changed by a merge; a missing one is added whole.
|
||||
- **Stale overrides**: `php artisan platform:check` after every update.
|
||||
- **Major upgrades** (e.g. Laravel 11 to 12) go through `scripts/upgrade.sh` (backup, maintenance window with tests,
|
||||
rollback), never through a routine deploy: `deploy.sh` stops a server deploy that would raise the Laravel major
|
||||
version. The Laravel 10 to 11 upgrade is described in [UPGRADE_LARAVEL_10_TO_11.md](UPGRADE_LARAVEL_10_TO_11.md);
|
||||
its runbook is Uuro's, the last installation on Laravel 10, which leaves its own branch with
|
||||
`upgrade.sh --leave-branch`.
|
||||
|
||||
After a merge, review new keys in your platform config: new switches default to the template's value
|
||||
(off in the default template), and names or addresses from the template are placeholders.
|
||||
|
||||
---
|
||||
|
||||
## 12. Troubleshooting
|
||||
|
||||
| Problem | Check |
|
||||
|---|---|
|
||||
| Theme not applied | `platform/theme.php` has an `id` and wins over `TIMEBANK_THEME`; without it, `TIMEBANK_THEME` matches a key in `config/themes.php`. The theme has all colour keys (compare with `themes.php.example`). |
|
||||
| Wrong or default settings | `php artisan platform:check` shows which config is used (`platform/config.php` wins over `config/<name>.php`); without a platform folder, `TIMEBANK_CONFIG` matches the file name without `.php`. Run `php artisan config:merge <name> --dry-run` for missing keys. Restart the queue workers and Reverb after a config change. |
|
||||
| A changed text or template does not show | `php artisan platform:check`: the code may have reworded the sentence (the override's key no longer exists) or changed the original template. After changing a template: `php artisan view:clear`. |
|
||||
| Logo, favicon or preview image from `platform/public/` not used | `public/platform` is not linked: `php artisan platform:link`. |
|
||||
| Brand name of another platform in a text | Look for a literal name in the view or translation key and switch it to a placeholder with `trans_with_platform()` ([section 7](#7-platform-name-in-texts-and-translations)). Texts in posts are content: edit them in the admin area. |
|
||||
| Email shows a text logo instead of yours | No `platform/public/mail-logo.png` (or it is not linked), and the file named in `logos.email_logo` is missing in `storage/app/public/`. |
|
||||
| Footer link missing | `visible` is true, the route exists on this platform, the section is visible, and `auth_required` matches. |
|
||||
| Footer link causes an error | Should not happen any more (unknown routes are skipped); check the `url`/`route` key names. |
|
||||
| City or Android pages appear on another platform | `local_pages` / `android_app` are missing from its config: run `config:merge`, then set them to `false`. |
|
||||
| Privacy policy download gives 404 | No `references/gdpr/<TIMEBANK_CONFIG>/<date>/privacy-policy-FULL-<locale>.md`. |
|
||||
| `deploy.sh` asks about uncommitted changes | Run `git status`: files changed on the server. The platform folder, uploaded logos, favicons and your platform config are excluded automatically; anything else was edited by hand. |
|
||||
| `config:merge` reports `not merged` or `Merged config is invalid` | The file was left unchanged; see [CONFIG_MANAGEMENT.md](CONFIG_MANAGEMENT.md). |
|
||||
|
||||
---
|
||||
|
||||
## 13. Quick reference
|
||||
|
||||
```bash
|
||||
# .env
|
||||
TIMEBANK_CONFIG=<platform> # config/<platform>.php
|
||||
TIMEBANK_THEME=<theme> # a key in config/themes.php (platform/theme.php wins)
|
||||
PLATFORM_PATH=platform # the platform folder (default)
|
||||
|
||||
# Commands
|
||||
./deploy.sh -e server # deploy (creates missing configs, merges new keys)
|
||||
php artisan config:merge --all --dry-run # new keys waiting
|
||||
php artisan config:merge <name> # merge into one config
|
||||
php artisan config:merge --restore # restore a config from its backup
|
||||
php artisan database:update # data updates of seeded content
|
||||
php artisan platform:init [--from-current] # create the platform folder
|
||||
php artisan platform:override <view> # copy a template into it
|
||||
php artisan platform:link # serve platform/public at /platform
|
||||
php artisan platform:check # stale overrides, config in use
|
||||
```
|
||||
|
||||
```php
|
||||
timebank_config('key.path', $default) // platform setting
|
||||
config('app.timebank_config') // platform config name
|
||||
platform_name() trans_with_platform('...@PLATFORM_NAME@...')
|
||||
theme_color('brand') theme_font('font_family_body') theme_logo('svg_inline')
|
||||
theme_email_logo_url() theme_email_logo_file() theme_favicon('favicon.ico') theme_og_image()
|
||||
platform_path('lang') // inside the platform folder
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Related documentation
|
||||
|
||||
**Setting up a server and instance**
|
||||
- [SETUP_GUIDE.md](SETUP_GUIDE.md): complete Debian server setup (PHP, MySQL, Redis, Elasticsearch, web server)
|
||||
- [PRODUCTION_DEPLOYMENT.md](PRODUCTION_DEPLOYMENT.md): production deployment
|
||||
- [EXTERNAL_SERVICES_REQUIREMENTS.md](EXTERNAL_SERVICES_REQUIREMENTS.md): external services an instance needs
|
||||
- [QUEUE_WORKERS_SETUP.md](QUEUE_WORKERS_SETUP.md): queue workers (all queues)
|
||||
- [WEBSOCKET_SETUP.md](WEBSOCKET_SETUP.md), [WEBSOCKET_SEPARATE_DOMAIN.md](WEBSOCKET_SEPARATE_DOMAIN.md): Reverb
|
||||
- [ELASTICSEARCH_SETUP.md](ELASTICSEARCH_SETUP.md): search
|
||||
- [SSL_CERTIFICATE_RENEWAL.md](SSL_CERTIFICATE_RENEWAL.md): certificates
|
||||
- [DEPLOYMENT_HTMLPURIFIER_FIX.md](DEPLOYMENT_HTMLPURIFIER_FIX.md): HTMLPurifier cache directory
|
||||
|
||||
**Configuration and updates**
|
||||
- [CONFIG_MANAGEMENT.md](CONFIG_MANAGEMENT.md): config templates, `config:merge`, backups, platforms with their own config name
|
||||
- [UPGRADE_LARAVEL_10_TO_11.md](UPGRADE_LARAVEL_10_TO_11.md): the Laravel 10 to 11 upgrade; [RUNBOOK_LARAVEL_10_TO_11.md](RUNBOOK_LARAVEL_10_TO_11.md): its checklist for the Uuro server
|
||||
- `platform.example/README.md`: what goes where in a platform folder
|
||||
- [PROFILE_INACTIVE_CONFIG.md](PROFILE_INACTIVE_CONFIG.md), [PROFILE_INCOMPLETE_CONFIG.md](PROFILE_INCOMPLETE_CONFIG.md), [PROFILE_AUTO_DELETE.md](PROFILE_AUTO_DELETE.md), [PROFILE_DELETION_GRACE_PERIOD.md](PROFILE_DELETION_GRACE_PERIOD.md): profile lifecycle settings
|
||||
- [SEARCH_REFERENCE.md](SEARCH_REFERENCE.md), [SEARCH_REFERENCE_SIMPLE.md](SEARCH_REFERENCE_SIMPLE.md): search settings and behaviour
|
||||
|
||||
**Look and feel**
|
||||
- [THEME_IMPLEMENTATION.md](THEME_IMPLEMENTATION.md): how themes work
|
||||
- [STYLE_GUIDE.md](STYLE_GUIDE.md): UI patterns for every view
|
||||
- [THEME_CLASS_CONVERSION_GUIDE.md](THEME_CLASS_CONVERSION_GUIDE.md): converting fixed colours to theme classes
|
||||
- `resources/views/logos/README.md`: website (SVG) logos
|
||||
|
||||
**Mail**
|
||||
- [EMAIL-TESTING-GUIDE.md](EMAIL-TESTING-GUIDE.md), [ARTISAN-EMAIL-COMMAND-SUMMARY.md](ARTISAN-EMAIL-COMMAND-SUMMARY.md): testing emails per theme
|
||||
- [BOUNCE_SETUP.md](BOUNCE_SETUP.md), [MAIL_BOUNCE_SYSTEM.md](MAIL_BOUNCE_SYSTEM.md): bounce handling
|
||||
|
||||
**Platform-specific features**
|
||||
- [ANDROID_APP_UPDATE_FLOW.md](ANDROID_APP_UPDATE_FLOW.md): Android app and its update check
|
||||
- `references/gdpr/<platform>/`: privacy policy per platform
|
||||
- `references/translations/`: translation workflow
|
||||
|
||||
**For developers and AI agents**: `AGENTS.md` in the repository root.
|
||||
Reference in new issue
Block a user