@geekity/cms 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -108
- package/admin/pages/documents/editor.njk +0 -13
- package/admin/pages/{settings/federation.njk → federation/settings.njk} +7 -5
- package/admin/pages/navigation/menus.njk +129 -0
- package/admin/pages/settings/reading.njk +2 -10
- package/admin/pages/tools/content-index.njk +89 -0
- package/admin/pages/users/list.njk +5 -2
- package/admin/static/admin.css +60 -0
- package/dist/access-log.d.ts +44 -0
- package/dist/access-log.d.ts.map +1 -0
- package/dist/access-log.js +71 -0
- package/dist/access-log.js.map +1 -0
- package/dist/admin/documents.d.ts +3 -13
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +2 -34
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/federation.d.ts +10 -0
- package/dist/admin/federation.d.ts.map +1 -1
- package/dist/admin/federation.js +10 -0
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/index.d.ts +3 -3
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +2 -2
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +19 -2
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/navigation.d.ts +155 -0
- package/dist/admin/navigation.d.ts.map +1 -0
- package/dist/admin/navigation.js +292 -0
- package/dist/admin/navigation.js.map +1 -0
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +9 -0
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/session.d.ts +17 -3
- package/dist/admin/session.d.ts.map +1 -1
- package/dist/admin/session.js +19 -5
- package/dist/admin/session.js.map +1 -1
- package/dist/admin/settings-federation.d.ts +10 -1
- package/dist/admin/settings-federation.d.ts.map +1 -1
- package/dist/admin/settings-federation.js +26 -6
- package/dist/admin/settings-federation.js.map +1 -1
- package/dist/admin/settings-page.d.ts +26 -9
- package/dist/admin/settings-page.d.ts.map +1 -1
- package/dist/admin/settings-page.js +18 -12
- package/dist/admin/settings-page.js.map +1 -1
- package/dist/admin/settings-pages.d.ts +14 -3
- package/dist/admin/settings-pages.d.ts.map +1 -1
- package/dist/admin/settings-pages.js +15 -5
- package/dist/admin/settings-pages.js.map +1 -1
- package/dist/admin/settings-reading.d.ts +5 -4
- package/dist/admin/settings-reading.d.ts.map +1 -1
- package/dist/admin/settings-reading.js +6 -5
- package/dist/admin/settings-reading.js.map +1 -1
- package/dist/admin/settings.d.ts +29 -15
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +28 -78
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/templates.d.ts +3 -1
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +3 -1
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/tools.d.ts +111 -0
- package/dist/admin/tools.d.ts.map +1 -0
- package/dist/admin/tools.js +182 -0
- package/dist/admin/tools.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/comments/form.d.ts +48 -0
- package/dist/comments/form.d.ts.map +1 -1
- package/dist/comments/form.js +15 -0
- package/dist/comments/form.js.map +1 -1
- package/dist/comments/index.d.ts +4 -3
- package/dist/comments/index.d.ts.map +1 -1
- package/dist/comments/index.js +2 -1
- package/dist/comments/index.js.map +1 -1
- package/dist/comments/routes.d.ts +3 -1
- package/dist/comments/routes.d.ts.map +1 -1
- package/dist/comments/routes.js +46 -11
- package/dist/comments/routes.js.map +1 -1
- package/dist/comments/submission.d.ts +37 -3
- package/dist/comments/submission.d.ts.map +1 -1
- package/dist/comments/submission.js +59 -20
- package/dist/comments/submission.js.map +1 -1
- package/dist/comments/viewer.d.ts +21 -0
- package/dist/comments/viewer.d.ts.map +1 -0
- package/dist/comments/viewer.js +45 -0
- package/dist/comments/viewer.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/content/store.d.ts +18 -0
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +18 -0
- package/dist/content/store.js.map +1 -1
- package/dist/env.d.ts +12 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/mount.d.ts +4 -0
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +44 -12
- package/dist/federation/mount.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -4
- package/dist/index.js.map +1 -1
- package/dist/web/index.d.ts +3 -3
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +1 -1
- package/dist/web/index.js.map +1 -1
- package/dist/web/navigation.d.ts +158 -36
- package/dist/web/navigation.d.ts.map +1 -1
- package/dist/web/navigation.js +250 -60
- package/dist/web/navigation.js.map +1 -1
- package/dist/web/negotiate.d.ts +22 -0
- package/dist/web/negotiate.d.ts.map +1 -1
- package/dist/web/negotiate.js +25 -10
- package/dist/web/negotiate.js.map +1 -1
- package/dist/web/render.d.ts +16 -10
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +21 -14
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +11 -2
- package/dist/web/routes.js.map +1 -1
- package/dist/web/themes.d.ts +21 -0
- package/dist/web/themes.d.ts.map +1 -1
- package/dist/web/themes.js +37 -0
- package/dist/web/themes.js.map +1 -1
- package/package.json +1 -1
- package/templates/site/content/_data/site.json +7 -0
- package/templates/site/content/pages/about.md +7 -0
- package/themes/default/README.md +200 -80
- package/themes/default/layouts/author.njk +3 -4
- package/themes/default/layouts/base.njk +38 -49
- package/themes/default/layouts/front-page.njk +1 -2
- package/themes/default/partials/bio.njk +15 -21
- package/themes/default/partials/comment-form.njk +31 -3
- package/themes/default/partials/menu.njk +32 -0
- package/themes/default/partials/search-form.njk +5 -0
- package/themes/default/static/style.css +31 -8
- package/themes/default/theme.json +5 -1
package/README.md
CHANGED
|
@@ -131,6 +131,16 @@ It refuses while the site is running, because deleting the file under a live
|
|
|
131
131
|
server would leave it writing to a database nothing can find. Stop the site
|
|
132
132
|
first. A file that will not parse is reported and exits `1`, as with `sync`.
|
|
133
133
|
|
|
134
|
+
That is why it is not the usual repair. **Tools > Content index** in the admin
|
|
135
|
+
empties the index and reads every file again in place, on the live connections:
|
|
136
|
+
no downtime, nobody signed out, and the delivery log, the relay handshakes and
|
|
137
|
+
the scheduler's watermark all kept. It is behind a confirm step, because the
|
|
138
|
+
site answers 404 for its documents between the emptying and the end of the
|
|
139
|
+
scan, and it federates nothing — a change a scan makes carries `origin: 'scan'`,
|
|
140
|
+
which delivery, the webmentions and the feed pings ignore. Use the command for
|
|
141
|
+
the one case the screen cannot be the door for: a database this version refuses
|
|
142
|
+
to open, where there is no site running to press a button in.
|
|
143
|
+
|
|
134
144
|
### Creating an admin from the command line
|
|
135
145
|
|
|
136
146
|
`geekity user add <username>` writes a user straight into `data/users.json`,
|
|
@@ -248,29 +258,76 @@ export default defineConfig({
|
|
|
248
258
|
Every field is optional. Relative directories resolve against the working
|
|
249
259
|
directory; absolute ones are used as given.
|
|
250
260
|
|
|
251
|
-
| Field | Default
|
|
252
|
-
| ------------------ |
|
|
253
|
-
| `port` | `3000`
|
|
254
|
-
| `contentDir` | `<cwd>/content`
|
|
255
|
-
| `dataDir` | `<cwd>/data`
|
|
256
|
-
| `themesDir` | `<cwd>/themes`
|
|
257
|
-
| `baseUrl` | `http://localhost:<port>`
|
|
258
|
-
| `watch` | `true`
|
|
259
|
-
| `sessionLifetime` | `1209600` (14 days)
|
|
260
|
-
| `loginAttempts` | `5`
|
|
261
|
-
| `loginLockout` | `900` (15 minutes)
|
|
262
|
-
| `trustProxy` | `false`
|
|
263
|
-
| `
|
|
264
|
-
| `
|
|
265
|
-
| `
|
|
266
|
-
| `
|
|
267
|
-
| `
|
|
261
|
+
| Field | Default | Environment override | Meaning |
|
|
262
|
+
| ------------------ | ------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
263
|
+
| `port` | `3000` | `GEEKITY_PORT`, then `PORT` | Port the HTTP server listens on. `0` picks a free one. |
|
|
264
|
+
| `contentDir` | `<cwd>/content` | `GEEKITY_CONTENT_DIR` | Markdown content. |
|
|
265
|
+
| `dataDir` | `<cwd>/data` | `GEEKITY_DATA_DIR` | Derived state — the SQLite index, the image variants — and the two things in it that are not derived and must be backed up: `users.json` and, under `keys/`, each user's actor key pairs. |
|
|
266
|
+
| `themesDir` | `<cwd>/themes` | `GEEKITY_THEMES_DIR` | The site's themes, one directory per theme, each with a `theme.json`. Which one is in use is the `theme` setting in `site.json`, not a path. Need not exist. |
|
|
267
|
+
| `baseUrl` | `http://localhost:<port>` | `GEEKITY_BASE_URL` | Public origin for canonical URLs, feeds and ActivityPub ids. A trailing slash is stripped. |
|
|
268
|
+
| `watch` | `true` | `GEEKITY_WATCH` | Watch `contentDir` while serving and keep the index in step. |
|
|
269
|
+
| `sessionLifetime` | `1209600` (14 days) | `GEEKITY_SESSION_LIFETIME` | How long an admin login lasts, in seconds. |
|
|
270
|
+
| `loginAttempts` | `5` | `GEEKITY_LOGIN_ATTEMPTS` | Failed sign-ins a username or an address may make before it is locked out. |
|
|
271
|
+
| `loginLockout` | `900` (15 minutes) | `GEEKITY_LOGIN_LOCKOUT` | How long the first lockout lasts, in seconds. See [Login hardening](#login-hardening). |
|
|
272
|
+
| `trustProxy` | `false` | `GEEKITY_TRUST_PROXY` | Believe `X-Forwarded-For` when deciding which address a sign-in came from. |
|
|
273
|
+
| `accessLog` | `false`; `true` under `geekity serve` | `GEEKITY_ACCESS_LOG` | One line per request on stdout: method, path with query, status, duration. See [The access log](#the-access-log). |
|
|
274
|
+
| `accessLogAddress` | `false` | `GEEKITY_ACCESS_LOG_ADDRESS` | Put the client address on the end of each access-log line. `trustProxy` decides which address that is. |
|
|
275
|
+
| `accessLogWriter` | stdout | — | Where the lines go instead. See [The access log](#the-access-log). |
|
|
276
|
+
| `onDocumentChange` | none | — | Hook run for every change to the index. See [Hooks](#hooks). |
|
|
277
|
+
| `onPublish` | none | — | Hook run when a document becomes visible. See [Hooks](#hooks). |
|
|
278
|
+
| `federation` | `{}` | — | Federation stores and guards. See [Federation](#federation). |
|
|
279
|
+
| `commentChecker` | Akismet | — | A spam checker of the site's own, which wins over the key in `data/akismet.json`. See [Akismet](#akismet). |
|
|
280
|
+
| `mail` | `{}` | — | Mail provider, retries, backoff and logger. See [Email](#email). |
|
|
268
281
|
|
|
269
282
|
Precedence is environment variable, then config file, then default, so a host
|
|
270
283
|
can override anything without editing the site. A boolean environment variable
|
|
271
284
|
takes `true`, `1`, `yes` and `on`, or their opposites; anything else is an error
|
|
272
285
|
rather than a silent `false`.
|
|
273
286
|
|
|
287
|
+
## The access log
|
|
288
|
+
|
|
289
|
+
`geekity serve` writes one line per request to stdout. `createCms` does not:
|
|
290
|
+
a CMS mounted inside somebody else's server has no business writing to its
|
|
291
|
+
stdout uninvited, so an embedder asks for it with `accessLog: true` — in the
|
|
292
|
+
config, no environment variable needed:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const cms = createCms({ accessLog: true });
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The line is the method, the path with its query string, the status and how
|
|
299
|
+
long the request took, in that order, separated by single spaces:
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
GET / 200 4.2ms
|
|
303
|
+
GET /.well-known/webfinger?resource=acct:ada@blog.example 200 1.9ms
|
|
304
|
+
GET /nothing-here 404 2.1ms
|
|
305
|
+
POST /admin/login 303 41.3ms
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Every route is on it — `/healthz`, the federation endpoints, the admin and the
|
|
309
|
+
public site — and a request whose handler threw gets its `500` line. Nothing
|
|
310
|
+
but the request line is ever read: no body is touched, so a password posted to
|
|
311
|
+
the login form cannot reach the log, and no cookie or `Authorization` header
|
|
312
|
+
is looked at either.
|
|
313
|
+
|
|
314
|
+
With `accessLogAddress: true` the client address goes on the end, after the
|
|
315
|
+
duration. It is off by default because an address is personal data. Which
|
|
316
|
+
address is right is `trustProxy`'s answer — the leftmost `X-Forwarded-For`
|
|
317
|
+
entry behind a proxy that sets it, the socket's own address otherwise — the
|
|
318
|
+
same one the login throttle counts a failed sign-in against.
|
|
319
|
+
|
|
320
|
+
`accessLogWriter` takes the lines somewhere else, one call per line, without
|
|
321
|
+
the newline:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
const lines: string[] = [];
|
|
325
|
+
const cms = createCms({
|
|
326
|
+
accessLog: true,
|
|
327
|
+
accessLogWriter: (line) => lines.push(line),
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
274
331
|
## Two directories: `content/` and `data/`
|
|
275
332
|
|
|
276
333
|
Everything a site cannot afford to lose is a file, and every one of those files
|
|
@@ -508,7 +565,7 @@ rather than in the trash: there is no file left to build the `Tombstone` from.
|
|
|
508
565
|
A [Mastodon-style relay][fepae0c] boosts every public activity it is sent on to
|
|
509
566
|
the instances subscribed to it, which is how a site nobody follows yet reaches
|
|
510
567
|
people. `relays` is the setting: one relay inbox per line on
|
|
511
|
-
`/admin/settings
|
|
568
|
+
`/admin/federation/settings`, kept in `site.json` like every other setting, and
|
|
512
569
|
`https://tags.pub/user/_____relay_____/inbox` is one worth knowing about — it
|
|
513
570
|
boosts any public post carrying a hashtag it tracks, which every `Article` this
|
|
514
571
|
CMS builds already carries one of per tag and per category.
|
|
@@ -616,7 +673,7 @@ next time it refetches the actor. So the CMS can carry them for a while, behind
|
|
|
616
673
|
a switch, and is meant to stop.
|
|
617
674
|
|
|
618
675
|
Turn **WordPress ActivityPub compatibility** on under
|
|
619
|
-
`/admin/settings
|
|
676
|
+
`/admin/federation/settings`. It is off by default and `site.json` says nothing
|
|
620
677
|
about it until it is on. It needs one thing on the user record in
|
|
621
678
|
`data/users.json` besides the stored actor id above: the number WordPress gave
|
|
622
679
|
that person, which is what its paths are built from.
|
|
@@ -719,11 +776,11 @@ followers.json` — which is worth doing if the old site is going away before
|
|
|
719
776
|
you took one. Check the report: every follower should be added, and any that
|
|
720
777
|
were skipped should be re-run once their servers answer.
|
|
721
778
|
3. **Switch on.** Turn **WordPress ActivityPub compatibility** on under
|
|
722
|
-
`/admin/settings
|
|
779
|
+
`/admin/federation/settings`, then move the DNS. Followers' servers go on
|
|
723
780
|
delivering to the plugin's old inbox paths until they next refetch the
|
|
724
781
|
actor, and the switch is what catches those deliveries.
|
|
725
782
|
4. **Watch.** `/admin/federation` lists the users with their actor ids and
|
|
726
|
-
followers; `/admin/settings
|
|
783
|
+
followers; `/admin/federation/settings` lists each compatibility path with
|
|
727
784
|
the instant it was last asked for. Deliveries should thin out as each
|
|
728
785
|
follower's server refetches the actor and learns the new endpoints.
|
|
729
786
|
5. **Switch off.** Once every path says _Never_ again for long enough — weeks
|
|
@@ -1406,47 +1463,52 @@ that ship inside the package, deliberately outside the theme search path: a
|
|
|
1406
1463
|
site's theme may override any public template, and must not be able to
|
|
1407
1464
|
shadow the login form.
|
|
1408
1465
|
|
|
1409
|
-
| Route | What it does
|
|
1410
|
-
| ------------------------------------------------ |
|
|
1411
|
-
| `/admin` | The dashboard: counts, the five most recent posts, the follower count.
|
|
1412
|
-
| `/admin/posts`, `/admin/pages` | The listings and the editors.
|
|
1413
|
-
| `/admin/tags`, `/admin/categories` | Every term in use, with rename, merge and delete.
|
|
1414
|
-
| `/admin/tags/rename`, `/admin/categories/rename` | `POST` only. Renames a term, or merges it into one that exists.
|
|
1415
|
-
| `/admin/tags/delete`, `/admin/categories/delete` | `POST` only. Takes a term out of every file.
|
|
1416
|
-
| `/admin/
|
|
1417
|
-
| `/admin/
|
|
1418
|
-
| `/admin/
|
|
1419
|
-
| `/admin/
|
|
1420
|
-
| `/admin/
|
|
1421
|
-
| `/admin/
|
|
1422
|
-
| `/admin/
|
|
1423
|
-
| `/admin/
|
|
1424
|
-
| `/admin/
|
|
1425
|
-
| `/admin/
|
|
1426
|
-
| `/admin/
|
|
1427
|
-
| `/admin/
|
|
1428
|
-
| `/admin/
|
|
1429
|
-
| `/admin/
|
|
1430
|
-
| `/admin/
|
|
1431
|
-
| `/admin/settings
|
|
1432
|
-
| `/admin/settings/
|
|
1433
|
-
| `/admin/settings/
|
|
1434
|
-
| `/admin/settings/
|
|
1435
|
-
| `/admin/
|
|
1436
|
-
| `/admin/
|
|
1437
|
-
| `/admin/
|
|
1438
|
-
| `/admin/
|
|
1439
|
-
| `/admin/users
|
|
1440
|
-
| `/admin/users/
|
|
1441
|
-
| `/admin/users
|
|
1442
|
-
| `/admin/users/
|
|
1443
|
-
| `/admin/users/
|
|
1444
|
-
| `/admin/
|
|
1445
|
-
| `/admin/
|
|
1446
|
-
| `/admin/
|
|
1447
|
-
| `/admin/
|
|
1448
|
-
| `/admin/
|
|
1449
|
-
| `/admin/
|
|
1466
|
+
| Route | What it does |
|
|
1467
|
+
| ------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
|
1468
|
+
| `/admin` | The dashboard: counts, the five most recent posts, the follower count. |
|
|
1469
|
+
| `/admin/posts`, `/admin/pages` | The listings and the editors. |
|
|
1470
|
+
| `/admin/tags`, `/admin/categories` | Every term in use, with rename, merge and delete. |
|
|
1471
|
+
| `/admin/tags/rename`, `/admin/categories/rename` | `POST` only. Renames a term, or merges it into one that exists. |
|
|
1472
|
+
| `/admin/tags/delete`, `/admin/categories/delete` | `POST` only. Takes a term out of every file. |
|
|
1473
|
+
| `/admin/navigation` | Navigation > Menus: every menu the site holds. `POST` saves one menu's items. |
|
|
1474
|
+
| `/admin/navigation/add` | `POST` only. Adds an empty menu under the name the form gives. |
|
|
1475
|
+
| `/admin/navigation/delete` | `POST` only. Deletes one menu the active theme renders nowhere. |
|
|
1476
|
+
| `/admin/media` | Everything under `content/uploads`, with the URL, the Markdown and what uses it. |
|
|
1477
|
+
| `/admin/media/upload` | `POST` only. Stores one file by the rules the editor's upload enforces. |
|
|
1478
|
+
| `/admin/media/delete` | `POST` only. Deletes one upload, asking first when a document points at it. |
|
|
1479
|
+
| `/admin/comments` | Pending, approved and spam, with approve, spam, delete and reply. |
|
|
1480
|
+
| `/admin/comments/moderate` | `POST` only. Approves one comment, files it as spam, or deletes it. |
|
|
1481
|
+
| `/admin/comments/reply` | `POST` only. Posts an approved reply under the comment it answers. |
|
|
1482
|
+
| `/admin/messages` | The contact form's inbox, with a Spam list beside it. |
|
|
1483
|
+
| `/admin/messages/read` | `POST` only. Marks one message read, or unread again. |
|
|
1484
|
+
| `/admin/messages/delete` | `POST` only. Deletes one message, and its file with it. |
|
|
1485
|
+
| `/admin/appearance/themes` | Appearance > Themes: the themes on disk. `POST` activates the one named. |
|
|
1486
|
+
| `/admin/tools` | Tools > Content index: what the index holds, and the button that rebuilds it. |
|
|
1487
|
+
| `/admin/tools/rebuild-index` | `POST` only. Offers the rebuild, then reads every file again on the live site. |
|
|
1488
|
+
| `/admin/settings` | Settings > General: title, tagline, author, base URL, time zone, language. |
|
|
1489
|
+
| `/admin/settings/reading` | What the homepage displays, posts per page, the notify server. |
|
|
1490
|
+
| `/admin/settings/permalinks` | The tag and category bases, and the archive redirects already recorded. |
|
|
1491
|
+
| `/admin/settings/discussion` | Comments and the closing window, webmentions, and the Akismet key. |
|
|
1492
|
+
| `/admin/settings/akismet` | `POST` only. Saves the Akismet key, or forgets it. |
|
|
1493
|
+
| `/admin/settings/email` | The mail provider, the From line, the reply-to and the contact address. |
|
|
1494
|
+
| `/admin/settings/mail` | `POST` only. Saves a mail credential, or forgets every one of them. |
|
|
1495
|
+
| `/admin/settings/mail/test` | `POST` only. Sends the theme's test message through the whole chain. |
|
|
1496
|
+
| `/admin/users` | Who may sign in: a row each, with Edit and Delete. Nothing on it edits anybody. |
|
|
1497
|
+
| `/admin/users/new` | Users > Add new: the add form. `POST` adds one. |
|
|
1498
|
+
| `/admin/users/<id>` | One user: the account, the profile, the notices, your password, and the delete. |
|
|
1499
|
+
| `/admin/users/password` | `POST` only. Changes the signed-in admin's own password. |
|
|
1500
|
+
| `/admin/users/email` | `POST` only. Sets or clears the email address on the user the form names. |
|
|
1501
|
+
| `/admin/users/profile` | `POST` only. Saves the whole public profile of the user the form names. |
|
|
1502
|
+
| `/admin/users/notifications` | `POST` only. Turns one notice on or off for the user the form names. |
|
|
1503
|
+
| `/admin/users/notifications/mode` | `POST` only. Sets how often that notice arrives: as they arrive, hourly or daily. |
|
|
1504
|
+
| `/admin/users/delete` | `POST` only. Deletes the user the form names. |
|
|
1505
|
+
| `/admin/federation` | The actors, their followers, the inbox log, and per-post delivery. |
|
|
1506
|
+
| `/admin/federation/settings` | Federation > Settings: the relays the site subscribes to, and the WordPress switch. |
|
|
1507
|
+
| `/admin/federation/resend` | `POST` only. Sends one post to the followers again, as its file now reads. |
|
|
1508
|
+
| `/admin/setup` | First run: creates the first admin. Closed once a user exists. |
|
|
1509
|
+
| `/admin/login` | Username and password. |
|
|
1510
|
+
| `/admin/logout` | `POST` only. Deletes the session row. |
|
|
1511
|
+
| `/admin/_static/*` | The admin's own stylesheet and scripts, cached for an hour. |
|
|
1450
1512
|
|
|
1451
1513
|
The screens behind the login share one layout: a bar across the top with the
|
|
1452
1514
|
site name and a link to the public site, the menu down the left, and a place for
|
|
@@ -1456,9 +1518,9 @@ read, so it survives exactly one redirect.
|
|
|
1456
1518
|
|
|
1457
1519
|
### The menu
|
|
1458
1520
|
|
|
1459
|
-
The menu is WordPress classic:
|
|
1460
|
-
Comments, Messages, Appearance, Users, Settings,
|
|
1461
|
-
over one or more children. Clicking a heading opens the section and lands on its first
|
|
1521
|
+
The menu is WordPress classic: twelve sections — Dashboard, Posts, Pages,
|
|
1522
|
+
Navigation, Media, Comments, Messages, Appearance, Users, Tools, Settings,
|
|
1523
|
+
Federation — each a heading over one or more children. Clicking a heading opens the section and lands on its first
|
|
1462
1524
|
child; the open section shows its children and the one you are on carries
|
|
1463
1525
|
`aria-current="page"`, so Users tells you that you are on Users > All users.
|
|
1464
1526
|
Tags and categories are children of Posts, because a tag with no post on it is
|
|
@@ -1552,10 +1614,12 @@ await createUser({
|
|
|
1552
1614
|
### Settings
|
|
1553
1615
|
|
|
1554
1616
|
`content/_data/site.json` is the source of truth for a site's settings. The
|
|
1555
|
-
pages under `/admin/settings` — General, Reading, Permalinks, Discussion
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1617
|
+
pages under `/admin/settings` — General, Reading, Permalinks, Discussion and
|
|
1618
|
+
Email — each read that file, validate what was typed and write it back; nothing
|
|
1619
|
+
else remembers a setting, and `data/geekity.db` holds none of them. Federation's
|
|
1620
|
+
page is a sixth one of exactly the same kind, filed under its own section at
|
|
1621
|
+
`/admin/federation/settings` rather than under Settings, because the relays and
|
|
1622
|
+
the compatibility switch are about the section that holds the followers.
|
|
1559
1623
|
|
|
1560
1624
|
Each page saves its own fields and no others, onto the file as re-read inside
|
|
1561
1625
|
the write, so two people saving two different pages at the same moment both
|
|
@@ -1574,7 +1638,7 @@ The file carries `title`, `tagline`, `url`, `author`, `postsPerPage`,
|
|
|
1574
1638
|
`timezone`, `language`, `tagBase`,
|
|
1575
1639
|
`categoryBase`, `notifyServer`, `webmentionsSend`, `webmentionsReceive`,
|
|
1576
1640
|
`mailProvider`, `mailFromName`, `mailFromAddress`, `mailReplyTo`,
|
|
1577
|
-
`contactEmail`, `relays`, `
|
|
1641
|
+
`contactEmail`, `relays`, `menus` and `taxonomyRedirects`,
|
|
1578
1642
|
and every other key it already had is kept, `feedSize` and anything a site put
|
|
1579
1643
|
there included. A key it does not carry is the default, and a key of the wrong
|
|
1580
1644
|
type is the default too: a hand-edited `site.json` cannot take the site down.
|
|
@@ -1961,37 +2025,96 @@ can put its archives at the same URLs.
|
|
|
1961
2025
|
|
|
1962
2026
|
### Navigation
|
|
1963
2027
|
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
`
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
2028
|
+
A menu is a named thing the site stores and the theme asks for.
|
|
2029
|
+
|
|
2030
|
+
**The site stores menus by name.** `content/_data/site.json` holds a `menus`
|
|
2031
|
+
object keyed by the name of each menu, each an ordered list of items:
|
|
2032
|
+
|
|
2033
|
+
```json
|
|
2034
|
+
{
|
|
2035
|
+
"menus": {
|
|
2036
|
+
"primary": [{ "label": "About", "url": "/about/" }],
|
|
2037
|
+
"footer": [
|
|
2038
|
+
{ "label": "Colophon", "url": "/colophon/" },
|
|
2039
|
+
{ "label": "Mastodon", "url": "https://example.social/@me", "me": true }
|
|
2040
|
+
]
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
```
|
|
2044
|
+
|
|
2045
|
+
An item is a label, a URL and the flags it carries. The URL is a site-root path
|
|
2046
|
+
or an absolute `http(s)` URL; anything else is refused. There is one flag,
|
|
2047
|
+
`me`, present only when it is set and read only when it is spelled exactly
|
|
2048
|
+
`true`: it gives the link `rel="me"`, which is how Mastodon and the rest of the
|
|
2049
|
+
IndieWeb verify that the site and the profile it links are the same person.
|
|
2050
|
+
|
|
2051
|
+
**The theme declares the areas it renders.** `theme.json` carries an `areas`
|
|
2052
|
+
list, each entry a `name` — the key under `menus` — and a `label` a person
|
|
2053
|
+
reads:
|
|
2054
|
+
|
|
2055
|
+
```json
|
|
2056
|
+
{ "areas": [{ "name": "primary", "label": "Site menu" }] }
|
|
2057
|
+
```
|
|
2058
|
+
|
|
2059
|
+
The packaged theme declares `primary` ("Site menu") and `footer` ("Footer
|
|
2060
|
+
links"). A theme that wants a third declares it; an `areas` that is missing or
|
|
2061
|
+
unreadable leaves that theme declaring none rather than failing to load it, and
|
|
2062
|
+
the Navigation screen then offers the packaged theme's — a site theme is laid
|
|
2063
|
+
over the packaged one a file at a time, so a theme that has declared nothing has
|
|
2064
|
+
not overridden the declaration, and its inherited `layouts/base.njk` really is
|
|
2065
|
+
still rendering `menus.primary` and `menus.footer`.
|
|
2066
|
+
|
|
2067
|
+
**A theme renders a menu by name.** Every template gets `menus`, keyed by the
|
|
2068
|
+
same names, each item already marked `current` for the path being rendered.
|
|
2069
|
+
`{% for item in menus.footer %}` is the whole interface, so a site theme can
|
|
2070
|
+
render a menu the packaged theme has never heard of. Every stored menu is on
|
|
2071
|
+
the context, not only the declared ones: the declaration is what the screen
|
|
2072
|
+
that edits menus reads, and a menu nothing loops over is rendered nowhere and
|
|
2073
|
+
kept — which is how somebody writes the menu a theme will use before switching
|
|
2074
|
+
to that theme.
|
|
2075
|
+
|
|
2076
|
+
**The menus are managed on `/admin/navigation`.** It is a top-level section
|
|
2077
|
+
after Pages rather than a settings page: a menu is content a site arranges, the
|
|
2078
|
+
way its pages are, and the shape of the screen comes from the active theme.
|
|
2079
|
+
The screen is two lists. First the areas that theme declares, in the theme's
|
|
2080
|
+
order and under its labels — an area the site has never filled in is an empty
|
|
2081
|
+
box, not a missing one. Then every other stored menu, under a heading saying
|
|
2082
|
+
this theme renders them nowhere; those are still editable, which is how the
|
|
2083
|
+
menu a theme will use is written before the site switches to it, and each has
|
|
2084
|
+
the Delete that is the only way a menu is removed. An area the theme declares
|
|
2085
|
+
is emptied rather than deleted: the theme goes on asking for the name, and the
|
|
2086
|
+
box has to stay for the next thing typed into it.
|
|
2087
|
+
|
|
2088
|
+
Each menu is one box of `Label | URL` lines, in the order they are typed, with
|
|
2089
|
+
a trailing `| me` marking the flag. Only a flag this CMS has is taken off the
|
|
2090
|
+
end of a line, so a URL holding a bar survives and a trailing word that is not
|
|
2091
|
+
a flag stays part of the URL. A line that is not an item is refused by name,
|
|
2092
|
+
with the box still holding every line of what was typed, and nothing written.
|
|
2093
|
+
|
|
2094
|
+
**A menu name** is lower-case ASCII letters, digits and underscores, starting
|
|
2095
|
+
with a letter, at most 32 characters. It is the word a theme writes after the
|
|
2096
|
+
dot — `{% for item in menus.footer %}` — so a dash is refused: after a dot it
|
|
2097
|
+
is a minus sign, and `menus.top-bar` renders nothing and raises nothing. Lower
|
|
2098
|
+
case is the rest of the rule and it is what refuses the confusable spellings:
|
|
2099
|
+
`Footer` is refused rather than quietly lowered, because `footer` and `Footer`
|
|
2100
|
+
would be two menus on one screen that nothing could tell apart. A name the site
|
|
2101
|
+
already holds is refused too, and the message says its box is already on the
|
|
2102
|
+
page. The rule governs a name somebody types; a name a `theme.json` declares is
|
|
2103
|
+
a menu name by declaration and is shown as the theme spells it, because a
|
|
2104
|
+
screen that hid a declared area over a spelling would leave somebody unable to
|
|
2105
|
+
fill in a menu their site renders.
|
|
2106
|
+
|
|
2107
|
+
There is no second source. A page cannot put itself in a menu, so there is one
|
|
2108
|
+
screen to edit it on, one order, and no way for the same link to appear twice.
|
|
2109
|
+
A page that should be linked is linked by typing a line for it — including the
|
|
2110
|
+
page a site serves as its front page, which is typed `Home | /`, the URL a
|
|
2111
|
+
reader lands on, rather than at the permalink that redirects there.
|
|
2112
|
+
|
|
2113
|
+
Because the menus live in `site.json`, an Eleventy build renders the same ones;
|
|
2114
|
+
the example config assembles them as `collections.menus`. A `menus` in a
|
|
2115
|
+
hand-edited `site.json` that is not an object of lists of items yields no menu
|
|
2116
|
+
rather than an error, exactly as a bad archive base falls back rather than
|
|
2117
|
+
taking the site down.
|
|
1995
2118
|
|
|
1996
2119
|
## Content negotiation
|
|
1997
2120
|
|
|
@@ -2572,10 +2695,13 @@ theme can shadow the login form or the CSRF field inside it.
|
|
|
2572
2695
|
The packaged theme is the andrewshell.org design (decision-16). `base.njk` is
|
|
2573
2696
|
its shell: a skip link, a `.global-wrapper` carrying `data-is-root-path="true"`
|
|
2574
2697
|
at `/` only, a header that is the site title and tagline on the front page and a
|
|
2575
|
-
small link home on every other
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2698
|
+
small link home on every other — with `menus.primary` inside it on every page
|
|
2699
|
+
— `<main id="main">`, and a footer with the copyright year, the site author,
|
|
2700
|
+
the colophon and `menus.footer`. The footer reads nothing off an account: it
|
|
2701
|
+
used to print an RSS link and one `rel="me"` link per link on the site author's
|
|
2702
|
+
profile, and a site that wants either types it into its footer menu. Nothing
|
|
2703
|
+
particular to one site is in it — webrings and badges belong in a site theme's
|
|
2704
|
+
`footer` block. Its head
|
|
2579
2705
|
carries a description, Open Graph and Twitter card tags, icons derived from the
|
|
2580
2706
|
site's avatar, and one JSON-LD `@graph` from `partials/jsonld.njk`: that partial
|
|
2581
2707
|
is all the structured data the theme emits, there is no Microdata anywhere, and
|
|
@@ -2668,9 +2794,9 @@ the rules the CMS follows that Eleventy does not know about on its own:
|
|
|
2668
2794
|
It also builds two collections Eleventy has no notion of. `collections.categories`
|
|
2669
2795
|
is the second taxonomy, one entry of `{ name, posts }` per category in use, for
|
|
2670
2796
|
paginating into archives at `/{{ site.categoryBase }}/{name}/`.
|
|
2671
|
-
`collections.
|
|
2672
|
-
|
|
2673
|
-
|
|
2797
|
+
`collections.menus` is the site's named menus — the `menus` object of
|
|
2798
|
+
`site.json`, and nothing else — each an ordered list of `{ label, url }`
|
|
2799
|
+
entries, plus `me: true` on a link that should carry `rel="me"`; a layout
|
|
2674
2800
|
marks the current one itself by comparing `item.url` with `page.url`, because a
|
|
2675
2801
|
collection is built once for the whole site.
|
|
2676
2802
|
|
|
@@ -133,19 +133,6 @@
|
|
|
133
133
|
<p class="admin-hint">Keeps the {{ kind.singular }} out of listings that gather content, without unpublishing it.</p>
|
|
134
134
|
{% endif %}
|
|
135
135
|
|
|
136
|
-
{% if kind.navigable %}
|
|
137
|
-
{# The page puts itself in the site menu, after the items the settings
|
|
138
|
-
screen names. #}
|
|
139
|
-
<p class="admin-check">
|
|
140
|
-
<input id="editor-navigation" name="navigation" type="checkbox" value="1"{% if form.navigation %} checked{% endif %} />
|
|
141
|
-
<label for="editor-navigation">Show in navigation</label>
|
|
142
|
-
</p>
|
|
143
|
-
|
|
144
|
-
<label for="editor-navigation-order">Menu order</label>
|
|
145
|
-
<input id="editor-navigation-order" name="navigation_order" type="text" inputmode="numeric" value="{{ form.navigationOrder }}" />
|
|
146
|
-
<p class="admin-hint">Puts this {{ kind.singular }} in the site header, after the items on the settings screen. A lower order comes first; leave it empty and it sorts by title after the ordered ones.</p>
|
|
147
|
-
{% endif %}
|
|
148
|
-
|
|
149
136
|
{% if kind.contactable %}
|
|
150
137
|
{# The page offers a contact form under its content (TASK-56). Where a
|
|
151
138
|
message goes is the contact address on the Settings screen; it is
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
{# /admin/settings
|
|
2
|
-
still answering the paths a WordPress site arrived with.
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
{# /admin/federation/settings: which relays boost the site, and whether it is
|
|
2
|
+
still answering the paths a WordPress site arrived with. It is Federation >
|
|
3
|
+
Settings rather than Settings > Federation (TASK-109): the section holds
|
|
4
|
+
everything about federation, and Followers beside it is where the actors and
|
|
5
|
+
the people following them are. Who the site's people are on the fediverse is
|
|
6
|
+
a user's profile now (decision-14). #}
|
|
5
7
|
{% extends "layouts/settings-page.njk" %}
|
|
6
8
|
{% import "components/fields.njk" as field %}
|
|
7
9
|
|
|
@@ -11,7 +13,7 @@
|
|
|
11
13
|
<code>https://tags.pub/user/_____relay_____/inbox</code>. Adding one sends
|
|
12
14
|
it a follow; every public post then goes to it as well as to the
|
|
13
15
|
followers, and it boosts them on to instances nobody here follows. The
|
|
14
|
-
|
|
16
|
+
Followers screen says where each subscription stands.
|
|
15
17
|
{% endset %}
|
|
16
18
|
{{ field.textarea('settings-relays', fields.relays, 'Relays', form.relays,
|
|
17
19
|
error=problems.relays, hint=relaysHint, rows='3', spellcheck='false', autofocus=true) }}
|