diff options
Diffstat (limited to 'internal/web/templates/dashboard')
| -rw-r--r-- | internal/web/templates/dashboard/files.html | 95 | ||||
| -rw-r--r-- | internal/web/templates/dashboard/images.html | 24 | ||||
| -rw-r--r-- | internal/web/templates/dashboard/overview.html | 2 | ||||
| -rw-r--r-- | internal/web/templates/dashboard/page_form.html | 2 | ||||
| -rw-r--r-- | internal/web/templates/dashboard/post_form.html | 2 | ||||
| -rw-r--r-- | internal/web/templates/dashboard/section_form.html | 2 |
6 files changed, 99 insertions, 28 deletions
diff --git a/internal/web/templates/dashboard/files.html b/internal/web/templates/dashboard/files.html new file mode 100644 index 0000000..b42cec2 --- /dev/null +++ b/internal/web/templates/dashboard/files.html @@ -0,0 +1,95 @@ +{{define "title"}}{{t "Files"}} · {{.Blog.Title}}{{end}} +{{define "content"}} +{{$b := .Blog.Subdomain}} +<div class="pagehead"> + <h1>{{t "Files"}}</h1> + <p class="lead muted">{{t "Files are stored with your blog. Put one in a post with"}} <em>{{t "Insert file"}}</em> {{t "in the editor, or copy its line from the table."}} {{tf "%d files, %s in use." .Data.count (size .Data.bytes)}}</p> +</div> +<div class="card dropzone" id="dropzone"> + <form method="post" action="/b/{{$b}}/files/upload" enctype="multipart/form-data" id="upload-form"> + <input type="hidden" name="_csrf" value="{{.CSRF}}"> + <label>{{t "Upload files"}} <span class="muted">({{tf "any type, up to %s each" .Data.limit}})</span><br><input type="file" name="file" id="upload-input" multiple required></label> + <p class="actionrow"><button type="submit">{{t "Upload"}}</button> <span class="muted small" id="upload-hint" hidden>{{t "…or drop files anywhere on this box."}}</span></p> + <ul class="progress" id="upload-progress"></ul> + </form> +</div> +<form method="get" class="filter files-filter"> + <span class="kinds"> + <a href="/b/{{$b}}/files{{if .Data.q}}?q={{.Data.q}}{{end}}"{{if not .Data.kind}} class="active"{{end}}>{{t "All"}}</a> + {{range .Data.kinds}}<a href="/b/{{$b}}/files?kind={{.}}{{if $.Data.q}}&q={{$.Data.q}}{{end}}"{{if eq . $.Data.kind}} class="active"{{end}}>{{t (index $.Data.kindNames .)}}</a>{{end}} + </span> + {{if .Data.kind}}<input type="hidden" name="kind" value="{{.Data.kind}}">{{end}} + <input type="search" name="q" value="{{.Data.q}}" placeholder="{{t "Search files"}}" size="18"> <button type="submit" class="small">{{t "Search"}}</button> +</form> +<div class="card"> +{{if .Data.files}} +<table class="files"> + <tr><th></th><th>{{t "Name"}}</th><th>{{t "Kind"}}</th><th>{{t "Size"}}</th><th>{{t "Date"}}</th><th>Markdown</th><th></th></tr> + {{range .Data.files}}<tr> + <td class="icon">{{if eq .Kind "image"}}<a href="/b/{{$b}}/media/{{.ID}}" target="_blank"><img class="thumb" src="/b/{{$b}}/media/{{.ID}}" alt=""></a>{{else}}<span class="badge">{{.Badge}}</span>{{end}}</td> + <td class="name"><a href="/b/{{$b}}/media/{{.ID}}" target="_blank">{{.Filename}}</a> + <details class="inline"><summary class="mini">{{t "rename"}}</summary> + <form method="post" action="/b/{{$b}}/files/{{.ID}}/rename" class="inline"> + <input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="back" value="{{$.Data.self}}"> + <input type="text" name="filename" value="{{.Filename}}" maxlength="120" required size="18"> + <button class="mini">{{t "Save"}}</button> + </form></details></td> + <td class="nowrap">{{t (index $.Data.kindNames .Kind)}}</td> + <td class="nowrap">{{size .Size}}</td> + <td class="nowrap">{{date .CreatedAt}}</td> + <td><input class="copy" readonly value="{{filemd .}}" onclick="this.select()"></td> + <td class="nowrap"><a href="/b/{{$b}}/media/{{.ID}}?download">{{t "download"}}</a> · + <form method="post" action="/b/{{$b}}/files/{{.ID}}/delete" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="back" value="{{$.Data.self}}"><button class="mini danger">{{t "delete"}}</button></form></td> + </tr>{{end}} +</table> +{{else if or .Data.q .Data.kind}}<p class="muted">{{t "No files match."}}</p> +{{else}}<p class="muted">{{t "No files yet."}}</p>{{end}} +</div> +{{if gt .Data.lastPage 1}} +<div class="pager"> + {{if gt .Data.pageNum 1}}<a href="{{.Data.prevURL}}">← {{t "Newer"}}</a>{{end}} + <span>{{tf "Page %d of %d" .Data.pageNum .Data.lastPage}}</span> + {{if lt .Data.pageNum .Data.lastPage}}<a href="{{.Data.nextURL}}">{{t "Older"}} →</a>{{end}} +</div> +{{end}} +<script> +(function () { + var zone = document.getElementById("dropzone"), form = document.getElementById("upload-form"), inp = document.getElementById("upload-input"), list = document.getElementById("upload-progress"); + if (!window.fetch || !window.FormData || !zone || !inp) return; // old browser: the plain form still works + document.getElementById("upload-hint").hidden = false; + var busy = false; + function line(text) { var li = document.createElement("li"); li.textContent = text; list.appendChild(li); return li; } + function upload(files) { + if (busy || !files.length) return; + busy = true; + var done = 0, i = 0; + function next() { + if (i >= files.length) { + busy = false; + if (done) location.href = {{.Data.self}} + ({{.Data.self}}.indexOf("?") < 0 ? "?ok=" : "&ok=") + encodeURIComponent({{t "Uploaded %d files."}}.replace("%d", done)); + return; + } + var f = files[i++], li = line(f.name + " — " + {{t "uploading…"}}); + var fd = new FormData(); + fd.append("_csrf", {{.CSRF}}); + fd.append("file", f); + fetch(form.action, { method: "POST", body: fd, credentials: "same-origin", headers: { Accept: "application/json" } }) + .then(function (r) { return r.json().then(function (j) { if (!r.ok || j.error) throw new Error(j.error || {{t "upload failed"}}); return j; }); }) + .then(function () { li.textContent = f.name + " — " + {{t "uploaded"}}; done++; }) + .catch(function (err) { li.textContent = f.name + " — " + err.message; }) + .then(next); + } + next(); + } + form.addEventListener("submit", function (ev) { if (inp.files && inp.files.length) { ev.preventDefault(); upload(inp.files); } }); + inp.addEventListener("change", function () { if (inp.files && inp.files.length) { var fs = Array.prototype.slice.call(inp.files); inp.value = ""; upload(fs); } }); + zone.addEventListener("dragover", function (ev) { ev.preventDefault(); zone.className = "card dropzone over"; }); + zone.addEventListener("dragleave", function () { zone.className = "card dropzone"; }); + zone.addEventListener("drop", function (ev) { + ev.preventDefault(); + zone.className = "card dropzone"; + if (ev.dataTransfer && ev.dataTransfer.files) upload(ev.dataTransfer.files); + }); +})(); +</script> +{{end}} diff --git a/internal/web/templates/dashboard/images.html b/internal/web/templates/dashboard/images.html deleted file mode 100644 index 8b9faa3..0000000 --- a/internal/web/templates/dashboard/images.html +++ /dev/null @@ -1,24 +0,0 @@ -{{define "title"}}{{t "Images"}} · {{.Blog.Title}}{{end}} -{{define "content"}} -<div class="pagehead"> - <h1>{{t "Images"}}</h1> - <p class="lead muted">{{t "Images are stored with your blog. Put one in a post with"}} <em>{{t "Insert image"}}</em> {{t "in the editor, or copy its line from below."}}</p> -</div> -<div class="card"> - <form method="post" action="/b/{{.Blog.Subdomain}}/images/upload" enctype="multipart/form-data"> - <input type="hidden" name="_csrf" value="{{.CSRF}}"> - <label>{{t "Upload an image"}} <span class="muted">({{t "PNG, JPEG, GIF, WebP or ICO"}})</span><br><input type="file" name="file" accept="image/*" required></label> - <p class="actionrow"><button type="submit">{{t "Upload"}}</button></p> - </form> -</div> -<div class="gallery"> -{{range .Data.images}} - <div class="card thumb"> - <a href="/b/{{$.Blog.Subdomain}}/media/{{.ID}}" target="_blank"><img src="/b/{{$.Blog.Subdomain}}/media/{{.ID}}" alt="{{.Filename}}"></a> - <div class="meta">{{.Filename}} <span class="muted">({{kb .Size}})</span></div> - <input class="copy" readonly value="" onclick="this.select()"> - <form method="post" action="/b/{{$.Blog.Subdomain}}/images/{{.ID}}/delete" class="inline"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><button class="mini danger">{{t "delete"}}</button></form> - </div> -{{else}}<p class="muted">{{t "No images yet."}}</p>{{end}} -</div> -{{end}} diff --git a/internal/web/templates/dashboard/overview.html b/internal/web/templates/dashboard/overview.html index e021495..0f611cd 100644 --- a/internal/web/templates/dashboard/overview.html +++ b/internal/web/templates/dashboard/overview.html @@ -7,7 +7,7 @@ <div class="card"> <h2>{{t "Quick actions"}}</h2> <p><a class="btn" href="/b/{{.Blog.Subdomain}}/posts/new">{{t "Write a new post"}}</a></p> - <p><a href="/b/{{.Blog.Subdomain}}/pages/new">{{t "Add a page"}}</a> · <a href="/b/{{.Blog.Subdomain}}/announcements/new">{{t "Post an announcement"}}</a> · <a href="/b/{{.Blog.Subdomain}}/layout">{{t "Arrange the layout"}}</a> · <a href="/b/{{.Blog.Subdomain}}/design">{{t "Change the look"}}</a> · <a href="/b/{{.Blog.Subdomain}}/images">{{t "Upload images"}}</a></p> + <p><a href="/b/{{.Blog.Subdomain}}/pages/new">{{t "Add a page"}}</a> · <a href="/b/{{.Blog.Subdomain}}/announcements/new">{{t "Post an announcement"}}</a> · <a href="/b/{{.Blog.Subdomain}}/layout">{{t "Arrange the layout"}}</a> · <a href="/b/{{.Blog.Subdomain}}/design">{{t "Change the look"}}</a> · <a href="/b/{{.Blog.Subdomain}}/files">{{t "Upload files"}}</a></p> </div> <div class="card"> <h2>{{t "Pages"}}</h2> diff --git a/internal/web/templates/dashboard/page_form.html b/internal/web/templates/dashboard/page_form.html index 08c4a43..18d1119 100644 --- a/internal/web/templates/dashboard/page_form.html +++ b/internal/web/templates/dashboard/page_form.html @@ -8,7 +8,7 @@ <label>{{t "Address"}} <span class="muted">({{t "leave empty to make one from the title; e.g."}} <code>about</code> → {{.BlogURL}}/about)</span><br> <input name="slug" value="{{.Data.page.Slug}}" pattern="[a-z0-9]([a-z0-9-]*[a-z0-9])?" maxlength="80"></label> <label for="ed-intro">{{t "Intro text"}} <span class="muted">({{t "Markdown, shown above the posts; optional"}})</span></label> - {{template "editor" (dict "name" "intro" "value" .Data.page.IntroMD "rows" 8 "tall" false "upload" (print "/b/" .Blog.Subdomain "/images/upload") "csrf" .CSRF)}} + {{template "editor" (dict "name" "intro" "value" .Data.page.IntroMD "rows" 8 "tall" false "upload" (print "/b/" .Blog.Subdomain "/files/upload") "csrf" .CSRF)}} <label class="check"><input type="checkbox" name="show_in_nav"{{if .Data.page.ShowInNav}} checked{{end}}> {{t "Show in menu"}}</label> <p class="actionrow"> <button type="submit">{{t "Save"}}</button> diff --git a/internal/web/templates/dashboard/post_form.html b/internal/web/templates/dashboard/post_form.html index 8069a9a..d12d15f 100644 --- a/internal/web/templates/dashboard/post_form.html +++ b/internal/web/templates/dashboard/post_form.html @@ -15,7 +15,7 @@ <input type="datetime-local" name="posted_at" value="{{.Data.post.CreatedAt.Format "2006-01-02T15:04"}}" placeholder="YYYY-MM-DD HH:MM"></label> </div> <label for="ed-body">{{t "Content"}} <span class="muted">(Markdown)</span></label> - {{template "editor" (dict "name" "body" "value" .Data.post.BodyMD "rows" 24 "tall" true "upload" (print "/b/" .Blog.Subdomain "/images/upload") "csrf" .CSRF)}} + {{template "editor" (dict "name" "body" "value" .Data.post.BodyMD "rows" 24 "tall" true "upload" (print "/b/" .Blog.Subdomain "/files/upload") "csrf" .CSRF)}} <label class="check"><input type="checkbox" name="published"{{if .Data.post.Published}} checked{{end}}> {{t "Visible on the blog"}}</label> <p class="actionrow"> <button type="submit">{{t "Save"}}</button> diff --git a/internal/web/templates/dashboard/section_form.html b/internal/web/templates/dashboard/section_form.html index bc3ba34..b401d9c 100644 --- a/internal/web/templates/dashboard/section_form.html +++ b/internal/web/templates/dashboard/section_form.html @@ -24,7 +24,7 @@ </div> <p class="muted small">{{t "“Top” of a side column means above its modules, “bottom” below them. If that column is not shown on your blog (see"}} <a href="/b/{{.Blog.Subdomain}}/layout">{{t "Layout"}}</a>) {{t "the announcement moves to the main content instead."}}</p> <label for="ed-body">{{t "Text"}} <span class="muted">(Markdown)</span></label> - {{template "editor" (dict "name" "body" "value" $s.BodyMD "rows" 10 "tall" false "upload" (print "/b/" .Blog.Subdomain "/images/upload") "csrf" .CSRF)}} + {{template "editor" (dict "name" "body" "value" $s.BodyMD "rows" 10 "tall" false "upload" (print "/b/" .Blog.Subdomain "/files/upload") "csrf" .CSRF)}} <label class="check"><input type="checkbox" name="enabled"{{if $s.Enabled}} checked{{end}}> {{t "Shown on the blog"}}</label> <p class="actionrow"> <button type="submit">{{t "Save"}}</button> |
