@geekity/cms 0.10.0 → 0.12.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 +22 -5
- package/admin/components/fields.njk +55 -12
- package/admin/layouts/base.njk +5 -1
- package/admin/layouts/settings-page.njk +7 -2
- package/admin/layouts/shell.njk +1 -1
- package/admin/pages/account/forgot.njk +1 -1
- package/admin/pages/account/login.njk +1 -1
- package/admin/pages/account/reset.njk +4 -2
- package/admin/pages/account/setup.njk +1 -1
- package/admin/pages/dashboard/home.njk +1 -0
- package/admin/pages/documents/list.njk +1 -0
- package/admin/pages/documents/taxonomy.njk +1 -0
- package/admin/pages/federation/followers.njk +3 -0
- package/admin/pages/federation/settings.njk +5 -0
- package/admin/pages/media/library.njk +39 -0
- package/admin/pages/navigation/menus.njk +6 -1
- package/admin/pages/settings/discussion.njk +7 -0
- package/admin/pages/settings/email.njk +10 -0
- package/admin/pages/settings/general.njk +7 -0
- package/admin/pages/settings/permalinks.njk +6 -0
- package/admin/pages/settings/reading.njk +7 -0
- package/admin/pages/tools/content-index.njk +1 -0
- package/admin/pages/tools/personal-data.njk +5 -0
- package/admin/pages/users/edit.njk +7 -5
- package/admin/pages/users/list.njk +1 -0
- package/admin/pages/users/new.njk +5 -1
- package/admin/static/admin-bar.css +16 -0
- package/admin/static/admin.css +103 -3
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +18 -0
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/media.d.ts +13 -2
- package/dist/admin/media.d.ts.map +1 -1
- package/dist/admin/media.js +58 -3
- package/dist/admin/media.js.map +1 -1
- package/dist/admin/store.d.ts +1 -1
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +1 -1
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/uploads.d.ts +5 -2
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +6 -2
- package/dist/admin/uploads.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +37 -1
- package/dist/federation/article.js.map +1 -1
- package/dist/images/alt-text.d.ts +51 -0
- package/dist/images/alt-text.d.ts.map +1 -0
- package/dist/images/alt-text.js +113 -0
- package/dist/images/alt-text.js.map +1 -0
- package/dist/images/markup.d.ts +11 -0
- package/dist/images/markup.d.ts.map +1 -1
- package/dist/images/markup.js +12 -0
- package/dist/images/markup.js.map +1 -1
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +27 -12
- package/dist/web/render.js.map +1 -1
- package/package.json +14 -9
- package/themes/default/README.md +230 -106
- package/themes/default/layouts/404.njk +2 -0
- package/themes/default/layouts/500.njk +2 -0
- package/themes/default/layouts/503.njk +2 -0
- package/themes/default/layouts/author.njk +3 -1
- package/themes/default/layouts/base.njk +1 -1
- package/themes/default/layouts/category.njk +4 -2
- package/themes/default/layouts/front-page.njk +1 -1
- package/themes/default/layouts/page.njk +4 -2
- package/themes/default/layouts/post.njk +41 -23
- package/themes/default/layouts/search.njk +2 -5
- package/themes/default/layouts/tag.njk +7 -1
- package/themes/default/partials/comment-form.njk +41 -14
- package/themes/default/partials/contact-form.njk +35 -14
- package/themes/default/partials/conversation.njk +11 -4
- package/themes/default/partials/kicker.njk +34 -0
- package/themes/default/partials/post-list.njk +40 -46
- package/themes/default/partials/reply-context.njk +12 -5
- package/themes/default/src/style.css +1095 -0
- package/themes/default/static/style.css +2079 -1083
- package/themes/default/theme.json +1 -1
package/README.md
CHANGED
|
@@ -1676,6 +1676,7 @@ shadow the login form.
|
|
|
1676
1676
|
| `/admin/media` | Everything under `content/uploads`, with the URL, the Markdown and what uses it. |
|
|
1677
1677
|
| `/admin/media/upload` | `POST` only. Stores one file by the rules the editor's upload enforces. |
|
|
1678
1678
|
| `/admin/media/delete` | `POST` only. Deletes one upload, asking first when a document points at it. |
|
|
1679
|
+
| `/admin/media/alt` | `POST` only. Sets a picture's alt text, or marks it decorative, in `media.json`. |
|
|
1679
1680
|
| `/admin/comments` | Pending, approved and spam, with approve, spam, delete and reply. |
|
|
1680
1681
|
| `/admin/comments/moderate` | `POST` only. Approves one comment, files it as spam, or deletes it. |
|
|
1681
1682
|
| `/admin/comments/reply` | `POST` only. Posts an approved reply under the comment it answers. |
|
|
@@ -1930,8 +1931,9 @@ written by an Eleventy build is on the screen without a restart, and one
|
|
|
1930
1931
|
removed the same way is off it. The only question the index is asked is which
|
|
1931
1932
|
documents mention a URL, which is the one thing a directory cannot answer.
|
|
1932
1933
|
|
|
1933
|
-
Each row carries the public URL and the ready-made Markdown — ``
|
|
1935
|
+
for a picture, with its alt text from the library or empty when it has none or
|
|
1936
|
+
is decorative, and `[name](url)` for anything else, from the same function the
|
|
1935
1937
|
editor's upload control pastes from — as readonly text fields, which select and
|
|
1936
1938
|
copy on their own. `admin/static/copy.js` reveals a Copy button beside each and
|
|
1937
1939
|
does nothing else, so the screen is complete before it loads and a browser
|
|
@@ -1956,7 +1958,17 @@ name a file elsewhere in the content directory.
|
|
|
1956
1958
|
content-relative path once the original is gone. The original under
|
|
1957
1959
|
`content/uploads` is the only source of truth (decision-10) and anything
|
|
1958
1960
|
generated from it is derived state that must not outlive it; the hook defaults
|
|
1959
|
-
to `removeImageVariants`, which takes the file's derived images with it.
|
|
1961
|
+
to `removeImageVariants`, which takes the file's derived images with it. The
|
|
1962
|
+
file's entry in `content/_data/media.json` goes too.
|
|
1963
|
+
|
|
1964
|
+
A picture's row also carries its alt text: a text field, a Decorative checkbox
|
|
1965
|
+
that wins over the text, and a line saying which of the two the library holds
|
|
1966
|
+
or that it holds neither. The form posts to `/admin/media/alt`, which writes
|
|
1967
|
+
`content/_data/media.json` atomically, keeping every other entry. The file is
|
|
1968
|
+
the truth (decision-9) and is read on each request, so an entry written by hand
|
|
1969
|
+
shows up without a restart. `readAltTexts(contentDir)` and
|
|
1970
|
+
`undescribedImages(html, library)` in `src/images/alt-text.ts` are what the
|
|
1971
|
+
editor's publish check and the federated `Image` attachments read.
|
|
1960
1972
|
|
|
1961
1973
|
### Image variants
|
|
1962
1974
|
|
|
@@ -3178,8 +3190,13 @@ The admin is not themed. Its templates and its static files live in a tree of
|
|
|
3178
3190
|
their own with a loader of their own, deliberately off this search path, so no
|
|
3179
3191
|
theme can shadow the login form or the CSRF field inside it.
|
|
3180
3192
|
|
|
3181
|
-
The packaged theme is the
|
|
3182
|
-
|
|
3193
|
+
The packaged theme is the Paper design (doc-9) on the andrewshell.org shell
|
|
3194
|
+
(decision-16). Its stylesheet is written in Tailwind v4 in
|
|
3195
|
+
`themes/default/src/style.css` and compiled to `themes/default/static/style.css`
|
|
3196
|
+
when the package is built (decision-22), so what ships is one plain CSS file; a
|
|
3197
|
+
site theme's own `static/style.css` replaces it wholesale and needs no
|
|
3198
|
+
Tailwind. Every colour is a `--color-*` custom property a site can override.
|
|
3199
|
+
`base.njk` is its shell: a skip link, a `.global-wrapper` carrying `data-is-root-path="true"`
|
|
3183
3200
|
at `/` only, a header that is the site title and tagline on the front page and a
|
|
3184
3201
|
small link home on every other — with `menus.primary` inside it on every page
|
|
3185
3202
|
— `<main id="main">`, and a footer with the copyright year, the site author,
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
with. `errorHtml` is the rare second thing a field has to say — the Reading
|
|
35
35
|
page's "that page is no longer published" — already written as its own
|
|
36
36
|
paragraph. #}
|
|
37
|
-
{% macro _below(hint, error, errorHtml) %}
|
|
37
|
+
{% macro _below(id, hint, error, errorHtml) %}
|
|
38
38
|
{% if error %}
|
|
39
|
-
<p class="admin-field-error">{{ error }}</p>
|
|
39
|
+
<p class="admin-field-error" id="{{ id }}-error">{{ error }}</p>
|
|
40
40
|
{% endif %}
|
|
41
41
|
{% if errorHtml %}
|
|
42
42
|
{{ errorHtml | safe }}
|
|
@@ -46,6 +46,14 @@
|
|
|
46
46
|
{% endif %}
|
|
47
47
|
{% endmacro %}
|
|
48
48
|
|
|
49
|
+
{# What a control says about itself when the form refused it: that it is
|
|
50
|
+
invalid, and which paragraph says why, so a screen reader reads the message
|
|
51
|
+
with the field rather than leaving it to be found. `errorHtml` is not named
|
|
52
|
+
here: it is a standing note about the stored value, not a refusal. #}
|
|
53
|
+
{% macro _invalid(id, error) -%}
|
|
54
|
+
{% if error %} aria-invalid="true" aria-describedby="{{ id }}-error"{% endif %}
|
|
55
|
+
{%- endmacro %}
|
|
56
|
+
|
|
49
57
|
{# A box somebody types into: text, email, number, or whatever `type` says.
|
|
50
58
|
|
|
51
59
|
`value` is always written, even empty, so the control's state is on the page
|
|
@@ -71,8 +79,8 @@
|
|
|
71
79
|
disabled=false
|
|
72
80
|
) %}
|
|
73
81
|
<label for="{{ id }}">{{ label | safe }}</label>
|
|
74
|
-
<input id="{{ id }}" name="{{ name }}" type="{{ type }}"{% if min %} min="{{ min }}"{% endif %}{% if step %} step="{{ step }}"{% endif %}{% if inputmode %} inputmode="{{ inputmode }}"{% endif %}{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %} value="{{ value }}"{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if readonly %} readonly{% endif %}{% if disabled %} disabled{% endif %} />
|
|
75
|
-
{{ _below(hint, error, errorHtml) }}
|
|
82
|
+
<input id="{{ id }}" name="{{ name }}" type="{{ type }}"{% if min %} min="{{ min }}"{% endif %}{% if step %} step="{{ step }}"{% endif %}{% if inputmode %} inputmode="{{ inputmode }}"{% endif %}{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %} value="{{ value }}"{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if readonly %} readonly{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error) }} />
|
|
83
|
+
{{ _below(id, hint, error, errorHtml) }}
|
|
76
84
|
{%- endmacro %}
|
|
77
85
|
|
|
78
86
|
{# A password box. It carries `value=""` and no way to set one: nothing typed
|
|
@@ -93,8 +101,8 @@
|
|
|
93
101
|
autofocus=false
|
|
94
102
|
) %}
|
|
95
103
|
<label for="{{ id }}">{{ label | safe }}</label>
|
|
96
|
-
<input id="{{ id }}" name="{{ name }}" type="password" value=""{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %} />
|
|
97
|
-
{{ _below(hint, error, errorHtml) }}
|
|
104
|
+
<input id="{{ id }}" name="{{ name }}" type="password" value=""{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{{ _invalid(id, error) }} />
|
|
105
|
+
{{ _below(id, hint, error, errorHtml) }}
|
|
98
106
|
{%- endmacro %}
|
|
99
107
|
|
|
100
108
|
{# A box of several lines. #}
|
|
@@ -114,8 +122,8 @@
|
|
|
114
122
|
disabled=false
|
|
115
123
|
) %}
|
|
116
124
|
<label for="{{ id }}">{{ label | safe }}</label>
|
|
117
|
-
<textarea id="{{ id }}" name="{{ name }}"{% if rows %} rows="{{ rows }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if readonly %} readonly{% endif %}{% if disabled %} disabled{% endif %}>{{ value }}</textarea>
|
|
118
|
-
{{ _below(hint, error, errorHtml) }}
|
|
125
|
+
<textarea id="{{ id }}" name="{{ name }}"{% if rows %} rows="{{ rows }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if readonly %} readonly{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error) }}>{{ value }}</textarea>
|
|
126
|
+
{{ _below(id, hint, error, errorHtml) }}
|
|
119
127
|
{%- endmacro %}
|
|
120
128
|
|
|
121
129
|
{# A list to pick from. The options are the call's body, because which of them
|
|
@@ -137,10 +145,10 @@
|
|
|
137
145
|
disabled=false
|
|
138
146
|
) %}
|
|
139
147
|
<label for="{{ id }}">{{ label | safe }}</label>
|
|
140
|
-
<select id="{{ id }}" name="{{ name }}"{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if disabled %} disabled{% endif %}>
|
|
148
|
+
<select id="{{ id }}" name="{{ name }}"{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error) }}>
|
|
141
149
|
{{ caller() }}
|
|
142
150
|
</select>
|
|
143
|
-
{{ _below(hint, error, errorHtml) }}
|
|
151
|
+
{{ _below(id, hint, error, errorHtml) }}
|
|
144
152
|
{%- endmacro %}
|
|
145
153
|
|
|
146
154
|
{# A switch. The label follows the box rather than heading it, which is what a
|
|
@@ -158,8 +166,43 @@
|
|
|
158
166
|
disabled=false
|
|
159
167
|
) %}
|
|
160
168
|
<p class="admin-check">
|
|
161
|
-
<input id="{{ id }}" name="{{ name }}" type="checkbox" value="{{ value }}"{% if checked %} checked{% endif %}{% if disabled %} disabled{% endif %} />
|
|
169
|
+
<input id="{{ id }}" name="{{ name }}" type="checkbox" value="{{ value }}"{% if checked %} checked{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error) }} />
|
|
162
170
|
<label for="{{ id }}">{{ label | safe }}</label>
|
|
163
171
|
</p>
|
|
164
|
-
{{ _below(hint, error, errorHtml) }}
|
|
172
|
+
{{ _below(id, hint, error, errorHtml) }}
|
|
173
|
+
{%- endmacro %}
|
|
174
|
+
|
|
175
|
+
{# The top of a refused form: what went wrong, and a link to every field that
|
|
176
|
+
needs putting right. It is an alert and takes focus when the page loads, so
|
|
177
|
+
a screen reader hears it at once and a keyboard starts from it, with no
|
|
178
|
+
script. It comes before the form, so its `autofocus` is the first on the
|
|
179
|
+
page and wins over any a field carries.
|
|
180
|
+
|
|
181
|
+
A form with one thing to say calls it bare, with the message as the heading.
|
|
182
|
+
A form with fields to fix calls it with a `problem` per field; one given no
|
|
183
|
+
message writes nothing, so a page lists every field it could fault:
|
|
184
|
+
|
|
185
|
+
{% call field.summary('Nothing was saved. Fix the fields marked below.') %}
|
|
186
|
+
{{ field.problem('settings-title', problems.title) }}
|
|
187
|
+
{% endcall %}
|
|
188
|
+
|
|
189
|
+
Only one form on a page is ever refused at a time, which is why the ids are
|
|
190
|
+
fixed. #}
|
|
191
|
+
{% macro summary(heading) %}
|
|
192
|
+
{% set problems = (caller() if caller else '') | trim %}
|
|
193
|
+
<div class="admin-error admin-error-summary" role="alert" tabindex="-1" autofocus aria-labelledby="admin-error-summary-heading">
|
|
194
|
+
<h2 id="admin-error-summary-heading">{{ heading }}</h2>
|
|
195
|
+
{% if problems %}
|
|
196
|
+
<ul>
|
|
197
|
+
{{ problems | safe }}
|
|
198
|
+
</ul>
|
|
199
|
+
{% endif %}
|
|
200
|
+
</div>
|
|
201
|
+
{%- endmacro %}
|
|
202
|
+
|
|
203
|
+
{# One field's line in the summary: its message, linking to the field. #}
|
|
204
|
+
{% macro problem(id, message) %}
|
|
205
|
+
{% if message %}
|
|
206
|
+
<li><a href="#{{ id }}">{{ message }}</a></li>
|
|
207
|
+
{% endif %}
|
|
165
208
|
{%- endmacro %}
|
package/admin/layouts/base.njk
CHANGED
|
@@ -17,9 +17,13 @@
|
|
|
17
17
|
<link rel="stylesheet" href="{{ assetPrefix }}admin.css" />
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
|
20
|
+
{# First on every screen, so a keyboard gets past the bar and the menu
|
|
21
|
+
in one key. It moves no focus of its own: a refused form's summary
|
|
22
|
+
still takes focus on load (TASK-142). #}
|
|
23
|
+
<a class="admin-skip-link" href="#main">Skip to main content</a>
|
|
20
24
|
{% block chrome %}{% endblock %}
|
|
21
25
|
{% block main %}
|
|
22
|
-
<main class="admin-plain">
|
|
26
|
+
<main id="main" class="admin-plain">
|
|
23
27
|
{% include "components/flash.njk" %}
|
|
24
28
|
{% block content %}{% endblock %}
|
|
25
29
|
</main>
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{# What every settings page is: a heading, one form that saves the fields that
|
|
2
2
|
page carries, and whatever panels sit under it. A page fills in the two
|
|
3
|
-
blocks
|
|
3
|
+
blocks, and names in `settingsProblems` each field a save can refuse, so
|
|
4
|
+
the summary at the top links to it; nothing about the chrome is written six
|
|
5
|
+
times. #}
|
|
4
6
|
{% extends "layouts/shell.njk" %}
|
|
7
|
+
{% import "components/fields.njk" as field %}
|
|
5
8
|
|
|
6
9
|
{% block title %}{{ settingsLabel }} settings{% endblock %}
|
|
7
10
|
|
|
@@ -9,7 +12,9 @@
|
|
|
9
12
|
<h1>{{ settingsLabel }}</h1>
|
|
10
13
|
|
|
11
14
|
{% if hasProblems %}
|
|
12
|
-
|
|
15
|
+
{% call field.summary('Nothing was saved. Fix the fields marked below and try again.') %}
|
|
16
|
+
{% block settingsProblems %}{% endblock %}
|
|
17
|
+
{% endcall %}
|
|
13
18
|
{% endif %}
|
|
14
19
|
|
|
15
20
|
<form class="admin-settings" method="post" action="{{ settingsUrl }}">
|
package/admin/layouts/shell.njk
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<p>Type your username or your email address, and a link to set a new password is sent to the address on your account.</p>
|
|
31
31
|
|
|
32
32
|
{% if error %}
|
|
33
|
-
|
|
33
|
+
{{ field.summary(error) }}
|
|
34
34
|
{% endif %}
|
|
35
35
|
|
|
36
36
|
<form method="post" action="{{ forgotUrl }}">
|
|
@@ -17,14 +17,16 @@
|
|
|
17
17
|
<p><a href="{{ forgotUrl }}">Ask for another one</a></p>
|
|
18
18
|
{% else %}
|
|
19
19
|
{% if problem %}
|
|
20
|
-
|
|
20
|
+
{% call field.summary('The password was not set.') %}
|
|
21
|
+
{{ field.problem('password', problem) }}
|
|
22
|
+
{% endcall %}
|
|
21
23
|
{% endif %}
|
|
22
24
|
|
|
23
25
|
<form method="post" action="{{ resetUrl }}">
|
|
24
26
|
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
25
27
|
<input type="hidden" name="{{ fields.token }}" value="{{ token }}" />
|
|
26
28
|
|
|
27
|
-
{{ field.password('password', fields.password, 'New password',
|
|
29
|
+
{{ field.password('password', fields.password, 'New password', error=problem,
|
|
28
30
|
autocomplete='new-password', required=true, autofocus=true) }}
|
|
29
31
|
|
|
30
32
|
{{ field.password('password_confirmation', fields.passwordConfirmation,
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
{% if actor.followers.length %}
|
|
42
42
|
<table class="admin-list">
|
|
43
|
+
<caption>Followers of {{ actor.handle }}</caption>
|
|
43
44
|
<thead>
|
|
44
45
|
<tr>
|
|
45
46
|
<th scope="col">Follower</th>
|
|
@@ -80,6 +81,7 @@
|
|
|
80
81
|
<h2>Relays</h2>
|
|
81
82
|
{% if relays.length %}
|
|
82
83
|
<table class="admin-list">
|
|
84
|
+
<caption class="admin-visually-hidden">Relays</caption>
|
|
83
85
|
<thead>
|
|
84
86
|
<tr>
|
|
85
87
|
<th scope="col">Relay</th>
|
|
@@ -174,6 +176,7 @@
|
|
|
174
176
|
<h2>Delivery</h2>
|
|
175
177
|
{% if posts.length %}
|
|
176
178
|
<table class="admin-list">
|
|
179
|
+
<caption class="admin-visually-hidden">Delivery</caption>
|
|
177
180
|
<thead>
|
|
178
181
|
<tr>
|
|
179
182
|
<th scope="col">Post</th>
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
{% extends "layouts/settings-page.njk" %}
|
|
8
8
|
{% import "components/fields.njk" as field %}
|
|
9
9
|
|
|
10
|
+
{% block settingsProblems %}
|
|
11
|
+
{{ field.problem('settings-relays', problems.relays) }}
|
|
12
|
+
{% endblock %}
|
|
13
|
+
|
|
10
14
|
{% block settingsFields %}
|
|
11
15
|
{% set relaysHint %}
|
|
12
16
|
One relay inbox per line, such as
|
|
@@ -42,6 +46,7 @@
|
|
|
42
46
|
|
|
43
47
|
{% if wordpressRoutes.length %}
|
|
44
48
|
<table class="admin-table">
|
|
49
|
+
<caption class="admin-visually-hidden">WordPress paths</caption>
|
|
45
50
|
<thead>
|
|
46
51
|
<tr><th scope="col">Path</th><th scope="col">For</th><th scope="col">Last asked for</th></tr>
|
|
47
52
|
</thead>
|
|
@@ -60,9 +60,11 @@
|
|
|
60
60
|
<p class="admin-empty">Nothing has been uploaded yet.</p>
|
|
61
61
|
{% else %}
|
|
62
62
|
<table class="admin-list admin-media">
|
|
63
|
+
<caption class="admin-visually-hidden">{{ heading }}</caption>
|
|
63
64
|
<thead>
|
|
64
65
|
<tr>
|
|
65
66
|
<th scope="col">File</th>
|
|
67
|
+
<th scope="col">Alt text</th>
|
|
66
68
|
<th scope="col">Uploaded</th>
|
|
67
69
|
<th scope="col">Size</th>
|
|
68
70
|
<th scope="col">Link</th>
|
|
@@ -83,6 +85,43 @@
|
|
|
83
85
|
{% endif %}
|
|
84
86
|
<a href="{{ file.url }}">{{ file.path }}</a>
|
|
85
87
|
</td>
|
|
88
|
+
<td class="admin-media-alt">
|
|
89
|
+
{% if file.image %}
|
|
90
|
+
<form method="post" action="{{ altUrl }}">
|
|
91
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
92
|
+
<input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
|
|
93
|
+
<input type="hidden" name="{{ fields.page }}" value="{{ page }}" />
|
|
94
|
+
<label class="admin-visually-hidden" for="media-alt-{{ loop.index }}">
|
|
95
|
+
Alt text for {{ file.name }}
|
|
96
|
+
</label>
|
|
97
|
+
<input
|
|
98
|
+
id="media-alt-{{ loop.index }}"
|
|
99
|
+
name="{{ fields.alt }}"
|
|
100
|
+
type="text"
|
|
101
|
+
value="{% if file.alt and file.alt.kind == 'described' %}{{ file.alt.text }}{% endif %}"
|
|
102
|
+
aria-describedby="media-alt-state-{{ loop.index }}"
|
|
103
|
+
/>
|
|
104
|
+
<span class="admin-check">
|
|
105
|
+
<input
|
|
106
|
+
id="media-decorative-{{ loop.index }}"
|
|
107
|
+
name="{{ fields.decorative }}"
|
|
108
|
+
type="checkbox"
|
|
109
|
+
value="1"
|
|
110
|
+
{% if file.alt and file.alt.kind == 'decorative' %}checked{% endif %}
|
|
111
|
+
/>
|
|
112
|
+
<label for="media-decorative-{{ loop.index }}">Decorative</label>
|
|
113
|
+
</span>
|
|
114
|
+
<button type="submit">Save</button>
|
|
115
|
+
<span id="media-alt-state-{{ loop.index }}" class="admin-status{% if not file.alt %} admin-status-missing{% endif %}">
|
|
116
|
+
{%- if not file.alt %}Missing: describe it, or mark it decorative
|
|
117
|
+
{%- elif file.alt.kind == 'decorative' %}Decorative: inserted with an empty alt
|
|
118
|
+
{%- else %}Offered when it is inserted{% endif -%}
|
|
119
|
+
</span>
|
|
120
|
+
</form>
|
|
121
|
+
{% else %}
|
|
122
|
+
<span class="admin-status">Not an image</span>
|
|
123
|
+
{% endif %}
|
|
124
|
+
</td>
|
|
86
125
|
<td>{{ file.modified | date('html') }}</td>
|
|
87
126
|
<td>{{ file.bytes }}</td>
|
|
88
127
|
<td class="admin-media-links">
|
|
@@ -60,7 +60,12 @@
|
|
|
60
60
|
<h1>{{ heading }}</h1>
|
|
61
61
|
|
|
62
62
|
{% if hasProblems %}
|
|
63
|
-
|
|
63
|
+
{% call field.summary('Nothing was saved. Fix the box marked below and try again.') %}
|
|
64
|
+
{% for menu in areas.concat(others) %}
|
|
65
|
+
{{ field.problem('menu-' + menu.name, menu.problem) }}
|
|
66
|
+
{% endfor %}
|
|
67
|
+
{{ field.problem('menu-name', nameProblem) }}
|
|
68
|
+
{% endcall %}
|
|
64
69
|
{% endif %}
|
|
65
70
|
|
|
66
71
|
<h2>Where {{ themeName }} puts a menu</h2>
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
{%- endif -%}
|
|
14
14
|
{% endmacro %}
|
|
15
15
|
|
|
16
|
+
{% block settingsProblems %}
|
|
17
|
+
{{ field.problem('settings-comments-close', problems.commentsCloseAfterDays) }}
|
|
18
|
+
{{ field.problem('settings-comment-email-retention', problems.commentEmailRetentionDays) }}
|
|
19
|
+
{{ field.problem('settings-address-hash-retention', problems.addressHashRetentionDays) }}
|
|
20
|
+
{{ field.problem('settings-contact-message-retention', problems.contactMessageRetentionDays) }}
|
|
21
|
+
{% endblock %}
|
|
22
|
+
|
|
16
23
|
{% block settingsFields %}
|
|
17
24
|
<h2>Comments</h2>
|
|
18
25
|
|
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
{% extends "layouts/settings-page.njk" %}
|
|
4
4
|
{% import "components/fields.njk" as field %}
|
|
5
5
|
|
|
6
|
+
{% block settingsProblems %}
|
|
7
|
+
{{ field.problem('settings-mail-provider', problems.mailProvider) }}
|
|
8
|
+
{{ field.problem('settings-mail-from-address', problems.mailFromAddress) }}
|
|
9
|
+
{{ field.problem('settings-mail-reply-to', problems.mailReplyTo) }}
|
|
10
|
+
{{ field.problem('settings-contact-email', problems.contactEmail) }}
|
|
11
|
+
{{ field.problem('settings-security-contacts', problems.securityContacts) }}
|
|
12
|
+
{{ field.problem('settings-security-policy', problems.securityPolicy) }}
|
|
13
|
+
{{ field.problem('settings-security-languages', problems.securityLanguages) }}
|
|
14
|
+
{% endblock %}
|
|
15
|
+
|
|
6
16
|
{% block settingsFields %}
|
|
7
17
|
{% set providerHint %}
|
|
8
18
|
The key or password each one needs is a credential and is saved below,
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
{% extends "layouts/settings-page.njk" %}
|
|
3
3
|
{% import "components/fields.njk" as field %}
|
|
4
4
|
|
|
5
|
+
{% block settingsProblems %}
|
|
6
|
+
{{ field.problem('settings-title', problems.title) }}
|
|
7
|
+
{{ field.problem('settings-base-url', problems.baseUrl) }}
|
|
8
|
+
{{ field.problem('settings-timezone', problems.timezone) }}
|
|
9
|
+
{{ field.problem('settings-language', problems.language) }}
|
|
10
|
+
{% endblock %}
|
|
11
|
+
|
|
5
12
|
{% block settingsFields %}
|
|
6
13
|
{{ field.text('settings-title', fields.title, 'Title', form.title,
|
|
7
14
|
error=problems.title, required=true, autofocus=true) }}
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
{% extends "layouts/settings-page.njk" %}
|
|
4
4
|
{% import "components/fields.njk" as field %}
|
|
5
5
|
|
|
6
|
+
{% block settingsProblems %}
|
|
7
|
+
{{ field.problem('settings-tag-base', problems.tagBase) }}
|
|
8
|
+
{{ field.problem('settings-category-base', problems.categoryBase) }}
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
6
11
|
{% block settingsFields %}
|
|
7
12
|
{% set tagBaseHint %}The tag archives live at <code>/{{ form.tagBase }}/{tag}/</code>. One path segment, no slashes.{% endset %}
|
|
8
13
|
{{ field.text('settings-tag-base', fields.tagBase, 'Tag base', form.tagBase,
|
|
@@ -22,6 +27,7 @@
|
|
|
22
27
|
|
|
23
28
|
{% if taxonomyRedirects.length %}
|
|
24
29
|
<table class="admin-list">
|
|
30
|
+
<caption class="admin-visually-hidden">Archive redirects</caption>
|
|
25
31
|
<thead>
|
|
26
32
|
<tr><th scope="col">Taxonomy</th><th scope="col">Was</th><th scope="col">Is now</th></tr>
|
|
27
33
|
</thead>
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
{% extends "layouts/settings-page.njk" %}
|
|
5
5
|
{% import "components/fields.njk" as field %}
|
|
6
6
|
|
|
7
|
+
{% block settingsProblems %}
|
|
8
|
+
{{ field.problem('settings-homepage', problems.homepage) }}
|
|
9
|
+
{{ field.problem('settings-posts-page', problems.postsPage) }}
|
|
10
|
+
{{ field.problem('settings-posts-per-page', problems.postsPerPage) }}
|
|
11
|
+
{{ field.problem('settings-notify-server', problems.notifyServer) }}
|
|
12
|
+
{% endblock %}
|
|
13
|
+
|
|
7
14
|
{% block settingsFields %}
|
|
8
15
|
{# WordPress's "Your homepage displays": the empty option is Your latest
|
|
9
16
|
posts, so the choice and the page it needs are one control rather than a
|
|
@@ -49,6 +49,11 @@
|
|
|
49
49
|
{% endif %}
|
|
50
50
|
</form>
|
|
51
51
|
{% else %}
|
|
52
|
+
{% if problem %}
|
|
53
|
+
{% call field.summary('Nothing was looked up.') %}
|
|
54
|
+
{{ field.problem('personal-data-email', problem) }}
|
|
55
|
+
{% endcall %}
|
|
56
|
+
{% endif %}
|
|
52
57
|
<form class="admin-settings" method="post" action="{{ actionUrl }}">
|
|
53
58
|
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
54
59
|
{{ field.text('personal-data-email', fields.email, 'Email address', email,
|
|
@@ -80,10 +80,9 @@
|
|
|
80
80
|
</p>
|
|
81
81
|
|
|
82
82
|
{% if profileProblems.links %}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</p>
|
|
83
|
+
{% call field.summary('Nothing was saved. Fix the Links box below and save again; every other box here still holds what you typed.') %}
|
|
84
|
+
{{ field.problem('profile-links', profileProblems.links) }}
|
|
85
|
+
{% endcall %}
|
|
87
86
|
{% endif %}
|
|
88
87
|
|
|
89
88
|
{{ field.text('profile-display-name', fields.displayName, 'Display name',
|
|
@@ -219,7 +218,10 @@
|
|
|
219
218
|
<h2>Change your password</h2>
|
|
220
219
|
|
|
221
220
|
{% if passwordProblems.currentPassword or passwordProblems.newPassword %}
|
|
222
|
-
|
|
221
|
+
{% call field.summary('Your password was not changed. Fix the fields marked below and try again.') %}
|
|
222
|
+
{{ field.problem('current-password', passwordProblems.currentPassword) }}
|
|
223
|
+
{{ field.problem('new-password', passwordProblems.newPassword) }}
|
|
224
|
+
{% endcall %}
|
|
223
225
|
{% endif %}
|
|
224
226
|
|
|
225
227
|
{{ field.password('current-password', fields.currentPassword, 'Current password',
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
17
17
|
|
|
18
18
|
{% if addProblems.username or addProblems.password or addProblems.email %}
|
|
19
|
-
|
|
19
|
+
{% call field.summary('Nobody was added. Fix the fields marked below and try again.') %}
|
|
20
|
+
{{ field.problem('add-username', addProblems.username) }}
|
|
21
|
+
{{ field.problem('add-email', addProblems.email) }}
|
|
22
|
+
{{ field.problem('add-password', addProblems.password) }}
|
|
23
|
+
{% endcall %}
|
|
20
24
|
{% endif %}
|
|
21
25
|
|
|
22
26
|
{{ field.text('add-username', fields.username, 'Username', addForm.username,
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
--admin-bar: #1d2327;
|
|
16
16
|
--admin-nav: #2c3338;
|
|
17
17
|
--admin-nav-text: #f0f0f1;
|
|
18
|
+
--admin-focus-on-dark: #fff;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
.admin-bar,
|
|
@@ -53,6 +54,15 @@
|
|
|
53
54
|
color: #72aee6;
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
/* The ring where the keyboard is, white on the dark bar. Here rather than in
|
|
58
|
+
admin.css because on the public site this is the only stylesheet the bar
|
|
59
|
+
has. */
|
|
60
|
+
|
|
61
|
+
.admin-bar :focus-visible {
|
|
62
|
+
outline: 2px solid var(--admin-focus-on-dark);
|
|
63
|
+
outline-offset: 2px;
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
.admin-bar-site {
|
|
57
67
|
font-weight: 600;
|
|
58
68
|
}
|
|
@@ -128,6 +138,12 @@
|
|
|
128
138
|
cursor: pointer;
|
|
129
139
|
}
|
|
130
140
|
|
|
141
|
+
/* A menu entry fills the menu's width, so its ring is drawn inside it. */
|
|
142
|
+
|
|
143
|
+
.admin-bar .admin-account-menu :focus-visible {
|
|
144
|
+
outline-offset: -2px;
|
|
145
|
+
}
|
|
146
|
+
|
|
131
147
|
.admin-account-menu a:hover,
|
|
132
148
|
.admin-account-menu a:focus-visible,
|
|
133
149
|
.admin-account-menu button[type='submit']:hover,
|