@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
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
{# /admin/media/file?path=…: one upload, everything that can be said about it
|
|
2
|
+
and done to it (TASK-429). The rest of the query is the library view it was
|
|
3
|
+
opened from, which previous, next and the way back keep.
|
|
4
|
+
|
|
5
|
+
Everything inside [data-media-details] is what media.js shows in its dialog
|
|
6
|
+
when a tile is opened from the library, so it is the whole screen apart
|
|
7
|
+
from the shell. #}
|
|
8
|
+
{% extends "layouts/shell.njk" %}
|
|
9
|
+
{% from "components/badge.njk" import status %}
|
|
10
|
+
{% from "components/button.njk" import button, buttonLink %}
|
|
11
|
+
{% from "components/card.njk" import card, cardActions %}
|
|
12
|
+
{% from "components/copy.njk" import copyField %}
|
|
13
|
+
|
|
14
|
+
{% block title %}{{ heading }} · Media{% endblock %}
|
|
15
|
+
|
|
16
|
+
{% block content %}
|
|
17
|
+
<div data-media-details>
|
|
18
|
+
<div class="mb-4 flex flex-wrap items-center gap-2">
|
|
19
|
+
<a class="link link-hover text-sm" href="{{ libraryUrl }}" data-media-library>Media</a>
|
|
20
|
+
<span class="flex-1"></span>
|
|
21
|
+
<span class="text-sm">{{ position }} of {{ count }}</span>
|
|
22
|
+
<nav class="join" aria-label="Files">
|
|
23
|
+
{% if previousUrl %}
|
|
24
|
+
<a class="join-item btn btn-sm" rel="prev" href="{{ previousUrl }}" data-media-step>Previous</a>
|
|
25
|
+
{% else %}
|
|
26
|
+
<span class="join-item btn btn-sm btn-disabled" aria-disabled="true">Previous</span>
|
|
27
|
+
{% endif %}
|
|
28
|
+
{% if nextUrl %}
|
|
29
|
+
<a class="join-item btn btn-sm" rel="next" href="{{ nextUrl }}" data-media-step>Next</a>
|
|
30
|
+
{% else %}
|
|
31
|
+
<span class="join-item btn btn-sm btn-disabled" aria-disabled="true">Next</span>
|
|
32
|
+
{% endif %}
|
|
33
|
+
</nav>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<h1 class="mb-6 text-2xl font-bold break-all">{{ heading }}</h1>
|
|
37
|
+
|
|
38
|
+
{% if confirm %}
|
|
39
|
+
<form class="mb-6 max-w-2xl" method="post" action="{{ deleteUrl }}">
|
|
40
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
41
|
+
<input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
|
|
42
|
+
<input type="hidden" name="{{ fields.from }}" value="{{ from }}" />
|
|
43
|
+
<input type="hidden" name="{{ fields.confirm }}" value="1" />
|
|
44
|
+
{% call card(title='Delete ' + confirm.url + '?', style='border') %}
|
|
45
|
+
<p>
|
|
46
|
+
{{ confirm.references.length }}
|
|
47
|
+
{% if confirm.references.length == 1 %}document still points{% else %}documents still point{% endif %}
|
|
48
|
+
at it. Deleting the file does not change them, so they will point at nothing.
|
|
49
|
+
</p>
|
|
50
|
+
<ul class="list-disc pl-6">
|
|
51
|
+
{% for reference in confirm.references %}
|
|
52
|
+
<li>
|
|
53
|
+
<a class="link" href="{{ reference.editUrl }}">{{ reference.title }}</a>
|
|
54
|
+
{% if reference.trashed %}{{ status('trashed', 'Trash') }}{% endif %}
|
|
55
|
+
<code>{{ reference.path }}</code>
|
|
56
|
+
</li>
|
|
57
|
+
{% endfor %}
|
|
58
|
+
</ul>
|
|
59
|
+
{% call cardActions() %}
|
|
60
|
+
{{ buttonLink('Cancel', selfUrl, style='ghost') }}
|
|
61
|
+
{{ button('Delete it anyway', type='submit', color='error') }}
|
|
62
|
+
{% endcall %}
|
|
63
|
+
{% endcall %}
|
|
64
|
+
</form>
|
|
65
|
+
{% endif %}
|
|
66
|
+
|
|
67
|
+
<div class="grid gap-6 overflow-hidden rounded-box border border-base-300 bg-base-100 lg:grid-cols-[3fr_2fr]">
|
|
68
|
+
<div class="flex min-h-64 items-center justify-center bg-base-200 p-4">
|
|
69
|
+
{% if file.kind == 'image' %}
|
|
70
|
+
<a href="{{ file.url }}"><img class="max-h-[70vh] rounded-box object-contain" src="{{ file.url }}" alt=""{% if file.width %} width="{{ file.width }}" height="{{ file.height }}"{% endif %} /></a>
|
|
71
|
+
{% elif file.kind == 'audio' %}
|
|
72
|
+
<audio class="w-full" controls preload="none" src="{{ file.url }}"></audio>
|
|
73
|
+
{% elif file.kind == 'video' %}
|
|
74
|
+
<video class="max-h-[70vh] w-full" controls preload="metadata" src="{{ file.url }}"></video>
|
|
75
|
+
{% else %}
|
|
76
|
+
<a class="flex size-40 items-center justify-center rounded-box bg-base-300 font-bold" href="{{ file.url }}">{{ file.extension | replace('.', '') | upper or 'FILE' }}</a>
|
|
77
|
+
{% endif %}
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="flex flex-col gap-5 p-4">
|
|
81
|
+
<dl class="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm">
|
|
82
|
+
<dt>Uploaded</dt><dd>{{ file.modified | date('html') }}</dd>
|
|
83
|
+
<dt>Size</dt><dd>{{ file.bytes | fileSize }}{% if file.width %} · {{ file.width }} × {{ file.height }}{% endif %}</dd>
|
|
84
|
+
<dt>Path</dt><dd><code class="break-all">{{ file.path }}</code></dd>
|
|
85
|
+
</dl>
|
|
86
|
+
|
|
87
|
+
{% if file.kind == 'image' %}
|
|
88
|
+
<form method="post" action="{{ altUrl }}">
|
|
89
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
90
|
+
<input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
|
|
91
|
+
<input type="hidden" name="{{ fields.from }}" value="{{ from }}" />
|
|
92
|
+
<div class="fieldset">
|
|
93
|
+
<label class="fieldset-legend" for="media-alt">Alt text</label>
|
|
94
|
+
<textarea id="media-alt" name="{{ fields.alt }}" rows="3" placeholder="Describe what the picture shows" aria-describedby="media-alt-state" class="textarea w-full">{% if file.alt and file.alt.kind == 'described' %}{{ file.alt.text }}{% endif %}</textarea>
|
|
95
|
+
<label class="label text-sm text-base-content" for="media-decorative">
|
|
96
|
+
<input id="media-decorative" name="{{ fields.decorative }}" type="checkbox" value="1"{% if file.alt and file.alt.kind == 'decorative' %} checked{% endif %} class="checkbox checkbox-sm" />
|
|
97
|
+
Decorative
|
|
98
|
+
</label>
|
|
99
|
+
<p id="media-alt-state" class="text-sm">
|
|
100
|
+
{%- if not file.alt %}{{ status('missing', 'Missing') }} Describe it, or mark it decorative.
|
|
101
|
+
{%- elif file.alt.kind == 'decorative' %}{{ status('decorative', 'Decorative') }} Inserted with an empty alt.
|
|
102
|
+
{%- else %}{{ status('described', 'Described') }} Offered when it is inserted.{% endif -%}
|
|
103
|
+
</p>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="mt-2">{{ button('Save', type='submit', color='primary') }}</div>
|
|
106
|
+
</form>
|
|
107
|
+
{% endif %}
|
|
108
|
+
|
|
109
|
+
<div class="flex flex-col gap-2">
|
|
110
|
+
<h2 class="text-sm font-semibold">Link</h2>
|
|
111
|
+
{{ copyField('media-url', 'URL of ' + file.name, file.url) }}
|
|
112
|
+
{{ copyField('media-markdown', 'Markdown for ' + file.name, file.markdown) }}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div>
|
|
116
|
+
<h2 class="text-sm font-semibold">Used by</h2>
|
|
117
|
+
{% if file.references.length %}
|
|
118
|
+
<ul class="text-sm">
|
|
119
|
+
{% for reference in file.references %}
|
|
120
|
+
<li>
|
|
121
|
+
<a class="link link-hover" href="{{ reference.editUrl }}">{{ reference.title }}</a>
|
|
122
|
+
{% if reference.trashed %}{{ status('trashed', 'Trash') }}{% endif %}
|
|
123
|
+
</li>
|
|
124
|
+
{% endfor %}
|
|
125
|
+
</ul>
|
|
126
|
+
{% else %}
|
|
127
|
+
<p class="text-sm">Nothing links to it.</p>
|
|
128
|
+
{% endif %}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<form class="border-t border-base-300 pt-4 text-right" method="post" action="{{ deleteUrl }}">
|
|
132
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
133
|
+
<input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
|
|
134
|
+
<input type="hidden" name="{{ fields.from }}" value="{{ from }}" />
|
|
135
|
+
{{ button('Delete', type='submit', style='ghost', color='error') }}
|
|
136
|
+
</form>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<script defer src="{{ assetPrefix }}copy.js"></script>
|
|
142
|
+
{% endblock %}
|
|
@@ -1,228 +1,133 @@
|
|
|
1
|
-
{# /admin/media: everything under content/uploads
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
here without a restart.
|
|
1
|
+
{# /admin/media: everything under content/uploads as a grid of tiles, each a
|
|
2
|
+
link to its details screen (TASK-429). The directory is walked on every
|
|
3
|
+
request, so a file copied in by hand is here without a restart.
|
|
5
4
|
|
|
6
|
-
Everything works with JavaScript switched off
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Everything works with JavaScript switched off: the upload form opens from a
|
|
6
|
+
<details>, the filters are a GET form, and a tile is a link. media.js opens
|
|
7
|
+
a tile's details in a dialog instead and steps through them with the arrow
|
|
8
|
+
keys. #}
|
|
9
9
|
{% extends "layouts/shell.njk" %}
|
|
10
|
-
{% from "components/
|
|
10
|
+
{% from "components/alert.njk" import alert %}
|
|
11
|
+
{% from "components/badge.njk" import badge %}
|
|
11
12
|
{% from "components/button.njk" import button, buttonLink %}
|
|
12
|
-
{% from "components/card.njk" import card, cardActions %}
|
|
13
|
-
{% from "components/copy.njk" import copyField %}
|
|
14
|
-
{% from "components/job-status.njk" import jobStatus %}
|
|
15
13
|
{% from "components/pagination.njk" import pagination %}
|
|
16
|
-
{% from "components/
|
|
14
|
+
{% from "components/tabs.njk" import tabs %}
|
|
17
15
|
|
|
18
16
|
{% block title %}{{ heading }}{% endblock %}
|
|
19
17
|
|
|
20
18
|
{% block content %}
|
|
21
|
-
<
|
|
19
|
+
<div class="mb-6 flex flex-wrap items-center gap-3">
|
|
20
|
+
<h1 class="text-2xl font-bold">{{ heading }}</h1>
|
|
21
|
+
<details class="dropdown">
|
|
22
|
+
<summary class="btn btn-primary btn-sm">Upload</summary>
|
|
23
|
+
<form class="dropdown-content z-10 mt-2 w-80 rounded-box bg-base-100 p-4 shadow sm:w-96" method="post" action="{{ uploadUrl }}" enctype="multipart/form-data">
|
|
24
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
25
|
+
<div class="fieldset">
|
|
26
|
+
<label class="fieldset-legend" for="media-file">Add a file</label>
|
|
27
|
+
<input id="media-file" name="{{ fields.file }}" type="file" accept="{{ accepts }}" required class="file-input file-input-sm w-full" />
|
|
28
|
+
<p class="label block whitespace-normal">
|
|
29
|
+
This site accepts {{ accepts | replace(',', ', ') }}, up to {{ maxBytes | fileSize }}, or
|
|
30
|
+
{{ mediaMaxBytes | fileSize }} for audio and video. The same rules the editor’s upload
|
|
31
|
+
control enforces.
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="mt-2">{{ button('Upload', type='submit', color='primary') }}</div>
|
|
35
|
+
</form>
|
|
36
|
+
</details>
|
|
37
|
+
</div>
|
|
22
38
|
|
|
23
|
-
{% if
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{%
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
</ul>
|
|
43
|
-
{% call cardActions() %}
|
|
44
|
-
{{ buttonLink('Cancel', listUrl, style='ghost') }}
|
|
45
|
-
{{ button('Delete it anyway', type='submit', color='error') }}
|
|
46
|
-
{% endcall %}
|
|
39
|
+
{% if sweepNotice %}
|
|
40
|
+
<section id="strip-metadata-notice" class="mb-6 max-w-4xl" aria-label="Location and camera metadata">
|
|
41
|
+
{% call alert(color='info', style='soft', direction='responsive', polite=true) %}
|
|
42
|
+
<span>
|
|
43
|
+
{% if sweepNotice.sweep and sweepNotice.sweep.active %}
|
|
44
|
+
The sweep that strips location and camera metadata from older uploads is {{ sweepNotice.sweep.label | lower }}.
|
|
45
|
+
{% else %}
|
|
46
|
+
Uploads from before files were cleaned on arrival may still carry location or camera metadata.
|
|
47
|
+
{% endif %}
|
|
48
|
+
</span>
|
|
49
|
+
<div class="flex flex-wrap gap-2">
|
|
50
|
+
{% if not sweepNotice.sweep or not sweepNotice.sweep.active %}
|
|
51
|
+
<form method="post" action="{{ sweepNotice.url }}">
|
|
52
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
53
|
+
{{ button('Strip it now', type='submit') }}
|
|
54
|
+
</form>
|
|
55
|
+
{% endif %}
|
|
56
|
+
{{ buttonLink('Details in Maintenance', sweepNotice.panelUrl, style='ghost') }}
|
|
57
|
+
</div>
|
|
47
58
|
{% endcall %}
|
|
48
|
-
</
|
|
59
|
+
</section>
|
|
49
60
|
{% endif %}
|
|
50
61
|
|
|
51
|
-
<form class="mb-8 max-w-2xl" method="post" action="{{ uploadUrl }}" enctype="multipart/form-data">
|
|
52
|
-
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
53
|
-
<div class="fieldset">
|
|
54
|
-
<label class="fieldset-legend" for="media-file">Add a file</label>
|
|
55
|
-
<input id="media-file" name="{{ fields.file }}" type="file" accept="{{ accepts }}" required class="file-input file-input-sm w-full" />
|
|
56
|
-
<p class="label block whitespace-normal">
|
|
57
|
-
This site accepts {{ accepts | replace(',', ', ') }}, up to {{ maxBytes }} bytes, or
|
|
58
|
-
{{ mediaMaxBytes }} bytes for audio and video. The same
|
|
59
|
-
rules the editor’s upload control enforces.
|
|
60
|
-
</p>
|
|
61
|
-
</div>
|
|
62
|
-
<div class="mt-2">{{ button('Upload', type='submit', color='primary') }}</div>
|
|
63
|
-
</form>
|
|
64
|
-
|
|
65
|
-
<section id="strip-metadata" class="mb-8 max-w-2xl" aria-labelledby="strip-metadata-heading">
|
|
66
|
-
{% call card() %}
|
|
67
|
-
<h2 id="strip-metadata-heading" class="card-title">Location and camera metadata</h2>
|
|
68
|
-
<p class="max-w-prose">
|
|
69
|
-
Location and camera metadata is removed from pictures and videos as they are
|
|
70
|
-
uploaded. Files uploaded before that keep theirs until this sweep runs over
|
|
71
|
-
<code>content/uploads/</code>. It runs in the background, leaves a clean file
|
|
72
|
-
as it is, and keeps each file's modification time.
|
|
73
|
-
</p>
|
|
74
|
-
{% if sweep %}
|
|
75
|
-
{% call jobStatus(sweep, listUrl + '#strip-metadata', verb='Checked', unit='files') %}
|
|
76
|
-
{% if sweepReport %}
|
|
77
|
-
<p>
|
|
78
|
-
Checked {{ sweepReport.checked }} {% if sweepReport.checked == 1 %}file{% else %}files{% endif %}:
|
|
79
|
-
{{ sweepReport.stripped.length }} stripped, {{ sweepReport.clean }} already clean,
|
|
80
|
-
{{ sweepReport.unreadable.length }} unreadable.
|
|
81
|
-
</p>
|
|
82
|
-
{% if sweepReport.stripped.length %}
|
|
83
|
-
<h3 class="font-semibold">Stripped</h3>
|
|
84
|
-
<ul class="list-disc pl-6">
|
|
85
|
-
{% for file in sweepReport.stripped %}
|
|
86
|
-
<li><code class="break-all">{{ file.path }}</code>: removed {{ file.removed | join(', ') }}</li>
|
|
87
|
-
{% endfor %}
|
|
88
|
-
</ul>
|
|
89
|
-
{% endif %}
|
|
90
|
-
{% if sweepReport.unreadable.length %}
|
|
91
|
-
<h3 class="font-semibold">Left as they were</h3>
|
|
92
|
-
<ul class="list-disc pl-6">
|
|
93
|
-
{% for file in sweepReport.unreadable %}
|
|
94
|
-
<li><code class="break-all">{{ file.path }}</code>: {{ file.problem }}</li>
|
|
95
|
-
{% endfor %}
|
|
96
|
-
</ul>
|
|
97
|
-
{% endif %}
|
|
98
|
-
{% endif %}
|
|
99
|
-
{% endcall %}
|
|
100
|
-
{% endif %}
|
|
101
|
-
{% if not sweep or not sweep.active %}
|
|
102
|
-
<form method="post" action="{{ stripMetadataUrl }}">
|
|
103
|
-
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
104
|
-
{% call cardActions() %}
|
|
105
|
-
{{ button('Run the sweep again' if sweep else 'Strip metadata from every upload', type='submit', color='primary') }}
|
|
106
|
-
{% endcall %}
|
|
107
|
-
</form>
|
|
108
|
-
{% endif %}
|
|
109
|
-
{% endcall %}
|
|
110
|
-
</section>
|
|
111
|
-
|
|
112
62
|
{% if total == 0 %}
|
|
113
63
|
<p>Nothing has been uploaded yet.</p>
|
|
114
64
|
{% else %}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
65
|
+
<div class="mb-4 flex flex-col gap-3">
|
|
66
|
+
{{ tabs(kindTabs, 'Kinds of file', size='sm') }}
|
|
67
|
+
<form class="flex flex-wrap items-center gap-3" method="get" action="{{ listUrl }}">
|
|
68
|
+
{% if view.kind %}<input type="hidden" name="kind" value="{{ view.kind }}" />{% endif %}
|
|
69
|
+
<label class="sr-only" for="media-search">Search file names and alt text</label>
|
|
70
|
+
<input id="media-search" name="q" type="search" value="{{ view.search }}" placeholder="Search file names and alt text" class="input input-sm w-64" />
|
|
71
|
+
<label class="label text-sm text-base-content">
|
|
72
|
+
<input name="alt" type="checkbox" value="missing" class="checkbox checkbox-sm"{% if view.needsAlt %} checked{% endif %} />
|
|
73
|
+
Needs alt text
|
|
74
|
+
</label>
|
|
75
|
+
<label class="label text-sm text-base-content">
|
|
76
|
+
<input name="unused" type="checkbox" value="1" class="checkbox checkbox-sm"{% if view.unused %} checked{% endif %} />
|
|
77
|
+
Not used anywhere
|
|
78
|
+
</label>
|
|
79
|
+
<label class="sr-only" for="media-sort">Sort</label>
|
|
80
|
+
<select id="media-sort" name="sort" class="select select-sm w-40">
|
|
81
|
+
{% for value, label in sorts %}
|
|
82
|
+
<option value="{{ value }}"{% if value == view.sort %} selected{% endif %}>{{ label }}</option>
|
|
83
|
+
{% endfor %}
|
|
84
|
+
</select>
|
|
85
|
+
{{ button('Filter', type='submit') }}
|
|
86
|
+
{% if filtered %}{{ buttonLink('Clear', clearUrl, style='ghost') }}{% endif %}
|
|
87
|
+
</form>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<p class="mb-3 text-sm">{{ shown }} of {{ total }} {% if total == 1 %}file{% else %}files{% endif %}</p>
|
|
91
|
+
|
|
92
|
+
{% if shown == 0 %}
|
|
93
|
+
<p>Nothing matches.</p>
|
|
94
|
+
{% else %}
|
|
95
|
+
<ul class="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-6">
|
|
128
96
|
{% for file in files %}
|
|
129
|
-
<
|
|
130
|
-
<
|
|
131
|
-
<
|
|
97
|
+
<li>
|
|
98
|
+
<a class="block overflow-hidden rounded-box bg-base-100 shadow-sm ring-primary hover:ring-2 focus-visible:ring-2 focus-visible:outline-none" href="{{ file.detailsUrl }}" data-media-tile>
|
|
99
|
+
<span class="relative block aspect-square bg-base-200">
|
|
132
100
|
{% if file.kind == 'image' %}
|
|
133
|
-
<
|
|
134
|
-
<img class="size-15 rounded-box bg-base-200 object-cover" src="{{ file.url }}" alt="" width="60" height="60" />
|
|
135
|
-
</a>
|
|
101
|
+
<img class="size-full object-cover" src="{{ file.thumbnailUrl }}" alt="" loading="lazy" />
|
|
136
102
|
{% else %}
|
|
137
|
-
<span class="flex size-
|
|
103
|
+
<span class="flex size-full items-center justify-center text-sm font-bold" aria-hidden="true">{{ file.extension | replace('.', '') | upper }}</span>
|
|
104
|
+
{% endif %}
|
|
105
|
+
{% if file.needsAlt or not file.references.length %}
|
|
106
|
+
<span class="absolute top-2 left-2 flex flex-col items-start gap-1">
|
|
107
|
+
{% if file.needsAlt %}{{ badge('No alt text', color='warning') }}{% endif %}
|
|
108
|
+
{% if not file.references.length %}{{ badge('Unused', color='neutral') }}{% endif %}
|
|
109
|
+
</span>
|
|
138
110
|
{% endif %}
|
|
139
|
-
|
|
140
|
-
</
|
|
141
|
-
</
|
|
142
|
-
|
|
143
|
-
{% if file.kind == 'image' %}
|
|
144
|
-
<form class="flex min-w-72 flex-wrap items-center gap-2" method="post" action="{{ altUrl }}">
|
|
145
|
-
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
146
|
-
<input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
|
|
147
|
-
<input type="hidden" name="{{ fields.page }}" value="{{ page }}" />
|
|
148
|
-
<label class="sr-only" for="media-alt-{{ loop.index }}">Alt text for {{ file.name }}</label>
|
|
149
|
-
<input
|
|
150
|
-
id="media-alt-{{ loop.index }}"
|
|
151
|
-
name="{{ fields.alt }}"
|
|
152
|
-
type="text"
|
|
153
|
-
value="{% if file.alt and file.alt.kind == 'described' %}{{ file.alt.text }}{% endif %}"
|
|
154
|
-
aria-describedby="media-alt-state-{{ loop.index }}"
|
|
155
|
-
class="input input-sm min-w-40 flex-1"
|
|
156
|
-
/>
|
|
157
|
-
<label class="label text-sm text-base-content" for="media-decorative-{{ loop.index }}">
|
|
158
|
-
<input
|
|
159
|
-
id="media-decorative-{{ loop.index }}"
|
|
160
|
-
name="{{ fields.decorative }}"
|
|
161
|
-
type="checkbox"
|
|
162
|
-
value="1"
|
|
163
|
-
{% if file.alt and file.alt.kind == 'decorative' %}checked{% endif %}
|
|
164
|
-
class="checkbox checkbox-sm"
|
|
165
|
-
/>
|
|
166
|
-
Decorative
|
|
167
|
-
</label>
|
|
168
|
-
{{ button('Save', type='submit', size='sm') }}
|
|
169
|
-
<p id="media-alt-state-{{ loop.index }}" class="w-full text-sm">
|
|
170
|
-
{%- if not file.alt %}{{ status('missing', 'Missing') }} Describe it, or mark it decorative.
|
|
171
|
-
{%- elif file.alt.kind == 'decorative' %}{{ status('decorative', 'Decorative') }} Inserted with an empty alt.
|
|
172
|
-
{%- else %}{{ status('described', 'Described') }} Offered when it is inserted.{% endif -%}
|
|
173
|
-
</p>
|
|
174
|
-
</form>
|
|
175
|
-
{% else %}
|
|
176
|
-
Not an image
|
|
177
|
-
{% endif %}
|
|
178
|
-
</td>
|
|
179
|
-
<td class="whitespace-nowrap">{{ file.modified | date('html') }}</td>
|
|
180
|
-
<td class="whitespace-nowrap">{{ file.bytes }}</td>
|
|
181
|
-
<td>
|
|
182
|
-
<div class="flex min-w-60 flex-col gap-2">
|
|
183
|
-
{{ copyField('media-url-' + loop.index, 'URL of ' + file.name, file.url) }}
|
|
184
|
-
{{ copyField('media-markdown-' + loop.index, 'Markdown for ' + file.name, file.markdown) }}
|
|
185
|
-
</div>
|
|
186
|
-
</td>
|
|
187
|
-
<td>
|
|
188
|
-
{% if file.references.length %}
|
|
189
|
-
<ul>
|
|
190
|
-
{% for reference in file.references %}
|
|
191
|
-
<li class="whitespace-nowrap">
|
|
192
|
-
<a class="link link-hover" href="{{ reference.editUrl }}">{{ reference.title }}</a>
|
|
193
|
-
{% if reference.trashed %}{{ status('trashed', 'Trash') }}{% endif %}
|
|
194
|
-
</li>
|
|
195
|
-
{% endfor %}
|
|
196
|
-
</ul>
|
|
197
|
-
{% else %}
|
|
198
|
-
Nothing
|
|
199
|
-
{% endif %}
|
|
200
|
-
</td>
|
|
201
|
-
<td>
|
|
202
|
-
<form method="post" action="{{ deleteUrl }}">
|
|
203
|
-
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
204
|
-
<input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
|
|
205
|
-
{{ button('Delete', type='submit', style='ghost', size='xs') }}
|
|
206
|
-
</form>
|
|
207
|
-
</td>
|
|
208
|
-
</tr>
|
|
111
|
+
</span>
|
|
112
|
+
<span class="block truncate px-2 py-1.5 text-xs">{{ file.name }}</span>
|
|
113
|
+
</a>
|
|
114
|
+
</li>
|
|
209
115
|
{% endfor %}
|
|
210
|
-
</
|
|
211
|
-
{% endcall %}
|
|
116
|
+
</ul>
|
|
212
117
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
118
|
+
<div class="mt-6">
|
|
119
|
+
{{ pagination(page, pages=pages, previousUrl=previousUrl, nextUrl=nextUrl) }}
|
|
120
|
+
</div>
|
|
121
|
+
{% endif %}
|
|
216
122
|
|
|
217
|
-
<p class="mt-
|
|
218
|
-
The
|
|
123
|
+
<p class="mt-6 max-w-prose text-sm">
|
|
124
|
+
The library is the <code>content/uploads</code> directory itself, read afresh on every request,
|
|
219
125
|
so a file copied in by hand is here without a restart. Deleting one removes the file and
|
|
220
126
|
whatever was derived from it; it does not touch the documents that link to it, which is why
|
|
221
127
|
a file something still points at asks first.
|
|
222
128
|
</p>
|
|
223
129
|
{% endif %}
|
|
224
130
|
|
|
225
|
-
{# The one enhancement on this screen. The fields above are readonly inputs
|
|
226
|
-
and already copy by hand; this adds a button beside each of them. #}
|
|
227
131
|
<script defer src="{{ assetPrefix }}copy.js"></script>
|
|
132
|
+
<script defer src="{{ assetPrefix }}media.js"></script>
|
|
228
133
|
{% endblock %}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{# /admin/settings: what the site is called, where it lives, and its picture. #}
|
|
2
2
|
{% extends "layouts/settings-page.njk" %}
|
|
3
3
|
{% import "components/fields.njk" as field %}
|
|
4
|
+
{% from "components/badge.njk" import status %}
|
|
5
|
+
{% from "components/button.njk" import button %}
|
|
6
|
+
{% from "components/card.njk" import card, cardActions %}
|
|
4
7
|
|
|
5
8
|
{% block settingsProblems %}
|
|
6
9
|
{{ field.problem('settings-title', problems.title) }}
|
|
@@ -93,4 +96,48 @@
|
|
|
93
96
|
error=problems.license_name, hint='What the footer calls it. Only read for a custom license.') }}
|
|
94
97
|
{% endblock %}
|
|
95
98
|
|
|
96
|
-
{% block settingsPanels %}
|
|
99
|
+
{% block settingsPanels %}
|
|
100
|
+
{% call card(title='Movies and TV shows') %}
|
|
101
|
+
<p>
|
|
102
|
+
{% if tmdbPresent %}
|
|
103
|
+
{{ status('connected', 'Connected') }} Key {{ tmdbKeyHint }}.
|
|
104
|
+
{% else %}
|
|
105
|
+
{{ status('disconnected', 'Not connected') }} This site has no TMDb key.
|
|
106
|
+
{% endif %}
|
|
107
|
+
</p>
|
|
108
|
+
|
|
109
|
+
{% if tmdbSetBy %}
|
|
110
|
+
<p class="text-sm">The key is set by <code>{{ tmdbSetBy }}</code> and cannot be changed here.</p>
|
|
111
|
+
{% else %}
|
|
112
|
+
<form method="post" action="{{ tmdbUrl }}">
|
|
113
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
114
|
+
{{ field.password('settings-tmdb-key', tmdbFields.key, 'TMDb API key',
|
|
115
|
+
autocomplete='off', spellcheck='false',
|
|
116
|
+
placeholder=('Replace the stored key' if tmdbPresent else 'Paste your key')) }}
|
|
117
|
+
<div class="mt-2">
|
|
118
|
+
{{ button('Replace key' if tmdbPresent else 'Save key', type='submit', color='primary') }}
|
|
119
|
+
</div>
|
|
120
|
+
</form>
|
|
121
|
+
{% endif %}
|
|
122
|
+
|
|
123
|
+
<p class="text-sm">
|
|
124
|
+
With a key from <a class="link" href="https://www.themoviedb.org/settings/api">themoviedb.org</a>,
|
|
125
|
+
Look up on a review fills in a movie or TV show from its TMDb or IMDb link:
|
|
126
|
+
its title, director or network, year and poster. Either the API key or
|
|
127
|
+
the API read access token works. It is kept in <code>data/tmdb.json</code>,
|
|
128
|
+
never in <code>site.json</code> and never on the public site. A deployment
|
|
129
|
+
can set it instead with <code>GEEKITY_TMDB__KEY</code>. This site uses TMDB
|
|
130
|
+
and the TMDB APIs but is not endorsed, certified or otherwise approved by TMDB.
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
{% if tmdbPresent and not tmdbSetBy %}
|
|
134
|
+
<form method="post" action="{{ tmdbUrl }}">
|
|
135
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
136
|
+
<input type="hidden" name="{{ tmdbFields.action }}" value="{{ tmdbRemove }}" />
|
|
137
|
+
{% call cardActions() %}
|
|
138
|
+
{{ button('Remove key', type='submit', color='error', style='ghost') }}
|
|
139
|
+
{% endcall %}
|
|
140
|
+
</form>
|
|
141
|
+
{% endif %}
|
|
142
|
+
{% endcall %}
|
|
143
|
+
{% endblock %}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
<p class="max-w-prose text-sm">
|
|
32
32
|
Location and camera metadata is always removed from pictures and videos as
|
|
33
33
|
they are uploaded, whatever is chosen above. Files uploaded before this
|
|
34
|
-
version keep theirs until the sweep on the <a class="link" href="{{ adminUrl }}/
|
|
34
|
+
version keep theirs until the sweep on the <a class="link" href="{{ adminUrl }}/tools/maintenance#strip-metadata">Maintenance</a>
|
|
35
35
|
screen, or <code>geekity strip-metadata</code>, is run over <code>content/uploads/</code>.
|
|
36
36
|
</p>
|
|
37
37
|
{% endblock %}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
{% from "components/alert.njk" import alert %}
|
|
4
4
|
{% from "components/button.njk" import button %}
|
|
5
5
|
{% from "components/card.njk" import card, cardActions %}
|
|
6
|
+
{% from "components/job-status.njk" import jobStatus %}
|
|
6
7
|
|
|
7
8
|
{% block title %}{{ heading }}{% endblock %}
|
|
8
9
|
|
|
@@ -47,5 +48,52 @@
|
|
|
47
48
|
<code>geekity maintenance on</code> and <code>off</code> at the command line turn the
|
|
48
49
|
same switch, and a running site notices either within a second.
|
|
49
50
|
</p>
|
|
51
|
+
|
|
52
|
+
<section id="strip-metadata" aria-labelledby="strip-metadata-heading">
|
|
53
|
+
{% call card() %}
|
|
54
|
+
<h2 id="strip-metadata-heading" class="card-title">Location and camera metadata</h2>
|
|
55
|
+
<p class="max-w-prose">
|
|
56
|
+
Location and camera metadata is removed from pictures and videos as they are
|
|
57
|
+
uploaded. Files uploaded before that keep theirs until this sweep runs over
|
|
58
|
+
<code>content/uploads/</code>. It runs in the background, leaves a clean file
|
|
59
|
+
as it is, and keeps each file's modification time.
|
|
60
|
+
</p>
|
|
61
|
+
{% if sweep %}
|
|
62
|
+
{% call jobStatus(sweep, saveUrl + '#strip-metadata', verb='Checked', unit='files') %}
|
|
63
|
+
{% if sweepReport %}
|
|
64
|
+
<p>
|
|
65
|
+
Checked {{ sweepReport.checked }} {% if sweepReport.checked == 1 %}file{% else %}files{% endif %}:
|
|
66
|
+
{{ sweepReport.stripped.length }} stripped, {{ sweepReport.clean }} already clean,
|
|
67
|
+
{{ sweepReport.unreadable.length }} unreadable.
|
|
68
|
+
</p>
|
|
69
|
+
{% if sweepReport.stripped.length %}
|
|
70
|
+
<h3 class="font-semibold">Stripped</h3>
|
|
71
|
+
<ul class="list-disc pl-6">
|
|
72
|
+
{% for file in sweepReport.stripped %}
|
|
73
|
+
<li><code class="break-all">{{ file.path }}</code>: removed {{ file.removed | join(', ') }}</li>
|
|
74
|
+
{% endfor %}
|
|
75
|
+
</ul>
|
|
76
|
+
{% endif %}
|
|
77
|
+
{% if sweepReport.unreadable.length %}
|
|
78
|
+
<h3 class="font-semibold">Left as they were</h3>
|
|
79
|
+
<ul class="list-disc pl-6">
|
|
80
|
+
{% for file in sweepReport.unreadable %}
|
|
81
|
+
<li><code class="break-all">{{ file.path }}</code>: {{ file.problem }}</li>
|
|
82
|
+
{% endfor %}
|
|
83
|
+
</ul>
|
|
84
|
+
{% endif %}
|
|
85
|
+
{% endif %}
|
|
86
|
+
{% endcall %}
|
|
87
|
+
{% endif %}
|
|
88
|
+
{% if not sweep or not sweep.active %}
|
|
89
|
+
<form method="post" action="{{ stripMetadataUrl }}">
|
|
90
|
+
<input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
|
|
91
|
+
{% call cardActions() %}
|
|
92
|
+
{{ button('Run the sweep again' if sweep else 'Strip metadata from every upload', type='submit', color='primary') }}
|
|
93
|
+
{% endcall %}
|
|
94
|
+
</form>
|
|
95
|
+
{% endif %}
|
|
96
|
+
{% endcall %}
|
|
97
|
+
</section>
|
|
50
98
|
</div>
|
|
51
99
|
{% endblock %}
|