@geekity/cms 0.17.0 → 0.18.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 +162 -50
- package/admin/pages/documents/editor.njk +63 -0
- package/admin/pages/documents/list.njk +2 -0
- package/admin/pages/indieauth/consent.njk +4 -0
- package/admin/pages/settings/privacy.njk +37 -0
- package/admin/pages/users/apps.njk +109 -3
- package/dist/admin/documents.d.ts +29 -1
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +88 -6
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/index.d.ts +1 -0
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/location-field.d.ts +26 -0
- package/dist/admin/location-field.d.ts.map +1 -0
- package/dist/admin/location-field.js +90 -0
- package/dist/admin/location-field.js.map +1 -0
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +1 -0
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +18 -5
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/read-field.d.ts +23 -0
- package/dist/admin/read-field.d.ts.map +1 -0
- package/dist/admin/read-field.js +53 -0
- package/dist/admin/read-field.js.map +1 -0
- package/dist/admin/settings-page.d.ts +3 -3
- package/dist/admin/settings-page.js +2 -2
- package/dist/admin/settings-pages.d.ts +2 -2
- package/dist/admin/settings-pages.d.ts.map +1 -1
- package/dist/admin/settings-pages.js +4 -2
- package/dist/admin/settings-pages.js.map +1 -1
- package/dist/admin/settings-privacy.d.ts +3 -0
- package/dist/admin/settings-privacy.d.ts.map +1 -0
- package/dist/admin/settings-privacy.js +10 -0
- package/dist/admin/settings-privacy.js.map +1 -0
- package/dist/admin/settings-reading.js +2 -2
- package/dist/admin/settings-reading.js.map +1 -1
- package/dist/admin/settings.d.ts +18 -2
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +27 -0
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/templates.d.ts +1 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +1 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +14 -1
- package/dist/admin/uploads.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +34 -0
- package/dist/cli.js.map +1 -1
- package/dist/comments/routes.js +2 -2
- package/dist/comments/routes.js.map +1 -1
- package/dist/contact/routes.js +2 -2
- package/dist/contact/routes.js.map +1 -1
- package/dist/content/location.d.ts +58 -0
- package/dist/content/location.d.ts.map +1 -0
- package/dist/content/location.js +208 -0
- package/dist/content/location.js.map +1 -0
- package/dist/content/locations.d.ts +9 -0
- package/dist/content/locations.d.ts.map +1 -0
- package/dist/content/locations.js +82 -0
- package/dist/content/locations.js.map +1 -0
- package/dist/content/metadata/gif.d.ts +3 -0
- package/dist/content/metadata/gif.d.ts.map +1 -0
- package/dist/content/metadata/gif.js +65 -0
- package/dist/content/metadata/gif.js.map +1 -0
- package/dist/content/metadata/index.d.ts +6 -0
- package/dist/content/metadata/index.d.ts.map +1 -0
- package/dist/content/metadata/index.js +24 -0
- package/dist/content/metadata/index.js.map +1 -0
- package/dist/content/metadata/isobmff.d.ts +22 -0
- package/dist/content/metadata/isobmff.d.ts.map +1 -0
- package/dist/content/metadata/isobmff.js +215 -0
- package/dist/content/metadata/isobmff.js.map +1 -0
- package/dist/content/metadata/jpeg.d.ts +3 -0
- package/dist/content/metadata/jpeg.d.ts.map +1 -0
- package/dist/content/metadata/jpeg.js +117 -0
- package/dist/content/metadata/jpeg.js.map +1 -0
- package/dist/content/metadata/png.d.ts +3 -0
- package/dist/content/metadata/png.d.ts.map +1 -0
- package/dist/content/metadata/png.js +74 -0
- package/dist/content/metadata/png.js.map +1 -0
- package/dist/content/metadata/shared.d.ts +17 -0
- package/dist/content/metadata/shared.d.ts.map +1 -0
- package/dist/content/metadata/shared.js +42 -0
- package/dist/content/metadata/shared.js.map +1 -0
- package/dist/content/metadata/sweep.d.ts +14 -0
- package/dist/content/metadata/sweep.d.ts.map +1 -0
- package/dist/content/metadata/sweep.js +54 -0
- package/dist/content/metadata/sweep.js.map +1 -0
- package/dist/content/metadata/tiff.d.ts +7 -0
- package/dist/content/metadata/tiff.d.ts.map +1 -0
- package/dist/content/metadata/tiff.js +56 -0
- package/dist/content/metadata/tiff.js.map +1 -0
- package/dist/content/metadata/webp.d.ts +3 -0
- package/dist/content/metadata/webp.d.ts.map +1 -0
- package/dist/content/metadata/webp.js +67 -0
- package/dist/content/metadata/webp.js.map +1 -0
- package/dist/content/post-type.d.ts +4 -2
- package/dist/content/post-type.d.ts.map +1 -1
- package/dist/content/post-type.js +9 -1
- package/dist/content/post-type.js.map +1 -1
- package/dist/content/read.d.ts +22 -0
- package/dist/content/read.d.ts.map +1 -0
- package/dist/content/read.js +67 -0
- package/dist/content/read.js.map +1 -0
- package/dist/content/store.d.ts +4 -4
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +19 -39
- package/dist/content/store.js.map +1 -1
- package/dist/content/sync.d.ts.map +1 -1
- package/dist/content/sync.js +3 -13
- package/dist/content/sync.js.map +1 -1
- package/dist/content/visibility.d.ts +11 -0
- package/dist/content/visibility.d.ts.map +1 -0
- package/dist/content/visibility.js +14 -0
- package/dist/content/visibility.js.map +1 -0
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +41 -14
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/delivery.d.ts +3 -1
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +6 -5
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +3 -1
- package/dist/federation/mount.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/indexnow.js +2 -2
- package/dist/indexnow.js.map +1 -1
- package/dist/indieauth/activity-log.d.ts +2 -0
- package/dist/indieauth/activity-log.d.ts.map +1 -1
- package/dist/indieauth/activity-log.js +12 -2
- package/dist/indieauth/activity-log.js.map +1 -1
- package/dist/indieauth/app-activity.js +5 -1
- package/dist/indieauth/app-activity.js.map +1 -1
- package/dist/indieauth/bearer.d.ts.map +1 -1
- package/dist/indieauth/bearer.js +17 -16
- package/dist/indieauth/bearer.js.map +1 -1
- package/dist/indieauth/client-id.d.ts +10 -0
- package/dist/indieauth/client-id.d.ts.map +1 -0
- package/dist/indieauth/client-id.js +30 -0
- package/dist/indieauth/client-id.js.map +1 -0
- package/dist/indieauth/connected-apps.d.ts +4 -0
- package/dist/indieauth/connected-apps.d.ts.map +1 -1
- package/dist/indieauth/connected-apps.js +149 -20
- package/dist/indieauth/connected-apps.js.map +1 -1
- package/dist/indieauth/consent.d.ts.map +1 -1
- package/dist/indieauth/consent.js +5 -1
- package/dist/indieauth/consent.js.map +1 -1
- package/dist/indieauth/grants.d.ts +2 -7
- package/dist/indieauth/grants.d.ts.map +1 -1
- package/dist/indieauth/grants.js.map +1 -1
- package/dist/indieauth/redeem.d.ts.map +1 -1
- package/dist/indieauth/redeem.js +10 -5
- package/dist/indieauth/redeem.js.map +1 -1
- package/dist/indieauth/request.d.ts +8 -4
- package/dist/indieauth/request.d.ts.map +1 -1
- package/dist/indieauth/request.js +29 -30
- package/dist/indieauth/request.js.map +1 -1
- package/dist/indieauth/token.js +1 -1
- package/dist/indieauth/token.js.map +1 -1
- package/dist/indieauth/tokens.d.ts +28 -7
- package/dist/indieauth/tokens.d.ts.map +1 -1
- package/dist/indieauth/tokens.js +26 -5
- package/dist/indieauth/tokens.js.map +1 -1
- package/dist/micropub/create.d.ts +22 -0
- package/dist/micropub/create.d.ts.map +1 -1
- package/dist/micropub/create.js +107 -8
- package/dist/micropub/create.js.map +1 -1
- package/dist/micropub/endpoint.d.ts.map +1 -1
- package/dist/micropub/endpoint.js +71 -20
- package/dist/micropub/endpoint.js.map +1 -1
- package/dist/micropub/media.d.ts +4 -2
- package/dist/micropub/media.d.ts.map +1 -1
- package/dist/micropub/media.js +41 -37
- package/dist/micropub/media.js.map +1 -1
- package/dist/micropub/update.d.ts +6 -2
- package/dist/micropub/update.d.ts.map +1 -1
- package/dist/micropub/update.js +31 -3
- package/dist/micropub/update.js.map +1 -1
- package/dist/notify.d.ts.map +1 -1
- package/dist/notify.js +2 -5
- package/dist/notify.js.map +1 -1
- package/dist/web/absolute-urls.d.ts +2 -0
- package/dist/web/absolute-urls.d.ts.map +1 -0
- package/dist/web/absolute-urls.js +22 -0
- package/dist/web/absolute-urls.js.map +1 -0
- package/dist/web/context.d.ts +29 -1
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +33 -0
- package/dist/web/context.js.map +1 -1
- package/dist/web/conversation.d.ts.map +1 -1
- package/dist/web/conversation.js +5 -4
- package/dist/web/conversation.js.map +1 -1
- package/dist/web/documents.d.ts +19 -10
- package/dist/web/documents.d.ts.map +1 -1
- package/dist/web/documents.js +28 -13
- package/dist/web/documents.js.map +1 -1
- package/dist/web/feed-item.d.ts +25 -6
- package/dist/web/feed-item.d.ts.map +1 -1
- package/dist/web/feed-item.js +31 -7
- package/dist/web/feed-item.js.map +1 -1
- package/dist/web/feed-json.d.ts +3 -1
- package/dist/web/feed-json.d.ts.map +1 -1
- package/dist/web/feed-json.js +2 -0
- package/dist/web/feed-json.js.map +1 -1
- package/dist/web/feed-source.d.ts +6 -0
- package/dist/web/feed-source.d.ts.map +1 -1
- package/dist/web/feed-source.js.map +1 -1
- package/dist/web/feeds.d.ts.map +1 -1
- package/dist/web/feeds.js +2 -0
- package/dist/web/feeds.js.map +1 -1
- package/dist/web/html-attributes.d.ts +3 -0
- package/dist/web/html-attributes.d.ts.map +1 -0
- package/dist/web/html-attributes.js +15 -0
- package/dist/web/html-attributes.js.map +1 -0
- package/dist/web/index.d.ts +1 -1
- 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/negotiate.d.ts +2 -0
- package/dist/web/negotiate.d.ts.map +1 -1
- package/dist/web/negotiate.js +2 -1
- package/dist/web/negotiate.js.map +1 -1
- package/dist/web/render.d.ts +8 -0
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +5 -1
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +14 -10
- package/dist/web/routes.js.map +1 -1
- package/dist/webmention/service.d.ts.map +1 -1
- package/dist/webmention/service.js +7 -2
- package/dist/webmention/service.js.map +1 -1
- package/package.json +1 -1
- package/themes/default/README.md +15 -0
- package/themes/default/layouts/base.njk +2 -0
- package/themes/default/layouts/post.njk +7 -1
- package/themes/default/partials/location.njk +12 -0
- package/themes/default/partials/post-list.njk +2 -0
- package/themes/default/partials/read.njk +5 -0
package/README.md
CHANGED
|
@@ -94,12 +94,13 @@ site (or `npx geekity`, or a `package.json` script, which is how the generated
|
|
|
94
94
|
| `geekity rebuild` | Delete `data/geekity.db` and build it again from the files. |
|
|
95
95
|
| `geekity resend --all`, `<slug>...` | Send announced posts to every follower and relay again, as they now read. See [Quote posts](#quote-posts). |
|
|
96
96
|
| `geekity maintenance on`, `off`, `status` | Take the public site down on purpose with a 503 and `Retry-After`, or bring it back, without a restart. `on --until <time>` names when it should be back. |
|
|
97
|
+
| `geekity strip-metadata` | Remove location and camera metadata from files already in `content/uploads`. See [The media library](#the-media-library). |
|
|
97
98
|
| `geekity user add <name>` | Create an admin account, so a site can get its first login without the setup screen. |
|
|
98
99
|
| `geekity import wordpress-actor <name>` | Bring one person across from the WordPress ActivityPub plugin: their key pair, the actor id their followers hold, the plugin's numeric actor id, and their followers. |
|
|
99
100
|
| `geekity --help`, `-h` | The same table, on the terminal. |
|
|
100
101
|
| `geekity --version` | The installed version. |
|
|
101
102
|
|
|
102
|
-
`serve`, `sync`, `rebuild`, `resend`, `maintenance`, `user add` and `import wordpress-actor` take
|
|
103
|
+
`serve`, `sync`, `rebuild`, `resend`, `maintenance`, `strip-metadata`, `user add` and `import wordpress-actor` take
|
|
103
104
|
`--config <file>`; without it they look for `geekity.config.ts`, then
|
|
104
105
|
`geekity.config.js`, then `geekity.config.mjs` in the working directory, and run
|
|
105
106
|
on defaults if there is none.
|
|
@@ -267,29 +268,29 @@ export default defineConfig({
|
|
|
267
268
|
Every field is optional. Relative directories resolve against the working
|
|
268
269
|
directory; absolute ones are used as given.
|
|
269
270
|
|
|
270
|
-
| Field | Default | Environment override | Meaning
|
|
271
|
-
| ------------------ | ------------------------------------- | ---------------------------- |
|
|
272
|
-
| `port` | `3000` | `GEEKITY_PORT`, then `PORT` | Port the HTTP server listens on. `0` picks a free one.
|
|
273
|
-
| `contentDir` | `<cwd>/content` | `GEEKITY_CONTENT_DIR` | Markdown content.
|
|
274
|
-
| `dataDir` | `<cwd>/data` | `GEEKITY_DATA_DIR` | Derived state — the SQLite index, the image variants — and the
|
|
275
|
-
| `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.
|
|
276
|
-
| `baseUrl` | `http://localhost:<port>` | `GEEKITY_BASE_URL` | Public origin for canonical URLs, feeds and ActivityPub ids. A trailing slash is stripped.
|
|
277
|
-
| `watch` | `true` | `GEEKITY_WATCH` | Watch `contentDir` while serving and keep the index in step.
|
|
278
|
-
| `sessionLifetime` | `1209600` (14 days) | `GEEKITY_SESSION_LIFETIME` | How long an admin login lasts, in seconds.
|
|
279
|
-
| `loginAttempts` | `5` | `GEEKITY_LOGIN_ATTEMPTS` | Failed sign-ins a username or an address may make before it is locked out.
|
|
280
|
-
| `loginLockout` | `900` (15 minutes) | `GEEKITY_LOGIN_LOCKOUT` | How long the first lockout lasts, in seconds. See [Login hardening](#login-hardening).
|
|
281
|
-
| `trustProxy` | `false` | `GEEKITY_TRUST_PROXY` | Believe `X-Forwarded-For` when deciding which address a sign-in came from.
|
|
282
|
-
| `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).
|
|
283
|
-
| `accessLogAddress` | `false` | `GEEKITY_ACCESS_LOG_ADDRESS` | Put the client address on the end of each access-log line. `trustProxy` decides which address that is.
|
|
284
|
-
| `accessLogWriter` | stdout | — | Where the lines go instead. See [The access log](#the-access-log).
|
|
285
|
-
| `maintenance` | `false` | `GEEKITY_MAINTENANCE` | Keep the site in maintenance mode, answering 503, until a restart without it.
|
|
286
|
-
| `compression` | `true` | `GEEKITY_COMPRESSION` | Compress text responses with brotli or gzip. Turn it off behind a proxy that compresses. See [Compression](#compression).
|
|
287
|
-
| `securityHeaders` | [see below](#security-headers) | — | Headers every response carries. A string replaces a default or adds a header, `false` removes one.
|
|
288
|
-
| `onDocumentChange` | none | — | Hook run for every change to the index. See [Hooks](#hooks).
|
|
289
|
-
| `onPublish` | none | — | Hook run when a document becomes visible. See [Hooks](#hooks).
|
|
290
|
-
| `federation` | `{}` | — | Federation stores and guards. See [Federation](#federation).
|
|
291
|
-
| `commentChecker` | Akismet | — | A spam checker of the site's own, which wins over the key in `data/akismet.json`. See [Akismet](#akismet).
|
|
292
|
-
| `mail` | `{}` | — | Mail provider, retries, backoff and logger. See [Email](#email).
|
|
271
|
+
| Field | Default | Environment override | Meaning |
|
|
272
|
+
| ------------------ | ------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
273
|
+
| `port` | `3000` | `GEEKITY_PORT`, then `PORT` | Port the HTTP server listens on. `0` picks a free one. |
|
|
274
|
+
| `contentDir` | `<cwd>/content` | `GEEKITY_CONTENT_DIR` | Markdown content. |
|
|
275
|
+
| `dataDir` | `<cwd>/data` | `GEEKITY_DATA_DIR` | Derived state — the SQLite index, the image variants — and the files that are not derived and must be backed up, such as `users.json`, the actor key pairs under `keys/` and `locations.json`. [Two directories](#two-directories-content-and-data) lists them. |
|
|
276
|
+
| `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. |
|
|
277
|
+
| `baseUrl` | `http://localhost:<port>` | `GEEKITY_BASE_URL` | Public origin for canonical URLs, feeds and ActivityPub ids. A trailing slash is stripped. |
|
|
278
|
+
| `watch` | `true` | `GEEKITY_WATCH` | Watch `contentDir` while serving and keep the index in step. |
|
|
279
|
+
| `sessionLifetime` | `1209600` (14 days) | `GEEKITY_SESSION_LIFETIME` | How long an admin login lasts, in seconds. |
|
|
280
|
+
| `loginAttempts` | `5` | `GEEKITY_LOGIN_ATTEMPTS` | Failed sign-ins a username or an address may make before it is locked out. |
|
|
281
|
+
| `loginLockout` | `900` (15 minutes) | `GEEKITY_LOGIN_LOCKOUT` | How long the first lockout lasts, in seconds. See [Login hardening](#login-hardening). |
|
|
282
|
+
| `trustProxy` | `false` | `GEEKITY_TRUST_PROXY` | Believe `X-Forwarded-For` when deciding which address a sign-in came from. |
|
|
283
|
+
| `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). |
|
|
284
|
+
| `accessLogAddress` | `false` | `GEEKITY_ACCESS_LOG_ADDRESS` | Put the client address on the end of each access-log line. `trustProxy` decides which address that is. |
|
|
285
|
+
| `accessLogWriter` | stdout | — | Where the lines go instead. See [The access log](#the-access-log). |
|
|
286
|
+
| `maintenance` | `false` | `GEEKITY_MAINTENANCE` | Keep the site in maintenance mode, answering 503, until a restart without it. |
|
|
287
|
+
| `compression` | `true` | `GEEKITY_COMPRESSION` | Compress text responses with brotli or gzip. Turn it off behind a proxy that compresses. See [Compression](#compression). |
|
|
288
|
+
| `securityHeaders` | [see below](#security-headers) | — | Headers every response carries. A string replaces a default or adds a header, `false` removes one. |
|
|
289
|
+
| `onDocumentChange` | none | — | Hook run for every change to the index. See [Hooks](#hooks). |
|
|
290
|
+
| `onPublish` | none | — | Hook run when a document becomes visible. See [Hooks](#hooks). |
|
|
291
|
+
| `federation` | `{}` | — | Federation stores and guards. See [Federation](#federation). |
|
|
292
|
+
| `commentChecker` | Akismet | — | A spam checker of the site's own, which wins over the key in `data/akismet.json`. See [Akismet](#akismet). |
|
|
293
|
+
| `mail` | `{}` | — | Mail provider, retries, backoff and logger. See [Email](#email). |
|
|
293
294
|
|
|
294
295
|
Precedence is environment variable, then config file, then default, so a host
|
|
295
296
|
can override anything without editing the site. A boolean environment variable
|
|
@@ -352,7 +353,7 @@ the same directory reads all of it, and everything in it is meant to be public:
|
|
|
352
353
|
| Path | What it holds |
|
|
353
354
|
| -------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
354
355
|
| `content/posts/`, `content/pages/` | The Markdown documents, `_trash/` included. |
|
|
355
|
-
| `content/uploads/` | Uploaded files
|
|
356
|
+
| `content/uploads/` | Uploaded files, with their location and camera metadata removed. |
|
|
356
357
|
| `content/_data/site.json` | Every site setting. |
|
|
357
358
|
| `content/_data/federation/followers.json` | Who follows the site. |
|
|
358
359
|
| `content/_data/federation/inbox/{yyyy}-{mm}.jsonl` | Every activity the inbox was handed, one per line. |
|
|
@@ -364,6 +365,7 @@ the same directory reads all of it, and everything in it is meant to be public:
|
|
|
364
365
|
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
365
366
|
| `data/users.json` | Usernames and argon2id password hashes. Mode `0600`. |
|
|
366
367
|
| `data/keys/` | Each user's actor key pairs as JWK files. Mode `0600`. **Losing these breaks federation.** |
|
|
368
|
+
| `data/locations.json` | Where each post was written, keyed by permalink (decision-29). Mode `0600`. |
|
|
367
369
|
| `data/comment-salt` | What hides commenters' addresses in the published comment files. Mode `0600`. |
|
|
368
370
|
| `data/comments/{slug}.json` | The emails of that post's commenters, and whether each asked to hear about replies, keyed by comment id. Mode `0600`. |
|
|
369
371
|
| `data/akismet.json` | The Akismet key, and what `verify-key` last said about it. Mode `0600`. |
|
|
@@ -1517,25 +1519,27 @@ This is every piece of personal data the CMS stores, where it is, and how long
|
|
|
1517
1519
|
it stays. "Kept" means until somebody deletes it, unless a retention period
|
|
1518
1520
|
says otherwise.
|
|
1519
1521
|
|
|
1520
|
-
| What | Where | How long
|
|
1521
|
-
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
1522
|
-
| A commenter's name, website and words | `content/_data/comments/{slug}.json`, public and in git | Kept. Erasing on request replaces the name and drops the website.
|
|
1523
|
-
| A commenter's email, and whether they asked to be told about replies | `data/comments/{slug}.json`, mode `0600`, keyed by comment id | `commentEmailRetentionDays`: forever unless set, 180 on a new site.
|
|
1524
|
-
| A salted hash of the address a comment, webmention or contact message came from (the address itself is never stored) | The comment file, or the contact message file | `addressHashRetentionDays`: forever unless set, 30 on a new site.
|
|
1525
|
-
| A webmention's author name, website, avatar URL and the source page's words | `content/_data/comments/{slug}.json` | Kept, as a copy of a page that is public already.
|
|
1526
|
-
| A contact message: the sender's name, email, subject and message | `data/contact/{id}.json`, mode `0600` | `contactMessageRetentionDays`: forever unless set, 365 on a new site.
|
|
1527
|
-
| The addresses that unsubscribed from reply notices | `data/comment-optouts.json`, mode `0600` | Kept, so the site goes on not writing to them. Erasing on request removes one.
|
|
1528
|
-
| Followers: actor id, handle, display name, avatar URL, profile URL | `content/_data/federation/{username}/followers.json`, in git | Until they unfollow.
|
|
1529
|
-
| Inbound likes, boosts, replies and quotes, with the actor who sent them | `content/_data/federation/inbox/{yyyy}-{mm}.jsonl`, in git | Kept.
|
|
1530
|
-
| What a reply shows of the post it answers: its title, words and author | `content/_data/replyContexts.json`, in git | Kept.
|
|
1531
|
-
| Other actors who liked, boosted, answered or quoted: handle, display name, avatar URL, profile URL | `data/geekity.db`, fetched from their actor document | A cache. Refetched weekly while the inbox log names them.
|
|
1532
|
-
| Remote avatars, shrunk | `data/avatars/` | Deleted by the avatar sweep once nothing shown names them.
|
|
1533
|
-
| Users: username, email, argon2id password hash, profile | `data/users.json`, mode `0600` | Until the user is deleted.
|
|
1534
|
-
| The apps a user signed in to with IndieAuth: app, scopes, hashes of its tokens, when they expire | `data/indieauth-tokens.json`, mode `0600` | Until the user is deleted, or 60 days after the app last refreshed.
|
|
1535
|
-
| Recent IndieAuth and Micropub requests: app, username, what it sent (no secrets, values cut short), the answer | `data/indieauth-activity.json`, mode `0600` | The last 100 requests, none older than 14 days.
|
|
1536
|
-
| An index of all of the above, and sessions, reset tokens and spent link tokens | `data/geekity.db` | A cache of the files. Sessions and tokens are pruned when they expire.
|
|
1537
|
-
| Client addresses in the rate limits | Memory | Until the window passes or the site restarts.
|
|
1538
|
-
|
|
|
1522
|
+
| What | Where | How long |
|
|
1523
|
+
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
1524
|
+
| A commenter's name, website and words | `content/_data/comments/{slug}.json`, public and in git | Kept. Erasing on request replaces the name and drops the website. |
|
|
1525
|
+
| A commenter's email, and whether they asked to be told about replies | `data/comments/{slug}.json`, mode `0600`, keyed by comment id | `commentEmailRetentionDays`: forever unless set, 180 on a new site. |
|
|
1526
|
+
| A salted hash of the address a comment, webmention or contact message came from (the address itself is never stored) | The comment file, or the contact message file | `addressHashRetentionDays`: forever unless set, 30 on a new site. |
|
|
1527
|
+
| A webmention's author name, website, avatar URL and the source page's words | `content/_data/comments/{slug}.json` | Kept, as a copy of a page that is public already. |
|
|
1528
|
+
| A contact message: the sender's name, email, subject and message | `data/contact/{id}.json`, mode `0600` | `contactMessageRetentionDays`: forever unless set, 365 on a new site. |
|
|
1529
|
+
| The addresses that unsubscribed from reply notices | `data/comment-optouts.json`, mode `0600` | Kept, so the site goes on not writing to them. Erasing on request removes one. |
|
|
1530
|
+
| Followers: actor id, handle, display name, avatar URL, profile URL | `content/_data/federation/{username}/followers.json`, in git | Until they unfollow. |
|
|
1531
|
+
| Inbound likes, boosts, replies and quotes, with the actor who sent them | `content/_data/federation/inbox/{yyyy}-{mm}.jsonl`, in git | Kept. |
|
|
1532
|
+
| What a reply shows of the post it answers: its title, words and author | `content/_data/replyContexts.json`, in git | Kept. |
|
|
1533
|
+
| Other actors who liked, boosted, answered or quoted: handle, display name, avatar URL, profile URL | `data/geekity.db`, fetched from their actor document | A cache. Refetched weekly while the inbox log names them. |
|
|
1534
|
+
| Remote avatars, shrunk | `data/avatars/` | Deleted by the avatar sweep once nothing shown names them. |
|
|
1535
|
+
| Users: username, email, argon2id password hash, profile | `data/users.json`, mode `0600` | Until the user is deleted. |
|
|
1536
|
+
| The apps a user signed in to with IndieAuth: app, scopes, hashes of its tokens, when they expire | `data/indieauth-tokens.json`, mode `0600` | Until the user is deleted, or 60 days after the app last refreshed. |
|
|
1537
|
+
| Recent IndieAuth and Micropub requests: app, username, what it sent (no secrets, values cut short), the answer | `data/indieauth-activity.json`, mode `0600` | The last 100 requests, none older than 14 days. |
|
|
1538
|
+
| An index of all of the above, and sessions, reset tokens and spent link tokens | `data/geekity.db` | A cache of the files. Sessions and tokens are pruned when they expire. |
|
|
1539
|
+
| Client addresses in the rate limits | Memory | Until the window passes or the site restarts. |
|
|
1540
|
+
| Where and when a photo or video was taken, and the camera or phone (EXIF, XMP, IPTC, a video's `©xyz`) | Removed from `content/uploads/` on upload | Never stored. Older uploads keep it until `geekity strip-metadata`; git history keeps the old bytes until rewritten. |
|
|
1541
|
+
| Where the author wrote a post: coordinates, accuracy, and the place's name, locality, region and country | `data/locations.json`, mode `0600`, keyed by permalink | Kept until the author clears it. Published only as far as **Settings > Privacy** allows, nothing by default. |
|
|
1542
|
+
| Client addresses in the access log | stdout, and whatever collects it | Only with `accessLogAddress` on. The collector keeps them. |
|
|
1539
1543
|
|
|
1540
1544
|
Two services outside the site see personal data when a site turns them on.
|
|
1541
1545
|
Akismet is sent a commenter's or sender's address, user agent, referrer, name,
|
|
@@ -1704,6 +1708,48 @@ cacheable, and a URL that answered differently for one viewer would be cached
|
|
|
1704
1708
|
and served to the rest. Use the editor's Preview button, which renders the post
|
|
1705
1709
|
through the theme's own layout at an admin URL.
|
|
1706
1710
|
|
|
1711
|
+
## Unlisted posts
|
|
1712
|
+
|
|
1713
|
+
An unlisted post is published at its URL and left off every list. Choose
|
|
1714
|
+
Unlisted in the editor's Visibility field, send `visibility=unlisted` from a
|
|
1715
|
+
Micropub app, or write it into the front matter:
|
|
1716
|
+
|
|
1717
|
+
```yaml
|
|
1718
|
+
visibility: unlisted
|
|
1719
|
+
```
|
|
1720
|
+
|
|
1721
|
+
Public is the key's absence, and the editor removes the key when you choose
|
|
1722
|
+
Public again. The site has no private posts. A value other than `public` or
|
|
1723
|
+
`unlisted`, such as a hand-typed `visibility: private`, hides the post the way
|
|
1724
|
+
`draft: true` does: its URL answers 404, it is on no list, and a post the
|
|
1725
|
+
followers already hold is withdrawn with a `Delete`. The editor shows the value
|
|
1726
|
+
as it is stored, marks the post Hidden in the list, and keeps the value through
|
|
1727
|
+
a save until you choose Public or Unlisted.
|
|
1728
|
+
|
|
1729
|
+
The rule works for pages too.
|
|
1730
|
+
|
|
1731
|
+
| Where | An unlisted post |
|
|
1732
|
+
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1733
|
+
| Its permalink, in every representation | 200, with `X-Robots-Tag: noindex`. The default theme also prints `<meta name="robots" content="noindex">`. |
|
|
1734
|
+
| The home page, tag, category and author archives, the `archive: true` page | Absent, and not counted in a pager or a tag count. |
|
|
1735
|
+
| Previous and next links under other posts, the front page's recent posts | Absent. |
|
|
1736
|
+
| Every RSS, Atom and JSON feed, and the site-wide comments feed | Absent. Saving one pings no notify server. |
|
|
1737
|
+
| The sitemap, search (HTML and JSON) and `llms.txt` | Absent. |
|
|
1738
|
+
| IndexNow | Not submitted. A public post that becomes unlisted is submitted once, so a search engine reads the `noindex`. |
|
|
1739
|
+
| The ActivityPub outbox and featured collection | Absent. |
|
|
1740
|
+
| Followers | Get the `Create` and every `Update`, addressed `to` the followers with Public in `cc`, which Mastodon shows as unlisted. Relays get nothing. |
|
|
1741
|
+
| Webmentions | Sent as for any post: the page is public. |
|
|
1742
|
+
|
|
1743
|
+
Changing a published post between public and unlisted sends its followers an
|
|
1744
|
+
`Update` with the new addressing.
|
|
1745
|
+
|
|
1746
|
+
In code, `isServed(document)` says whether the site serves a document at its
|
|
1747
|
+
URL and `isListed(document)` whether it also lists it. The index answers the
|
|
1748
|
+
same two rules in SQL. An Eleventy build with the example config reads the key
|
|
1749
|
+
too: an unlisted post gets its page, `noindex`, and no place in any collection,
|
|
1750
|
+
and an unrecognized value builds no page. See
|
|
1751
|
+
[Building the same content with Eleventy](#building-the-same-content-with-eleventy).
|
|
1752
|
+
|
|
1707
1753
|
## Hooks
|
|
1708
1754
|
|
|
1709
1755
|
Two hooks let a site do something of its own when content changes. Both are
|
|
@@ -1868,8 +1914,8 @@ email address, notification preferences, a public profile and the
|
|
|
1868
1914
|
[stored actor id](#a-users-stored-actor-id) they were published under
|
|
1869
1915
|
elsewhere. Written atomically with `0600` permissions and serialised against
|
|
1870
1916
|
itself, so two admins adding the same name at once cannot both succeed. It is in `data/` rather than `content/` because
|
|
1871
|
-
`content/` is published with the site, and it is one of the
|
|
1872
|
-
`dataDir` that must be backed up
|
|
1917
|
+
`content/` is published with the site, and it is one of the files under
|
|
1918
|
+
`dataDir` that must be backed up.
|
|
1873
1919
|
|
|
1874
1920
|
Sessions stay in SQLite, where the rest of the cache lives. They name a user by
|
|
1875
1921
|
id and are joined against the file on every admin request, so a session whose
|
|
@@ -2088,6 +2134,60 @@ shows up without a restart. `readAltTexts(contentDir)` and
|
|
|
2088
2134
|
`undescribedImages(html, library)` in `src/images/alt-text.ts` are what the
|
|
2089
2135
|
editor's publish check and the federated `Image` attachments read.
|
|
2090
2136
|
|
|
2137
|
+
#### Location and camera metadata
|
|
2138
|
+
|
|
2139
|
+
A phone photo carries where it was taken, when, and on what camera, and the
|
|
2140
|
+
original under `content/uploads` is what the site serves at `/uploads/…`, puts
|
|
2141
|
+
in every `<picture>` as the fallback, and federates as a photo post's
|
|
2142
|
+
attachment. So `storeUpload` removes that metadata before the file is written,
|
|
2143
|
+
for every upload: the editor's control, the media library, the Micropub media
|
|
2144
|
+
endpoint and Micropub photos. It is not a setting.
|
|
2145
|
+
|
|
2146
|
+
| Format | What goes | What stays |
|
|
2147
|
+
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
2148
|
+
| JPEG | EXIF (GPS, camera, lens, serial, time), XMP, IPTC, comments, other APPn segments, data after the image | JFIF, the ICC profile, Adobe's colour flag, the orientation |
|
|
2149
|
+
| PNG | `eXIf`, `tEXt`, `zTXt`, `iTXt` (XMP), `tIME` and private chunks | Image and colour chunks, APNG animation, the orientation |
|
|
2150
|
+
| WebP | `EXIF`, `XMP ` and unknown chunks | The bitstream, alpha, animation, ICC profile, the orientation |
|
|
2151
|
+
| AVIF | The contents of the Exif and XMP items, overwritten with an empty EXIF block and an empty XMP packet | The picture, and its `irot`/`imir` orientation |
|
|
2152
|
+
| GIF | Comment extensions and application extensions other than looping and ICC (XMP lives in one) | Frames, timing, looping |
|
|
2153
|
+
| MP4, M4V | `udta` (the `©xyz` location, camera), `meta` (the QuickTime location key) and `uuid` (XMP) boxes, in the movie, its tracks and at the top level | Every other box |
|
|
2154
|
+
|
|
2155
|
+
Nothing is re-encoded. Segments, chunks and blocks are cut out and the pixel
|
|
2156
|
+
data is copied byte for byte, so a stored JPEG or PNG decodes to the same
|
|
2157
|
+
pixels as the upload. A picture taken sideways keeps a minimal EXIF block
|
|
2158
|
+
holding its orientation and nothing else, so it still shows the right way up.
|
|
2159
|
+
AVIF and MP4 keep their layout exactly: the metadata is overwritten in place
|
|
2160
|
+
and a removed box becomes a zero-filled `free` box of the same size, so no
|
|
2161
|
+
offset inside the file moves. A file whose structure cannot be followed far
|
|
2162
|
+
enough to find its metadata is refused with a 415 rather than stored as it is.
|
|
2163
|
+
|
|
2164
|
+
Audio (MP3, M4A, Ogg), WebM, PDF and the text formats are stored as they
|
|
2165
|
+
arrive. An episode's tags are its title and artwork, not where it was
|
|
2166
|
+
recorded.
|
|
2167
|
+
|
|
2168
|
+
Files uploaded before this version still carry their metadata. Strip them
|
|
2169
|
+
with
|
|
2170
|
+
|
|
2171
|
+
```sh
|
|
2172
|
+
geekity strip-metadata
|
|
2173
|
+
```
|
|
2174
|
+
|
|
2175
|
+
which rewrites each file under `content/uploads` that has something to remove,
|
|
2176
|
+
prints what it removed, keeps the file's modification time (the library sorts
|
|
2177
|
+
by it), and leaves alone, and names, a file it cannot read, exiting `1`. It is
|
|
2178
|
+
safe to run again: a clean file is left as it is, and a second run reports
|
|
2179
|
+
every file already clean. The derived variants under `data/images` never
|
|
2180
|
+
carried metadata.
|
|
2181
|
+
|
|
2182
|
+
A site that keeps `content/` in git still has the old bytes in its history,
|
|
2183
|
+
and so does every clone and fork of it. Removing them means rewriting that
|
|
2184
|
+
history. One way: run `geekity strip-metadata`, copy `content/uploads`
|
|
2185
|
+
somewhere outside the repository, remove it from every commit with
|
|
2186
|
+
[`git filter-repo`](https://github.com/newren/git-filter-repo)
|
|
2187
|
+
`--path content/uploads --invert-paths`, copy the stripped files back, commit
|
|
2188
|
+
them, and force-push. That changes every commit hash, so anybody else with a
|
|
2189
|
+
clone has to clone again.
|
|
2190
|
+
|
|
2091
2191
|
### Image variants
|
|
2092
2192
|
|
|
2093
2193
|
`generateImageVariants(config, uploadPath)` derives one upload's copies with
|
|
@@ -2956,6 +3056,16 @@ its own item: RSS as `dc:language`, Atom as `xml:lang` on the `entry` and JSON
|
|
|
2956
3056
|
Feed as the item's `language`. A post in the site's language, or whose `lang`
|
|
2957
3057
|
is not a language tag, adds nothing.
|
|
2958
3058
|
|
|
3059
|
+
**Photos.** A post's `photo` front matter prints in every format's content,
|
|
3060
|
+
before the body, which is where the page prints it too. Each photo is a plain
|
|
3061
|
+
`<figure><img src alt></figure>` of the original upload, never the responsive
|
|
3062
|
+
variants, which a reader cannot resolve (decision-10). Its `src` is absolute on
|
|
3063
|
+
the site's base URL. Its `alt` is the post's own text, else the media library's,
|
|
3064
|
+
else empty. So a photo-only post reads in a feed reader as its photos. JSON Feed
|
|
3065
|
+
also names the item's main image as `image`: the post's `image` front matter,
|
|
3066
|
+
else its first photo. A photo is never an RSS `enclosure`, an Atom
|
|
3067
|
+
`rel="enclosure"` link or a JSON Feed attachment. Those carry a recording only.
|
|
3068
|
+
|
|
2959
3069
|
### RSS 2.0
|
|
2960
3070
|
|
|
2961
3071
|
The channel carries `title`, `link`, `description` (the tagline), `language`
|
|
@@ -3058,7 +3168,7 @@ Feed has no field for a license and says nothing about one.
|
|
|
3058
3168
|
[rfc4946]: https://www.rfc-editor.org/rfc/rfc4946
|
|
3059
3169
|
|
|
3060
3170
|
A JSON Feed item carries `id` (the object id), `url` (the permalink), `title`,
|
|
3061
|
-
`content_html`, `summary`, `date_published`, `date_modified`, `tags` (the
|
|
3171
|
+
`content_html`, `image` (see Photos above), `summary`, `date_published`, `date_modified`, `tags` (the
|
|
3062
3172
|
terms) and `authors`; the feed carries `version`, `title`, `home_page_url`,
|
|
3063
3173
|
`feed_url`, `description`, `authors` and `hubs`. Keys with nothing behind them
|
|
3064
3174
|
are left out rather than sent empty.
|
|
@@ -3349,9 +3459,10 @@ dates — a page with no date in its front matter — carries no `<lastmod>`
|
|
|
3349
3459
|
rather than an invented one.
|
|
3350
3460
|
|
|
3351
3461
|
Nothing hidden reaches it. The sitemap is drawn from the same queries the
|
|
3352
|
-
listings are and checked against the same `
|
|
3353
|
-
|
|
3354
|
-
|
|
3462
|
+
listings are and checked against the same `isListed`, so drafts, the trash and
|
|
3463
|
+
posts whose date has not arrived are absent for the same reason their
|
|
3464
|
+
permalinks 404, and an [unlisted post](#unlisted-posts) is absent for the same
|
|
3465
|
+
reason it is off the home page.
|
|
3355
3466
|
|
|
3356
3467
|
The protocol caps one file at 50,000 URLs. Past that, `/sitemap.xml` becomes a
|
|
3357
3468
|
`<sitemapindex>` naming `/sitemap-1.xml`, `/sitemap-2.xml` and so on, each
|
|
@@ -3684,6 +3795,7 @@ the rules the CMS follows that Eleventy does not know about on its own:
|
|
|
3684
3795
|
| Rule | How the config does it |
|
|
3685
3796
|
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3686
3797
|
| `draft: true` hides a document | An `addPreprocessor` that returns `false` for it. `BUILD_DRAFTS=1` builds drafts anyway, for a local preview. |
|
|
3798
|
+
| `visibility: unlisted` is listed nowhere | An `addPreprocessor` that sets `eleventyExcludeFromCollections` on the document, so a feed, sitemap or archive drawn from collections leaves it out, and sets `noindex` for the layout to print as `<meta name="robots" content="noindex">`. Any value other than `public` or `unlisted` returns `false`, as a draft does. |
|
|
3687
3799
|
| A file with no `permalink` gets the CMS default | An `addPreprocessor` that fills in `/{yyyy}/{mm}/{slug}/` for a post and `/{slug}/` for a page, slugifying the title exactly as the CMS does. Front matter always wins; the CMS writes `permalink` into every file it saves, so this only matters for hand-authored files. |
|
|
3688
3800
|
| `content/uploads/` is served at `/uploads/` | `addPassthroughCopy({ 'content/uploads': 'uploads' })`, plus an `ignores` entry for the same path. Without the ignore, an upload that happens to be Markdown would be copied _and_ rendered as a page; the CMS only ever indexes `posts/` and `pages/`. |
|
|
3689
3801
|
| `content/_data/federation/` is data | `followers.json` is an ordinary data file in a namespaced `_data` subdirectory, so Eleventy hands it over as `federation.followers` without being told. The monthly inbox logs are JSON Lines, which Eleventy has no reader for, so the config registers one with `addDataExtension('jsonl', …)`: each month becomes an entry of `federation.inbox`, keyed by its `{yyyy}-{mm}` name. |
|
|
@@ -86,6 +86,27 @@
|
|
|
86
86
|
{% endfor %}
|
|
87
87
|
</datalist>
|
|
88
88
|
</fieldset>
|
|
89
|
+
<fieldset class="admin-editor-enclosure admin-editor-location">
|
|
90
|
+
<legend>Location</legend>
|
|
91
|
+
<p class="admin-hint">
|
|
92
|
+
Kept in <code>data/locations.json</code>, not in the post’s file. What readers see of it
|
|
93
|
+
is chosen on Settings > Privacy. Empty every box to remove it.
|
|
94
|
+
</p>
|
|
95
|
+
<label for="editor-location-geo">Coordinates</label>
|
|
96
|
+
<input id="editor-location-geo" name="{{ locationFields.geo }}" type="text" value="{{ form.location.geo }}" placeholder="48.85837, 2.29448" autocomplete="off" spellcheck="false" aria-describedby="editor-location-geo-hint" />
|
|
97
|
+
<p class="admin-hint" id="editor-location-geo-hint">Latitude, longitude, as decimal degrees. A <code>geo:</code> URI works too.</p>
|
|
98
|
+
<label for="editor-location-accuracy">Accuracy</label>
|
|
99
|
+
<input id="editor-location-accuracy" name="{{ locationFields.accuracy }}" type="text" inputmode="numeric" value="{{ form.location.accuracy }}" autocomplete="off" aria-describedby="editor-location-accuracy-hint" />
|
|
100
|
+
<p class="admin-hint" id="editor-location-accuracy-hint">Metres. Leave it empty if you do not know.</p>
|
|
101
|
+
<label for="editor-location-name">Place</label>
|
|
102
|
+
<input id="editor-location-name" name="{{ locationFields.name }}" type="text" value="{{ form.location.name }}" />
|
|
103
|
+
<label for="editor-location-locality">Locality</label>
|
|
104
|
+
<input id="editor-location-locality" name="{{ locationFields.locality }}" type="text" value="{{ form.location.locality }}" />
|
|
105
|
+
<label for="editor-location-region">Region</label>
|
|
106
|
+
<input id="editor-location-region" name="{{ locationFields.region }}" type="text" value="{{ form.location.region }}" />
|
|
107
|
+
<label for="editor-location-country">Country</label>
|
|
108
|
+
<input id="editor-location-country" name="{{ locationFields.country }}" type="text" value="{{ form.location.country }}" />
|
|
109
|
+
</fieldset>
|
|
89
110
|
<fieldset class="admin-editor-enclosure">
|
|
90
111
|
<legend>Recording</legend>
|
|
91
112
|
|
|
@@ -199,6 +220,29 @@
|
|
|
199
220
|
<label for="editor-bookmark-of">Bookmark of</label>
|
|
200
221
|
<input id="editor-bookmark-of" name="bookmark-of" type="url" value="{{ form.bookmarkOf }}" />
|
|
201
222
|
<p class="admin-hint">The address of a post this one likes, reposts or bookmarks. Leave them empty for a post that cites nothing.</p>
|
|
223
|
+
|
|
224
|
+
<fieldset class="admin-editor-enclosure admin-editor-read">
|
|
225
|
+
<legend>Read</legend>
|
|
226
|
+
<label for="editor-read-status">Read status</label>
|
|
227
|
+
<select id="editor-read-status" name="{{ readFields.status }}">
|
|
228
|
+
<option value=""{% if form.readStatus == "" %} selected{% endif %}>Not a read</option>
|
|
229
|
+
{% for status in readStatuses %}
|
|
230
|
+
<option value="{{ status.value }}"{% if form.readStatus == status.value %} selected{% endif %}>{{ status.label }}</option>
|
|
231
|
+
{% endfor %}
|
|
232
|
+
{% if unrecognizedReadStatus %}
|
|
233
|
+
<option value="{{ unrecognizedReadStatus }}" selected>{{ unrecognizedReadStatus }} (not recognized)</option>
|
|
234
|
+
{% endif %}
|
|
235
|
+
</select>
|
|
236
|
+
<label for="editor-read-of-name">Title</label>
|
|
237
|
+
<input id="editor-read-of-name" name="{{ readFields.name }}" type="text" value="{{ form.readOf.name }}" />
|
|
238
|
+
<label for="editor-read-of-author">Author</label>
|
|
239
|
+
<input id="editor-read-of-author" name="{{ readFields.author }}" type="text" value="{{ form.readOf.author }}" />
|
|
240
|
+
<label for="editor-read-of-uid">Identifier</label>
|
|
241
|
+
<input id="editor-read-of-uid" name="{{ readFields.uid }}" type="text" value="{{ form.readOf.uid }}" placeholder="isbn:9780441478125" autocomplete="off" spellcheck="false" />
|
|
242
|
+
<label for="editor-read-of-url">Address</label>
|
|
243
|
+
<input id="editor-read-of-url" name="{{ readFields.url }}" type="url" value="{{ form.readOf.url }}" />
|
|
244
|
+
<p class="admin-hint">A book or paper this post says its author read. An identifier is an ISBN or a DOI, written isbn:… or doi:…. Choose Not a read and empty every box to remove it. A read is described by what it says, so leave its Description empty.</p>
|
|
245
|
+
</fieldset>
|
|
202
246
|
{% endif %}
|
|
203
247
|
|
|
204
248
|
<label for="editor-description">Description</label>
|
|
@@ -229,6 +273,25 @@
|
|
|
229
273
|
<label for="editor-draft">Draft</label>
|
|
230
274
|
</p>
|
|
231
275
|
|
|
276
|
+
<label for="editor-visibility">Visibility</label>
|
|
277
|
+
<select id="editor-visibility" name="visibility" aria-describedby="editor-visibility-hint">
|
|
278
|
+
{% if form.visibility.unrecognized is defined %}
|
|
279
|
+
<option value="{{ form.visibility.unrecognized }}" selected>{{ form.visibility.unrecognized }} (not recognized)</option>
|
|
280
|
+
{% endif %}
|
|
281
|
+
<option value="public"{% if form.visibility == "public" %} selected{% endif %}>Public</option>
|
|
282
|
+
<option value="unlisted"{% if form.visibility == "unlisted" %} selected{% endif %}>Unlisted</option>
|
|
283
|
+
</select>
|
|
284
|
+
<p class="admin-hint" id="editor-visibility-hint">
|
|
285
|
+
{% if form.visibility.unrecognized is defined %}
|
|
286
|
+
The front matter says <code>visibility: {{ form.visibility.unrecognized }}</code>, which this
|
|
287
|
+
site does not recognize. The {{ kind.singular }} is hidden until you choose Public or Unlisted:
|
|
288
|
+
its page answers 404 and it is not listed or sent to your followers.
|
|
289
|
+
{% endif %}
|
|
290
|
+
Unlisted writes <code>visibility: unlisted</code> into the front matter. The {{ kind.singular }}
|
|
291
|
+
keeps its page and still goes to your followers, but is left off the home page, archives,
|
|
292
|
+
feeds, the sitemap, search and llms.txt, and asks search engines not to index it.
|
|
293
|
+
</p>
|
|
294
|
+
|
|
232
295
|
{% if kind.type == "post" %}
|
|
233
296
|
{# TASK-207: shown at the top of the author's fediverse profile, from
|
|
234
297
|
the featured collection their actor links. #}
|
|
@@ -69,6 +69,8 @@
|
|
|
69
69
|
<span class="admin-status admin-status-draft">Draft</span>
|
|
70
70
|
{% elif document.scheduled %}
|
|
71
71
|
<span class="admin-status admin-status-scheduled">Scheduled</span>
|
|
72
|
+
{% elif document.hidden %}
|
|
73
|
+
<span class="admin-status admin-status-draft">Hidden</span>
|
|
72
74
|
{% else %}
|
|
73
75
|
<span class="admin-status">Published</span>
|
|
74
76
|
{% endif %}
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
{% endif %}
|
|
27
27
|
</dl>
|
|
28
28
|
|
|
29
|
+
{% if withoutPkce %}
|
|
30
|
+
<p class="admin-hint">This app does not use PKCE, which protects a sign-in if the code it is handed is intercepted. It is let in only because it is on your list of apps allowed without PKCE, under Users > Connected apps.</p>
|
|
31
|
+
{% endif %}
|
|
32
|
+
|
|
29
33
|
{% if loopback %}
|
|
30
34
|
<p class="admin-hint">This app runs on your own computer or network. Approve only if you started this sign-in yourself.</p>
|
|
31
35
|
{% endif %}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{% extends "layouts/settings-page.njk" %}
|
|
2
|
+
{% import "components/fields.njk" as field %}
|
|
3
|
+
|
|
4
|
+
{% block settingsProblems %}
|
|
5
|
+
{{ field.problem('settings-location-sharing', problems.locationSharing) }}
|
|
6
|
+
{% endblock %}
|
|
7
|
+
|
|
8
|
+
{% block settingsFields %}
|
|
9
|
+
<h2>Location</h2>
|
|
10
|
+
|
|
11
|
+
{% set locationHint %}
|
|
12
|
+
A Micropub app such as Quill can send where a post was written, and the
|
|
13
|
+
editor has fields for it. The site keeps it in <code>data/locations.json</code>,
|
|
14
|
+
never in the post's file, so it stays out of <code>content/</code> and its git
|
|
15
|
+
history whatever is chosen here. Exact coordinates can say where you live or
|
|
16
|
+
are right now. Changing this changes every post at once, and nothing is
|
|
17
|
+
rewritten.
|
|
18
|
+
{% endset %}
|
|
19
|
+
{% call field.select('settings-location-sharing', fields.locationSharing, 'Location on posts',
|
|
20
|
+
error=problems.locationSharing, hint=locationHint, autofocus=true) %}
|
|
21
|
+
{% for option in [
|
|
22
|
+
['none', 'Keep it, publish nothing'],
|
|
23
|
+
['place', 'Publish the place only: its name, locality, region and country'],
|
|
24
|
+
['exact', 'Publish the exact coordinates too']] %}
|
|
25
|
+
<option value="{{ option[0] }}"{% if option[0] == form.locationSharing %} selected{% endif %}>{{ option[1] }}</option>
|
|
26
|
+
{% endfor %}
|
|
27
|
+
{% endcall %}
|
|
28
|
+
|
|
29
|
+
<h2>Uploads</h2>
|
|
30
|
+
|
|
31
|
+
<p class="admin-hint">
|
|
32
|
+
Location and camera metadata is always removed from pictures and videos as
|
|
33
|
+
they are uploaded, whatever is chosen above. Files uploaded before this
|
|
34
|
+
version keep theirs until <code>geekity strip-metadata</code> is run over
|
|
35
|
+
<code>content/uploads/</code>; the package README describes the command.
|
|
36
|
+
</p>
|
|
37
|
+
{% endblock %}
|