@geekity/cms 0.34.0 → 0.35.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/admin/pages/documents/chooser.njk +30 -0
- package/admin/pages/documents/editor.njk +394 -164
- package/admin/pages/media/file.njk +142 -0
- package/admin/pages/media/library.njk +103 -198
- package/admin/pages/settings/general.njk +48 -1
- package/admin/pages/settings/privacy.njk +1 -1
- package/admin/pages/tools/maintenance.njk +48 -0
- package/admin/src/admin.css +1 -0
- package/admin/static/admin.css +728 -65
- package/admin/static/copy.js +30 -23
- package/admin/static/item-lookup.js +179 -0
- package/admin/static/media.js +82 -0
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +141 -22
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/editor-actions.d.ts.map +1 -1
- package/dist/admin/editor-actions.js +2 -3
- package/dist/admin/editor-actions.js.map +1 -1
- package/dist/admin/editor-fields.d.ts +20 -8
- package/dist/admin/editor-fields.d.ts.map +1 -1
- package/dist/admin/editor-fields.js +35 -12
- package/dist/admin/editor-fields.js.map +1 -1
- package/dist/admin/editor-kinds.d.ts +30 -0
- package/dist/admin/editor-kinds.d.ts.map +1 -0
- package/dist/admin/editor-kinds.js +136 -0
- package/dist/admin/editor-kinds.js.map +1 -0
- package/dist/admin/editor-layout.d.ts +10 -1
- package/dist/admin/editor-layout.d.ts.map +1 -1
- package/dist/admin/editor-layout.js +38 -31
- package/dist/admin/editor-layout.js.map +1 -1
- package/dist/admin/federation.d.ts +1 -1
- package/dist/admin/federation.d.ts.map +1 -1
- package/dist/admin/federation.js +2 -1
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/formatting.d.ts +2 -0
- package/dist/admin/formatting.d.ts.map +1 -1
- package/dist/admin/formatting.js +8 -0
- package/dist/admin/formatting.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/item-lookup.d.ts +16 -0
- package/dist/admin/item-lookup.d.ts.map +1 -0
- package/dist/admin/item-lookup.js +192 -0
- package/dist/admin/item-lookup.js.map +1 -0
- package/dist/admin/maintenance.d.ts +2 -0
- package/dist/admin/maintenance.d.ts.map +1 -1
- package/dist/admin/maintenance.js +14 -0
- package/dist/admin/maintenance.js.map +1 -1
- package/dist/admin/media.d.ts +43 -29
- package/dist/admin/media.d.ts.map +1 -1
- package/dist/admin/media.js +221 -54
- package/dist/admin/media.js.map +1 -1
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +9 -12
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +3 -0
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/settings-general.d.ts +6 -0
- package/dist/admin/settings-general.d.ts.map +1 -1
- package/dist/admin/settings-general.js +46 -1
- package/dist/admin/settings-general.js.map +1 -1
- package/dist/admin/templates.d.ts +2 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +4 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/cms/app.d.ts.map +1 -1
- package/dist/cms/app.js +1 -0
- package/dist/cms/app.js.map +1 -1
- package/dist/cms/services.d.ts +2 -0
- package/dist/cms/services.d.ts.map +1 -1
- package/dist/cms/services.js +13 -0
- package/dist/cms/services.js.map +1 -1
- package/dist/content/document-form.d.ts +5 -13
- package/dist/content/document-form.d.ts.map +1 -1
- package/dist/content/document-form.js +2 -3
- package/dist/content/document-form.js.map +1 -1
- package/dist/content/excerpt.d.ts +1 -3
- package/dist/content/excerpt.d.ts.map +1 -1
- package/dist/content/excerpt.js +2 -7
- package/dist/content/excerpt.js.map +1 -1
- package/dist/content/post-fields.d.ts +1 -1
- package/dist/content/post-fields.d.ts.map +1 -1
- package/dist/content/post-fields.js +30 -35
- package/dist/content/post-fields.js.map +1 -1
- package/dist/content/post-type.d.ts +2 -3
- package/dist/content/post-type.d.ts.map +1 -1
- package/dist/content/post-type.js +8 -9
- package/dist/content/post-type.js.map +1 -1
- package/dist/content/review-form.d.ts +49 -0
- package/dist/content/review-form.d.ts.map +1 -0
- package/dist/content/review-form.js +282 -0
- package/dist/content/review-form.js.map +1 -0
- package/dist/content/review.d.ts +57 -0
- package/dist/content/review.d.ts.map +1 -0
- package/dist/content/review.js +264 -0
- package/dist/content/review.js.map +1 -0
- package/dist/content/write.d.ts.map +1 -1
- package/dist/content/write.js +6 -9
- package/dist/content/write.js.map +1 -1
- package/dist/exports.d.ts +1 -1
- package/dist/exports.d.ts.map +1 -1
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +27 -29
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/federation.d.ts +3 -0
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js +5 -1
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/refusals.d.ts +18 -0
- package/dist/federation/refusals.d.ts.map +1 -0
- package/dist/federation/refusals.js +84 -0
- package/dist/federation/refusals.js.map +1 -0
- package/dist/images/variants.d.ts +1 -0
- package/dist/images/variants.d.ts.map +1 -1
- package/dist/images/variants.js +10 -0
- package/dist/images/variants.js.map +1 -1
- package/dist/item-lookup/item-id.d.ts +33 -0
- package/dist/item-lookup/item-id.d.ts.map +1 -0
- package/dist/item-lookup/item-id.js +71 -0
- package/dist/item-lookup/item-id.js.map +1 -0
- package/dist/item-lookup/lookup.d.ts +58 -0
- package/dist/item-lookup/lookup.d.ts.map +1 -0
- package/dist/item-lookup/lookup.js +532 -0
- package/dist/item-lookup/lookup.js.map +1 -0
- package/dist/micropub/create.d.ts +1 -1
- package/dist/micropub/create.d.ts.map +1 -1
- package/dist/micropub/create.js +4 -2
- package/dist/micropub/create.js.map +1 -1
- package/dist/micropub/endpoint.d.ts.map +1 -1
- package/dist/micropub/endpoint.js +3 -10
- package/dist/micropub/endpoint.js.map +1 -1
- package/dist/micropub/update.d.ts.map +1 -1
- package/dist/micropub/update.js +12 -6
- package/dist/micropub/update.js.map +1 -1
- package/dist/plugin.d.ts +26 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/settings/names.d.ts +1 -1
- package/dist/settings/names.d.ts.map +1 -1
- package/dist/settings/names.js +8 -1
- package/dist/settings/names.js.map +1 -1
- package/dist/settings/registry.d.ts.map +1 -1
- package/dist/settings/registry.js +2 -1
- package/dist/settings/registry.js.map +1 -1
- package/dist/settings/tmdb.d.ts +5 -0
- package/dist/settings/tmdb.d.ts.map +1 -0
- package/dist/settings/tmdb.js +22 -0
- package/dist/settings/tmdb.js.map +1 -0
- package/dist/store/profiles.d.ts +3 -2
- package/dist/store/profiles.d.ts.map +1 -1
- package/dist/store/profiles.js +2 -4
- package/dist/store/profiles.js.map +1 -1
- package/dist/web/context.d.ts +9 -21
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +20 -20
- package/dist/web/context.js.map +1 -1
- package/dist/web/feed-item.d.ts +6 -6
- package/dist/web/feed-item.d.ts.map +1 -1
- package/dist/web/feed-item.js +7 -3
- package/dist/web/feed-item.js.map +1 -1
- package/dist/web/plugin-federation.d.ts +2 -0
- package/dist/web/plugin-federation.d.ts.map +1 -1
- package/dist/web/plugin-federation.js +3 -0
- package/dist/web/plugin-federation.js.map +1 -1
- package/dist/webmention/cited-picture.d.ts +1 -0
- package/dist/webmention/cited-picture.d.ts.map +1 -1
- package/dist/webmention/cited-picture.js +7 -4
- package/dist/webmention/cited-picture.js.map +1 -1
- package/dist/webmention/fetch-public.d.ts +3 -0
- package/dist/webmention/fetch-public.d.ts.map +1 -1
- package/dist/webmention/fetch-public.js +1 -1
- package/dist/webmention/fetch-public.js.map +1 -1
- package/dist/webmention/reply-context/json-ld.d.ts +8 -0
- package/dist/webmention/reply-context/json-ld.d.ts.map +1 -1
- package/dist/webmention/reply-context/json-ld.js +21 -0
- package/dist/webmention/reply-context/json-ld.js.map +1 -1
- package/dist/webmention/service.d.ts.map +1 -1
- package/dist/webmention/service.js +3 -5
- package/dist/webmention/service.js.map +1 -1
- package/package.json +1 -1
- package/themes/default/layouts/post.njk +6 -5
- package/themes/default/partials/post-list.njk +15 -20
- package/themes/default/partials/review.njk +1 -0
- package/themes/default/src/style.css +24 -4
- package/themes/default/static/style.css +33 -5
- package/themes/default/partials/read.njk +0 -5
package/admin/static/copy.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* The
|
|
1
|
+
/* The copy buttons beside readonly fields, and nothing else.
|
|
2
2
|
|
|
3
3
|
Every URL and every ready-made Markdown line is already a readonly text
|
|
4
4
|
input, which selects and copies like any other text, so the screen is
|
|
@@ -17,30 +17,37 @@
|
|
|
17
17
|
button appears. */
|
|
18
18
|
if (!navigator.clipboard) return;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
button.hidden = false;
|
|
28
|
-
button.addEventListener('click', function () {
|
|
29
|
-
navigator.clipboard.writeText(field.value).then(
|
|
30
|
-
function () {
|
|
31
|
-
said(button, 'Copied');
|
|
32
|
-
},
|
|
33
|
-
function () {
|
|
34
|
-
/* Refused, usually because the document was not focused. Selecting
|
|
35
|
-
the text is then the useful thing to do: the keyboard shortcut
|
|
36
|
-
everybody already knows takes it from there. */
|
|
37
|
-
field.select();
|
|
38
|
-
said(button, 'Press ⌘C');
|
|
39
|
-
},
|
|
40
|
-
);
|
|
41
|
-
});
|
|
20
|
+
enhance(document);
|
|
21
|
+
|
|
22
|
+
/* media.js shows a details screen in a dialog after this has run, and
|
|
23
|
+
announces the content it inserted. */
|
|
24
|
+
document.addEventListener('geekity:inserted', function (event) {
|
|
25
|
+
enhance(event.target);
|
|
42
26
|
});
|
|
43
27
|
|
|
28
|
+
function enhance(root) {
|
|
29
|
+
Array.prototype.forEach.call(root.querySelectorAll('button[data-copy]'), function (button) {
|
|
30
|
+
var field = document.getElementById(button.getAttribute('data-copy'));
|
|
31
|
+
if (!field) return;
|
|
32
|
+
|
|
33
|
+
button.hidden = false;
|
|
34
|
+
button.addEventListener('click', function () {
|
|
35
|
+
navigator.clipboard.writeText(field.value).then(
|
|
36
|
+
function () {
|
|
37
|
+
said(button, 'Copied');
|
|
38
|
+
},
|
|
39
|
+
function () {
|
|
40
|
+
/* Refused, usually because the document was not focused. Selecting
|
|
41
|
+
the text is then the useful thing to do: the keyboard shortcut
|
|
42
|
+
everybody already knows takes it from there. */
|
|
43
|
+
field.select();
|
|
44
|
+
said(button, 'Press ⌘C');
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
/* Say something on the button for a moment, then put its label back. */
|
|
45
52
|
function said(button, message) {
|
|
46
53
|
if (button.dataset.label === undefined) button.dataset.label = button.textContent;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var press = document.querySelector('button[data-item-lookup]');
|
|
3
|
+
var status = document.getElementById('editor-item-lookup-status');
|
|
4
|
+
if (!press || !status || !press.form) return;
|
|
5
|
+
var form = press.form;
|
|
6
|
+
var url = press.getAttribute('data-item-lookup');
|
|
7
|
+
|
|
8
|
+
function plainForm() {
|
|
9
|
+
var fields = new URLSearchParams();
|
|
10
|
+
new FormData(form).forEach(function (value, name) {
|
|
11
|
+
if (typeof value === 'string') fields.append(name, value);
|
|
12
|
+
});
|
|
13
|
+
return fields;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function tell(line, message, failed) {
|
|
17
|
+
line.textContent = message;
|
|
18
|
+
line.classList.toggle('text-error', failed === true);
|
|
19
|
+
line.classList.toggle('text-base-content', failed !== true);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function say(message, failed) {
|
|
23
|
+
tell(status, message, failed);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function answerOf(response) {
|
|
27
|
+
var type = response.headers.get('content-type') || '';
|
|
28
|
+
if (type.indexOf('application/json') === -1) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
response.status === 403
|
|
31
|
+
? 'The site refused the request because this page is out of date. Copy your changes somewhere safe, then reload the page.'
|
|
32
|
+
: response.redirected
|
|
33
|
+
? 'You are signed out. Sign in again in another tab, then try again.'
|
|
34
|
+
: 'The site answered ' + response.status + ' instead of JSON.',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return response.json();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function lookUp(pick) {
|
|
41
|
+
var fields = plainForm();
|
|
42
|
+
if (pick) fields.set('item-pick', pick);
|
|
43
|
+
press.disabled = true;
|
|
44
|
+
say('Looking it up…');
|
|
45
|
+
|
|
46
|
+
return fetch(url, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
body: fields,
|
|
49
|
+
credentials: 'same-origin',
|
|
50
|
+
headers: { accept: 'application/json' },
|
|
51
|
+
})
|
|
52
|
+
.then(answerOf)
|
|
53
|
+
.then(function (answer) {
|
|
54
|
+
var filled = answer.fields || {};
|
|
55
|
+
Object.keys(filled).forEach(function (name) {
|
|
56
|
+
var box = form.elements.namedItem(name);
|
|
57
|
+
if (box && box.value === '') box.value = String(filled[name]);
|
|
58
|
+
});
|
|
59
|
+
var preview = document.getElementById('editor-item-photo-preview');
|
|
60
|
+
if (preview && answer.thumbnail && filled['item-photo']) {
|
|
61
|
+
preview.src = answer.thumbnail;
|
|
62
|
+
preview.hidden = false;
|
|
63
|
+
}
|
|
64
|
+
say(String(answer.message || ''), !answer.ok);
|
|
65
|
+
})
|
|
66
|
+
.catch(function (error) {
|
|
67
|
+
say(error.message || 'The lookup failed.', true);
|
|
68
|
+
})
|
|
69
|
+
.then(function () {
|
|
70
|
+
press.disabled = false;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
press.addEventListener('click', function (event) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
lookUp('');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
var find = document.getElementById('editor-item-find');
|
|
80
|
+
var query = document.getElementById('editor-item-find-query');
|
|
81
|
+
var search = document.getElementById('editor-item-find-button');
|
|
82
|
+
var said = document.getElementById('editor-item-find-status');
|
|
83
|
+
var list = document.getElementById('editor-item-find-matches');
|
|
84
|
+
var credit = document.getElementById('editor-item-find-credit');
|
|
85
|
+
var shape = document.getElementById('editor-item-find-match');
|
|
86
|
+
if (!find || !query || !search || !said || !list || !credit || !shape) return;
|
|
87
|
+
find.hidden = false;
|
|
88
|
+
var searching = false;
|
|
89
|
+
|
|
90
|
+
function chosenKind() {
|
|
91
|
+
var ticked = form.querySelector('input[name="item-kind"]:checked');
|
|
92
|
+
return ticked ? ticked.value : '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function clear() {
|
|
96
|
+
Array.prototype.slice.call(list.querySelectorAll('li[data-match]')).forEach(function (row) {
|
|
97
|
+
row.remove();
|
|
98
|
+
});
|
|
99
|
+
credit.hidden = true;
|
|
100
|
+
list.hidden = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function show(matches) {
|
|
104
|
+
clear();
|
|
105
|
+
matches.forEach(function (match) {
|
|
106
|
+
var row = shape.content.firstElementChild.cloneNode(true);
|
|
107
|
+
row.setAttribute('data-match', '');
|
|
108
|
+
var picture = row.querySelector('img');
|
|
109
|
+
if (match.thumbnail) {
|
|
110
|
+
picture.src = match.thumbnail;
|
|
111
|
+
picture.hidden = false;
|
|
112
|
+
row.querySelector('[data-no-picture]').hidden = true;
|
|
113
|
+
}
|
|
114
|
+
row.querySelector('[data-name]').textContent = match.name;
|
|
115
|
+
row.querySelector('[data-detail]').textContent = match.detail;
|
|
116
|
+
row.querySelector('[data-source]').textContent = match.source;
|
|
117
|
+
row.querySelector('button').addEventListener('click', function () {
|
|
118
|
+
clear();
|
|
119
|
+
tell(said, '');
|
|
120
|
+
lookUp(match.id);
|
|
121
|
+
});
|
|
122
|
+
list.insertBefore(row, credit);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function run() {
|
|
127
|
+
if (searching) return;
|
|
128
|
+
searching = true;
|
|
129
|
+
search.disabled = true;
|
|
130
|
+
tell(said, 'Searching…');
|
|
131
|
+
var address =
|
|
132
|
+
find.getAttribute('data-item-search') +
|
|
133
|
+
'?' +
|
|
134
|
+
new URLSearchParams({ kind: chosenKind(), q: query.value }).toString();
|
|
135
|
+
|
|
136
|
+
fetch(address, { credentials: 'same-origin', headers: { accept: 'application/json' } })
|
|
137
|
+
.then(answerOf)
|
|
138
|
+
.then(function (answer) {
|
|
139
|
+
var matches = answer.matches || [];
|
|
140
|
+
show(matches);
|
|
141
|
+
if (answer.credit) {
|
|
142
|
+
credit.textContent = answer.credit;
|
|
143
|
+
credit.hidden = false;
|
|
144
|
+
}
|
|
145
|
+
list.hidden = !answer.ok;
|
|
146
|
+
tell(
|
|
147
|
+
said,
|
|
148
|
+
answer.ok
|
|
149
|
+
? matches.length === 0
|
|
150
|
+
? 'Nothing matches “' + query.value.trim() + '”.'
|
|
151
|
+
: matches.length === 1
|
|
152
|
+
? 'One match. Pick it to fill in the item.'
|
|
153
|
+
: matches.length + ' matches. Pick one to fill in the item.'
|
|
154
|
+
: String(answer.message || ''),
|
|
155
|
+
!answer.ok,
|
|
156
|
+
);
|
|
157
|
+
})
|
|
158
|
+
.catch(function (error) {
|
|
159
|
+
clear();
|
|
160
|
+
tell(said, error.message || 'The search failed.', true);
|
|
161
|
+
})
|
|
162
|
+
.then(function () {
|
|
163
|
+
searching = false;
|
|
164
|
+
search.disabled = false;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
search.addEventListener('click', run);
|
|
169
|
+
query.addEventListener('keydown', function (event) {
|
|
170
|
+
if (event.key !== 'Enter') return;
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
run();
|
|
173
|
+
});
|
|
174
|
+
form.addEventListener('change', function (event) {
|
|
175
|
+
if (event.target.name !== 'item-kind') return;
|
|
176
|
+
clear();
|
|
177
|
+
tell(said, '');
|
|
178
|
+
});
|
|
179
|
+
})();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* The media library's details dialog (TASK-429).
|
|
2
|
+
|
|
3
|
+
Every tile is already a link to its file's details screen, so the library
|
|
4
|
+
is complete before this file loads. This opens that screen in a dialog
|
|
5
|
+
instead: it fetches the page the link names, takes its
|
|
6
|
+
[data-media-details], and shows it over the grid. Previous and Next inside
|
|
7
|
+
the dialog, and the left and right arrow keys, fetch the neighbouring file
|
|
8
|
+
the same way. Anything that goes wrong falls back to following the link.
|
|
9
|
+
|
|
10
|
+
Forms inside the dialog post as they would on the details screen, which is
|
|
11
|
+
where their answers land.
|
|
12
|
+
|
|
13
|
+
Plain ES5-shaped browser JavaScript, like copy.js: it is served as it is
|
|
14
|
+
written. */
|
|
15
|
+
(function () {
|
|
16
|
+
var tiles = document.querySelectorAll('a[data-media-tile]');
|
|
17
|
+
if (!tiles.length || !window.fetch) return;
|
|
18
|
+
|
|
19
|
+
var dialog = document.createElement('dialog');
|
|
20
|
+
dialog.className = 'modal';
|
|
21
|
+
dialog.setAttribute('aria-label', 'File details');
|
|
22
|
+
dialog.innerHTML =
|
|
23
|
+
'<div class="modal-box max-w-6xl">' +
|
|
24
|
+
'<form method="dialog" class="mb-2 text-right"><button class="btn btn-sm btn-ghost">Close</button></form>' +
|
|
25
|
+
'<div data-media-dialog-body></div>' +
|
|
26
|
+
'</div>' +
|
|
27
|
+
'<form method="dialog" class="modal-backdrop"><button>Close</button></form>';
|
|
28
|
+
document.body.appendChild(dialog);
|
|
29
|
+
dialog.tabIndex = -1;
|
|
30
|
+
var body = dialog.querySelector('[data-media-dialog-body]');
|
|
31
|
+
|
|
32
|
+
Array.prototype.forEach.call(tiles, function (tile) {
|
|
33
|
+
tile.addEventListener('click', function (event) {
|
|
34
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
show(tile.href);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
body.addEventListener('click', function (event) {
|
|
43
|
+
var link = event.target.closest('a[data-media-step], a[data-media-library]');
|
|
44
|
+
if (!link) return;
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
if (link.hasAttribute('data-media-library')) dialog.close();
|
|
47
|
+
else show(link.href);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
dialog.addEventListener('keydown', function (event) {
|
|
51
|
+
if (event.target.closest('input, textarea, select')) return;
|
|
52
|
+
var rel = event.key === 'ArrowLeft' ? 'prev' : event.key === 'ArrowRight' ? 'next' : '';
|
|
53
|
+
if (!rel) return;
|
|
54
|
+
var link = body.querySelector('a[rel="' + rel + '"]');
|
|
55
|
+
if (!link) return;
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
show(link.href);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function show(url) {
|
|
61
|
+
fetch(url, { credentials: 'same-origin' })
|
|
62
|
+
.then(function (response) {
|
|
63
|
+
if (!response.ok) throw new Error(String(response.status));
|
|
64
|
+
return response.text();
|
|
65
|
+
})
|
|
66
|
+
.then(function (html) {
|
|
67
|
+
/* A template's content is inert: the other page's nonced <style>
|
|
68
|
+
elements are neither applied nor checked against this page's CSP. */
|
|
69
|
+
var parsed = document.createElement('template');
|
|
70
|
+
parsed.innerHTML = html;
|
|
71
|
+
var details = parsed.content.querySelector('[data-media-details]');
|
|
72
|
+
if (!details) throw new Error('no details');
|
|
73
|
+
body.replaceChildren(document.importNode(details, true));
|
|
74
|
+
body.dispatchEvent(new CustomEvent('geekity:inserted', { bubbles: true }));
|
|
75
|
+
if (!dialog.open) dialog.showModal();
|
|
76
|
+
dialog.focus();
|
|
77
|
+
})
|
|
78
|
+
.catch(function () {
|
|
79
|
+
window.location.href = url;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/admin/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM1C,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,wBAAwB,CAAC;AAYrE,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,6BAA6B,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/admin/documents.ts"],"names":[],"mappings":"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"}
|
package/dist/admin/documents.js
CHANGED
|
@@ -5,13 +5,13 @@ import { PAGE_KIND, POST_KIND } from '../content/document-kind.js';
|
|
|
5
5
|
import { TRANSCRIPT_TYPES } from '../content/enclosure.js';
|
|
6
6
|
import { ENCLOSURE_FIELDS } from '../content/enclosure-form.js';
|
|
7
7
|
import { EVENT_FIELDS } from '../content/event-form.js';
|
|
8
|
+
import { isReviewKind, REVIEW_KIND_FIELDS, REVIEW_KIND_WORDS, REVIEW_KINDS, } from '../content/review.js';
|
|
9
|
+
import { REVIEW_FIELDS } from '../content/review-form.js';
|
|
8
10
|
import { BLANK_LOCATION_FORM } from '../content/location-form.js';
|
|
9
11
|
import { postLocations } from '../content/locations.js';
|
|
10
12
|
import { PHOTO_FIELDS } from '../content/photo-form.js';
|
|
11
13
|
import { syndicateToField } from '../content/post-fields.js';
|
|
12
14
|
import { postLabel } from '../content/post-type.js';
|
|
13
|
-
import { isReadStatus, READ_STATUS_LABELS, READ_STATUSES } from '../content/read.js';
|
|
14
|
-
import { READ_FIELDS } from '../content/read-form.js';
|
|
15
15
|
import { RSVP_LABELS, RSVP_VALUES, rsvpValue } from '../content/rsvp.js';
|
|
16
16
|
import { scheduledFor } from '../content/schedule.js';
|
|
17
17
|
import { isTrashedPath } from '../content/store.js';
|
|
@@ -21,16 +21,18 @@ import { isVisibility, visibilityOf } from '../content/visibility.js';
|
|
|
21
21
|
import { missingAltText, writeDocument } from '../content/write.js';
|
|
22
22
|
import { currentUsername, documentSite } from '../web/document-site.js';
|
|
23
23
|
import { normalizeBody, serializeDocument } from '../content/writer.js';
|
|
24
|
-
import { readAltTexts } from '../content/alt-text.js';
|
|
25
24
|
import { listUsers } from '../site/users.js';
|
|
26
25
|
import { stringOrEmpty } from '../util/forms.js';
|
|
27
26
|
import { userForAuthor } from '../content/authors.js';
|
|
28
27
|
import { isServed } from '../content/served.js';
|
|
28
|
+
import { SEARCH_KINDS } from '../item-lookup/lookup.js';
|
|
29
29
|
import { syndicationTargetsReader } from '../webmention/syndication.js';
|
|
30
30
|
import { editorActionViews } from './editor-actions.js';
|
|
31
|
-
import { BLANK_ALTERNATE_ROW, enclosureChoices, LOCATION_FIELDS,
|
|
32
|
-
import {
|
|
31
|
+
import { BLANK_ALTERNATE_ROW, enclosureChoices, LOCATION_FIELDS, libraryThumbnail, photoPicker, readLocationForm, } from './editor-fields.js';
|
|
32
|
+
import { discoveredKind, EDITOR_KINDS, isPostKind, PAGE_CHOICE, POST_KINDS, REVIEW_EDITOR_WORDS, } from './editor-kinds.js';
|
|
33
|
+
import { editorSections, openGroups, sidebarLocation } from './editor-layout.js';
|
|
33
34
|
import { flash } from './flash.js';
|
|
35
|
+
import { ITEM_LOOKUP_BUTTON, ITEM_LOOKUP_PATH, ITEM_SEARCH_PATH, lookUpReviewItem, } from './item-lookup.js';
|
|
34
36
|
import { formatInTimezone } from './formatting.js';
|
|
35
37
|
import { allowGeolocation } from './headers.js';
|
|
36
38
|
import { PREVIEW_PATH } from './preview.js';
|
|
@@ -146,13 +148,24 @@ export function mountDocumentScreens(app, options) {
|
|
|
146
148
|
});
|
|
147
149
|
// Registered before the slug route, so a document can never take the URL of
|
|
148
150
|
// the form that makes a new one.
|
|
149
|
-
app.get(newEditorPath(kind), (c) =>
|
|
150
|
-
kind
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
app.get(newEditorPath(kind), (c) => {
|
|
152
|
+
const postKind = c.req.query('kind');
|
|
153
|
+
if (kind.type === 'post' && !isPostKind(postKind))
|
|
154
|
+
return renderChooser(c, kind, render);
|
|
155
|
+
return renderEditor(c, {
|
|
156
|
+
kind,
|
|
157
|
+
render,
|
|
158
|
+
document: undefined,
|
|
159
|
+
form: blankForm(kind, siteTimezone(c), c.var.store.now()),
|
|
160
|
+
postKind: isPostKind(postKind) ? postKind : undefined,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
app.post(newEditorPath(kind), async (c) => {
|
|
164
|
+
const body = await c.req.parseBody();
|
|
165
|
+
return (switchKind(c, { kind, render, document: undefined, body }) ??
|
|
166
|
+
lookUpItem(c, { kind, render, document: undefined, body }) ??
|
|
167
|
+
saveFromForm(c, { kind, render, document: undefined, body }));
|
|
168
|
+
});
|
|
156
169
|
app.get(`${kind.basePath}/:slug`, (c) => {
|
|
157
170
|
const document = findEdited(c.var.store, kind, c.req.param('slug'), c.req.query('path'));
|
|
158
171
|
if (document === undefined)
|
|
@@ -174,7 +187,9 @@ export function mountDocumentScreens(app, options) {
|
|
|
174
187
|
if (action === 'trash' || action === 'restore') {
|
|
175
188
|
return moveDocument(c, { kind, document, action, returnTo: stringOrEmpty(body['return']) });
|
|
176
189
|
}
|
|
177
|
-
return
|
|
190
|
+
return (switchKind(c, { kind, render, document, body }) ??
|
|
191
|
+
lookUpItem(c, { kind, render, document, body }) ??
|
|
192
|
+
saveFromForm(c, { kind, render, document, body }));
|
|
178
193
|
});
|
|
179
194
|
}
|
|
180
195
|
/**
|
|
@@ -183,7 +198,7 @@ export function mountDocumentScreens(app, options) {
|
|
|
183
198
|
*/
|
|
184
199
|
export function submittedForm(kind, body, contentDir) {
|
|
185
200
|
const declared = syndicationTargetsReader(contentDir)();
|
|
186
|
-
|
|
201
|
+
const form = {
|
|
187
202
|
title: stringOrEmpty(body['title']).trim(),
|
|
188
203
|
slug: stringOrEmpty(body['slug']).trim(),
|
|
189
204
|
permalink: stringOrEmpty(body['permalink']).trim(),
|
|
@@ -204,6 +219,61 @@ export function submittedForm(kind, body, contentDir) {
|
|
|
204
219
|
body: normalizeBody(stringOrEmpty(body['body'])),
|
|
205
220
|
hash: stringOrEmpty(body['hash']),
|
|
206
221
|
};
|
|
222
|
+
return kind.replies ? withLink(form, body) : form;
|
|
223
|
+
}
|
|
224
|
+
const LINK_FIELDS = { 'in-reply-to': 'inReplyTo', ...CITATION_FIELDS };
|
|
225
|
+
function withLink(form, body) {
|
|
226
|
+
const verb = stringOrEmpty(body['link-verb']);
|
|
227
|
+
if (!Object.hasOwn(LINK_FIELDS, verb) || Object.hasOwn(body, verb))
|
|
228
|
+
return form;
|
|
229
|
+
return {
|
|
230
|
+
...form,
|
|
231
|
+
[LINK_FIELDS[verb]]: stringOrEmpty(body['link-url']).trim(),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function switchKind(c, options) {
|
|
235
|
+
const { kind, render, document, body } = options;
|
|
236
|
+
const postKind = body['switch-kind'];
|
|
237
|
+
if (kind.type !== 'post' || !isPostKind(postKind))
|
|
238
|
+
return undefined;
|
|
239
|
+
return renderEditor(c, {
|
|
240
|
+
kind,
|
|
241
|
+
render,
|
|
242
|
+
document,
|
|
243
|
+
form: submittedForm(kind, body, c.var.config.contentDir),
|
|
244
|
+
postKind,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function lookUpItem(c, options) {
|
|
248
|
+
const { kind, render, document, body } = options;
|
|
249
|
+
if (kind.type !== 'post' || body[ITEM_LOOKUP_BUTTON] === undefined)
|
|
250
|
+
return undefined;
|
|
251
|
+
return (async () => {
|
|
252
|
+
const form = submittedForm(kind, body, c.var.config.contentDir);
|
|
253
|
+
const outcome = await lookUpReviewItem(c, form.review);
|
|
254
|
+
return renderEditor(c, {
|
|
255
|
+
kind,
|
|
256
|
+
render,
|
|
257
|
+
document,
|
|
258
|
+
form: { ...form, review: outcome.review },
|
|
259
|
+
postKind: 'review',
|
|
260
|
+
itemLookup: { message: outcome.message, failed: outcome.failed },
|
|
261
|
+
});
|
|
262
|
+
})();
|
|
263
|
+
}
|
|
264
|
+
function renderChooser(c, kind, render) {
|
|
265
|
+
return render(c, ADMIN_TEMPLATES.documentChooser, {
|
|
266
|
+
section: kind.section,
|
|
267
|
+
child: 'new',
|
|
268
|
+
kinds: [
|
|
269
|
+
...POST_KINDS.map((key) => ({
|
|
270
|
+
...EDITOR_KINDS[key],
|
|
271
|
+
url: `${newEditorPath(kind)}?kind=${key}`,
|
|
272
|
+
page: false,
|
|
273
|
+
})),
|
|
274
|
+
{ ...PAGE_CHOICE, url: newEditorPath(PAGE_SCREENS), page: true },
|
|
275
|
+
],
|
|
276
|
+
});
|
|
207
277
|
}
|
|
208
278
|
/**
|
|
209
279
|
* Write what the editor submitted through {@link writeDocument}, and answer
|
|
@@ -225,6 +295,7 @@ async function saveFromForm(c, options) {
|
|
|
225
295
|
render,
|
|
226
296
|
document,
|
|
227
297
|
form: { ...form, draft },
|
|
298
|
+
postKind: isPostKind(body['kind']) ? body['kind'] : undefined,
|
|
228
299
|
refusal,
|
|
229
300
|
status: 400,
|
|
230
301
|
});
|
|
@@ -408,6 +479,9 @@ function siteDefaultLabel(type, policy) {
|
|
|
408
479
|
*/
|
|
409
480
|
async function renderEditor(c, options) {
|
|
410
481
|
const { kind, document, form } = options;
|
|
482
|
+
const postKind = options.postKind ?? discoveredKind(form);
|
|
483
|
+
const editorKind = kind.type === 'post' ? EDITOR_KINDS[postKind] : undefined;
|
|
484
|
+
const sections = editorSections(postKind, form);
|
|
411
485
|
const trashed = document !== undefined && isTrashedPath(document.path);
|
|
412
486
|
const now = c.var.store.now();
|
|
413
487
|
const timezone = siteTimezone(c);
|
|
@@ -449,21 +523,45 @@ async function renderEditor(c, options) {
|
|
|
449
523
|
enclosureChoices: await enclosureChoices(c.var.config.contentDir, form.enclosure.url),
|
|
450
524
|
transcriptTypes: TRANSCRIPT_TYPES,
|
|
451
525
|
alternateRows: [...form.enclosure.alternates, BLANK_ALTERNATE_ROW],
|
|
526
|
+
postKind,
|
|
527
|
+
editorKind,
|
|
528
|
+
postKinds: POST_KINDS.map((key) => ({ key, ...EDITOR_KINDS[key] })),
|
|
529
|
+
sections,
|
|
530
|
+
sidebarLocation: sidebarLocation(postKind, form),
|
|
531
|
+
link: linkView(form, sections.shown.rsvp),
|
|
452
532
|
photoFields: PHOTO_FIELDS,
|
|
453
|
-
|
|
454
|
-
|
|
533
|
+
photos: await photoPicker(c.var.config, form.photos),
|
|
534
|
+
reviewPhotoThumbnail: libraryThumbnail(c.var.config, form.review.photo),
|
|
455
535
|
locationFields: LOCATION_FIELDS,
|
|
456
536
|
...citedPreviews(c, form),
|
|
457
537
|
rsvpChoices: RSVP_VALUES.map((value) => ({ value, label: RSVP_LABELS[value] })),
|
|
458
538
|
...(form.rsvp === '' || rsvpValue(form.rsvp) !== undefined
|
|
459
539
|
? {}
|
|
460
540
|
: { unrecognizedRsvp: form.rsvp }),
|
|
461
|
-
readFields: READ_FIELDS,
|
|
462
541
|
eventFields: EVENT_FIELDS,
|
|
463
|
-
|
|
464
|
-
|
|
542
|
+
reviewFields: REVIEW_FIELDS,
|
|
543
|
+
itemLookupButton: ITEM_LOOKUP_BUTTON,
|
|
544
|
+
itemLookupUrl: ITEM_LOOKUP_PATH,
|
|
545
|
+
itemLookup: options.itemLookup,
|
|
546
|
+
itemSearchUrl: ITEM_SEARCH_PATH,
|
|
547
|
+
itemSearchKinds: SEARCH_KINDS,
|
|
548
|
+
reviewKinds: REVIEW_KINDS.map((value) => ({
|
|
549
|
+
value,
|
|
550
|
+
...REVIEW_KIND_WORDS[value],
|
|
551
|
+
...REVIEW_EDITOR_WORDS[value],
|
|
552
|
+
})),
|
|
553
|
+
reviewKindsWith: Object.fromEntries(['by', 'year', 'uid', 'url', 'photo', 'date', 'season', 'episode', 'category'].map((field) => [
|
|
554
|
+
field,
|
|
555
|
+
REVIEW_KINDS.filter((value) => REVIEW_KIND_FIELDS[value].includes(field)),
|
|
556
|
+
])),
|
|
557
|
+
reviewKindChosen: form.review.kind === ''
|
|
558
|
+
? sections.filled.review
|
|
559
|
+
? 'other'
|
|
560
|
+
: 'book'
|
|
561
|
+
: form.review.kind,
|
|
562
|
+
...(form.review.kind === '' || isReviewKind(form.review.kind)
|
|
465
563
|
? {}
|
|
466
|
-
: {
|
|
564
|
+
: { unrecognizedReviewKind: form.review.kind }),
|
|
467
565
|
// TASK-155: one checkbox per target the site declares.
|
|
468
566
|
syndicationTargets: syndicationTargetsReader(c.var.config.contentDir)().map((target) => ({
|
|
469
567
|
...target,
|
|
@@ -478,8 +576,9 @@ async function renderEditor(c, options) {
|
|
|
478
576
|
// What an empty Language field means.
|
|
479
577
|
siteLanguage: c.var.siteAtRequestStart.language,
|
|
480
578
|
heading: document === undefined
|
|
481
|
-
? `
|
|
482
|
-
: `Edit ${kind.singular}: ${postLabel(document, (url) => c.var.replyContexts.read(url, c.var.siteAtRequestStart))}`,
|
|
579
|
+
? `New ${editorKind?.noun ?? kind.singular}`
|
|
580
|
+
: `Edit ${editorKind?.noun ?? kind.singular}: ${postLabel(document, (url) => c.var.replyContexts.read(url, c.var.siteAtRequestStart))}`,
|
|
581
|
+
chooserUrl: newEditorPath(kind),
|
|
483
582
|
saveUrl: document === undefined ? newEditorPath(kind) : documentEditorPath(kind, document),
|
|
484
583
|
listUrl: kind.basePath,
|
|
485
584
|
previewUrl: PREVIEW_PATH,
|
|
@@ -495,6 +594,26 @@ async function renderEditor(c, options) {
|
|
|
495
594
|
open: openGroups(form, options.refusal?.field),
|
|
496
595
|
});
|
|
497
596
|
}
|
|
597
|
+
const LINK_VERBS = [
|
|
598
|
+
{ property: 'in-reply-to', label: 'Replies to', also: 'Also replies to' },
|
|
599
|
+
{ property: 'like-of', label: 'Likes', also: 'Also likes' },
|
|
600
|
+
{ property: 'repost-of', label: 'Reposts', also: 'Also reposts' },
|
|
601
|
+
{ property: 'bookmark-of', label: 'Bookmarks', also: 'Also bookmarks' },
|
|
602
|
+
];
|
|
603
|
+
const LINK_ORDER = ['repost-of', 'like-of', 'in-reply-to', 'bookmark-of'];
|
|
604
|
+
function linkView(form, rsvpShown) {
|
|
605
|
+
const value = (property) => form[LINK_FIELDS[property]];
|
|
606
|
+
const offered = LINK_VERBS.filter((verb) => !rsvpShown || verb.property !== 'in-reply-to');
|
|
607
|
+
const chosen = LINK_ORDER.find((property) => value(property) !== '' && offered.some((verb) => verb.property === property)) ?? offered[0]?.property;
|
|
608
|
+
return {
|
|
609
|
+
verbs: offered,
|
|
610
|
+
chosen,
|
|
611
|
+
url: chosen === undefined ? '' : value(chosen),
|
|
612
|
+
others: offered
|
|
613
|
+
.filter((verb) => verb.property !== chosen && value(verb.property) !== '')
|
|
614
|
+
.map((verb) => ({ ...verb, url: value(verb.property) })),
|
|
615
|
+
};
|
|
616
|
+
}
|
|
498
617
|
function citedPreviews(c, form) {
|
|
499
618
|
const cited = [
|
|
500
619
|
['in-reply-to', form.inReplyTo],
|