File edit modal

This commit is contained in:
2021-02-23 16:50:13 +01:00
parent 346fa355c4
commit 5e8598f112
10 changed files with 180 additions and 290 deletions

View File

@@ -7,14 +7,19 @@ function EditWindow() {
)
let clone = document.getElementById("tpl_edit_file").content.cloneNode(true)
this.notification = clone.querySelector(".edit_file_notification")
this.fileNameField = clone.querySelector(".edit_file_name_field")
clone.querySelector(".btn_delete_file").addEventListener("click", () => { this.deleteFile() })
clone.querySelector(".edit_file_name_form").addEventListener("submit", e => { this.renameFile(e) })
this.modal.setBody(clone)
this.btnEdit = document.getElementById("btn_edit")
this.btnEdit.addEventListener("click", () => { this.toggle() })
}
EditWindow.prototype.toggle = function() {
EditWindow.prototype.toggle = function () {
if (this.visible) {
this.modal.close()
this.btnEdit.classList.remove("button_highlight")
@@ -26,9 +31,11 @@ EditWindow.prototype.toggle = function() {
}
}
EditWindow.prototype.setFile = function(file) {
EditWindow.prototype.setFile = function (file) {
this.file = file
this.modal.setTitle("Editing "+file.name)
this.modal.setTitle("Editing " + file.name)
this.notification.style.display = "none"
this.fileNameField.value = file.name
if (this.file.can_edit) {
this.btnEdit.style.display = ""
@@ -37,16 +44,44 @@ EditWindow.prototype.setFile = function(file) {
}
}
EditWindow.prototype.deleteFile = function() {
if (!confirm("Are you sure you want to delete '"+this.file.name+"'?")) {
EditWindow.prototype.notify = function (success, content) {
this.notification.style.display = ""
this.notification.classList = "edit_file_notification " + (success ? "highlight_green" : "highlight_red")
this.notification.innerHTML = content
}
EditWindow.prototype.deleteFile = async function () {
if (!confirm("Are you sure you want to delete '" + this.file.name + "'?")) {
return
}
fetch(
this.file.get_href, {method: "DELETE"}
).then(resp => {
this.modal.setBody(document.createTextNode("This file has been deleted"))
}).catch(err => {
alert("Error! Could not delete file")
})
try {
const resp = await fetch(this.file.get_href, { method: "DELETE" });
if (resp.status >= 400) {
throw (await resp.json()).message
}
this.notify(true, "This file has been deleted, you can close the page")
} catch (err) {
this.notify(false, "Could not delete file: " + err)
}
}
EditWindow.prototype.renameFile = async function (e) {
e.preventDefault()
const form = new FormData()
form.append("action", "rename")
form.append("name", this.fileNameField.value)
try {
const resp = await fetch(this.file.get_href, { method: "POST", body: form });
if (resp.status >= 400) {
throw (await resp.json()).message
}
this.notify(true, "File name has been changed. Reload the page to see the changes")
} catch (err) {
this.notify(false, "Could not change file name: " + err)
}
}

View File

@@ -59,11 +59,11 @@ function Viewer(type, viewToken, data) {
this.initialized = true
}
Viewer.prototype.getFile = function() {
Viewer.prototype.getFile = function () {
return this.file
}
Viewer.prototype.setFile = function(file) {
Viewer.prototype.setFile = function (file) {
this.file = file
if (this.isList) {
@@ -85,10 +85,9 @@ Viewer.prototype.setFile = function(file) {
if (file.view_href !== "") {
fetch(file.view_href, {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "token="+this.viewToken
}
)
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "token=" + this.viewToken
})
}
// Clean up the previous viewer if possible
@@ -108,8 +107,8 @@ Viewer.prototype.setFile = function(file) {
}
if (file.abuse_type !== "") {
this.viewerScript = new AbuseViewer(this,file)
}else if (file.mime_type.startsWith("image")) {
this.viewerScript = new AbuseViewer(this, file)
} else if (file.mime_type.startsWith("image")) {
this.viewerScript = new ImageViewer(this, file)
} else if (
file.mime_type.startsWith("video") ||
@@ -140,7 +139,7 @@ Viewer.prototype.setFile = function(file) {
this.viewerScript.render(this.divFilepreview)
}
Viewer.prototype.renderSponsors = function() {
Viewer.prototype.renderSponsors = function () {
// Check if the ad is enabled
if (document.querySelector(".sponsors_banner") == null) { return }
@@ -151,10 +150,10 @@ Viewer.prototype.renderSponsors = function() {
let bannerHeight = document.querySelector(".sponsors_banner").offsetHeight
if (window.innerWidth < bannerWidth) {
scaleWidth = window.innerWidth/bannerWidth
scaleWidth = window.innerWidth / bannerWidth
}
if (window.innerHeight < minWindowHeight) {
scaleHeight = window.innerHeight/minWindowHeight
scaleHeight = window.innerHeight / minWindowHeight
}
// The smaller scale is the scale we'll use
@@ -165,19 +164,22 @@ Viewer.prototype.renderSponsors = function() {
// width of the viewport - the width of the ad to calculate the amount of
// pixels around the ad. We multiply the ad size by the scale we calcualted
// to account for the smaller size.
let offset = (window.innerWidth - (bannerWidth*scale)) / 2
let offset = (window.innerWidth - (bannerWidth * scale)) / 2
document.querySelector(".sponsors").style.height = (bannerHeight*scale)+"px"
document.querySelector(".sponsors_banner").style.marginLeft = offset+"px"
document.querySelector(".sponsors_banner").style.transform = "scale("+scale+")"
document.querySelector(".sponsors").style.height = (bannerHeight * scale) + "px"
document.querySelector(".sponsors_banner").style.marginLeft = offset + "px"
document.querySelector(".sponsors_banner").style.transform = "scale(" + scale + ")"
}
Viewer.prototype.keyboardEvent = function(evt) {
Viewer.prototype.keyboardEvent = function (evt) {
if (evt.ctrlKey || evt.altKey || evt.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts
}
if (document.activeElement.type && document.activeElement.type === "text") {
return // Prevent shortcuts from interfering with input fields
}
console.debug("Key pressed: "+evt.keyCode)
console.debug("Key pressed: " + evt.keyCode)
switch (evt.keyCode) {
case 65: // A or left arrow key go to previous file
case 37:
@@ -231,13 +233,13 @@ function escapeHTML(str) {
function fileFromAPIResp(resp) {
resp.date_created = new Date(resp.date_upload)
resp.date_last_view = new Date(resp.date_last_view)
resp.icon_href = apiEndpoint+"/file/"+resp.id+"/thumbnail"
resp.get_href = apiEndpoint+"/file/"+resp.id
resp.download_href = apiEndpoint+"/file/"+resp.id+"?download"
resp.view_href = apiEndpoint+"/file/"+resp.id+"/view"
resp.timeseries_href = apiEndpoint+"/file/"+resp.id+"/timeseries"
resp.stats_href = apiEndpoint+"/file/"+resp.id+"/stats"
resp.link = domainURL()+"/u/"+resp.id
resp.icon_href = apiEndpoint + "/file/" + resp.id + "/thumbnail"
resp.get_href = apiEndpoint + "/file/" + resp.id
resp.download_href = apiEndpoint + "/file/" + resp.id + "?download"
resp.view_href = apiEndpoint + "/file/" + resp.id + "/view"
resp.timeseries_href = apiEndpoint + "/file/" + resp.id + "/timeseries"
resp.stats_href = apiEndpoint + "/file/" + resp.id + "/stats"
resp.link = domainURL() + "/u/" + resp.id
if (resp.description === undefined) {
resp.description = ""
}
@@ -247,11 +249,11 @@ function fileFromAPIResp(resp) {
function fileFromSkyNet(resp) {
let file = fileFromAPIResp(resp)
file.icon_href = "/res/img/mime/empty.png"
file.get_href = "https://skydrain.net/file/"+resp.id
file.download_href = "https://skydrain.net/file/"+resp.id+"?attachment=1"
file.get_href = "https://siasky.net/file/" + resp.id
file.download_href = "https://siasky.net/file/" + resp.id + "?attachment=1"
file.view_href = ""
file.timeseries_href = ""
file.stats_href = ""
file.link = domainURL()+"/s/"+resp.id
file.link = domainURL() + "/s/" + resp.id
return file
}

View File

@@ -244,7 +244,15 @@
</template>
<template id="tpl_edit_file">
<h3>Delete file</h3>
<div class="edit_file_notification" style="display: none;"></div>
<h3>Rename</h3>
<form class="edit_file_name_form" style="display: flex; width: 100%">
<input class="edit_file_name_field" type="text" style="flex: 1 1 auto"/>
<button class="edit_file_name_submit" role="submit" style="flex: 0 0 auto">
<i class="icon">save</i> Save
</button>
</form>
<h3>Delete</h3>
<p>
When you delete a file it cannot be recovered.
Nobody will be able to download it and the link will

View File

@@ -1,40 +1,33 @@
{{ define "filesystem" }}<!DOCTYPE html>
{{define "filesystem"}}
<!DOCTYPE html>
<html lang="en">
<head>
{{template "meta_tags" .Title}}
<title>{{.Title}}</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{template "user_style" .}}
<style>{{template "file_manager.css" .}}</style>
<script>var apiEndpoint = '{{.APIEndpoint}}';</script>
<link rel="icon" sizes="32x32" href="/res/img/pixeldrain_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/pixeldrain_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/pixeldrain_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<meta name="theme-color" content="#75AD38"/>
{{ template "opengraph" .OGData }}
<script>
const initialNode = {{.Other}};
window.api_endpoint = '{{.APIEndpoint}}';
</script>
<link rel='stylesheet' href='/res/svelte/filesystem.css'>
<script defer src='/res/svelte/filesystem.js'></script>
</head>
<body>
{{template "page_top" .}}
<h1>{{ .Title}}</h1>
<div class="page_content">
<div class="limit_width">
<h2>
<a href="/fs">Buckets</a>
{{ range $node := .Other.Path }}
/ <a href="{{ $node.HREF }}">{{ $node.Name }}</a>
{{ end }}
{{ if ne .Other.Base.Name "" }}
/ {{ .Other.Base.Name }}
{{ end }}
</h2>
{{ range $node := .Other.Children }}
<a class="node" href="{{ $node.HREF }}"><div>
<img src="{{ $node.Icon }}"/>
<span>{{ $node.Name }}</span>
</div></a>
{{ end }}
</div>
</div>
{{template "page_bottom" .}}
{{template "analytics"}}
</body>
<body></body>
</html>
{{ end }}
{{end}}

View File

@@ -1,33 +0,0 @@
{{define "filesystem_svelte"}}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{.Title}}</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{template "user_style" .}}
<link rel="icon" sizes="32x32" href="/res/img/pixeldrain_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/pixeldrain_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/pixeldrain_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<meta name="theme-color" content="#75AD38"/>
{{ template "opengraph" .OGData }}
<script>
const initialNode = {{.Other}};
window.api_endpoint = '{{.APIEndpoint}}';
</script>
<link rel='stylesheet' href='/res/svelte/filesystem.css'>
<script defer src='/res/svelte/filesystem.js'></script>
</head>
<body></body>
</html>
{{end}}

View File

@@ -13,8 +13,7 @@ let new_bucket_name
const get_buckets = async () => {
loading = true;
try {
let resp = await fs_get_buckets();
buckets = resp.buckets;
buckets = await fs_get_buckets();
} catch (err) {
alert(err);
} finally {

View File

@@ -28,7 +28,7 @@ func (wc *WebController) adminGlobalsForm(td *TemplateData, r *http.Request) (f
return f
}
var globalsMap = make(map[string]string)
for _, v := range globals.Globals {
for _, v := range globals {
f.Fields = append(f.Fields, Field{
Name: v.Key,
DefaultValue: v.Value,

View File

@@ -225,7 +225,7 @@ func (wc *WebController) serveSkynetViewer(w http.ResponseWriter, r *http.Reques
// Get the first few bytes from the file to probe the content type and
// length
rq, err := http.NewRequest("GET", "https://skydrain.net/file/"+p.ByName("id"), nil)
rq, err := http.NewRequest("GET", "https://siasky.net/file/"+p.ByName("id"), nil)
if err != nil {
log.Warn("Failed to make request to sia portal: %s", err)
w.WriteHeader(http.StatusInternalServerError)

View File

@@ -3,123 +3,12 @@ package webcontroller
import (
"fmt"
"net/http"
"sort"
"strings"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
"github.com/Fornaxian/log"
"github.com/julienschmidt/httprouter"
)
type filesystemPath struct {
Path []filesystemNode
Base filesystemNode
Children []filesystemNode
}
type filesystemNode struct {
HREF string
Icon string
Name string
Type string
FileSize int64
FileType string
}
func convFilesystemNode(bucketID string, v apitype.FilesystemNode) (node filesystemNode) {
node = filesystemNode{
HREF: "/fs/" + bucketID + v.Path,
Type: v.Type,
Name: v.Name,
FileSize: v.FileSize,
FileType: v.FileType,
}
if node.Type == "dir" {
node.Icon = "/res/img/mime/folder.png"
} else {
node.Icon = "/res/img/mime/empty.png"
}
return node
}
func (wc *WebController) serveFilesystem(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var err error
var td = wc.newTemplateData(w, r)
var path = strings.TrimPrefix(p.ByName("path"), "/")
var fsPath filesystemPath
if path == "" {
buckets, err := td.PixelAPI.GetFilesystemBuckets()
if err != nil {
if err.Error() == "not_found" {
wc.templates.Get().ExecuteTemplate(w, "404", td)
} else if err.Error() == "authentication_required" {
http.Redirect(w, r, "/login", http.StatusSeeOther)
} else {
log.Error("Failed to get buckets: %s", err)
wc.templates.Get().ExecuteTemplate(w, "500", td)
}
return
}
for _, v := range buckets.Buckets {
fsPath.Children = append(fsPath.Children, filesystemNode{
HREF: "/fs/" + v.ID,
Icon: "/res/img/mime/folder-remote.png",
Type: "dir",
Name: v.Name,
FileSize: 0,
FileType: "inode/directory",
})
}
} else {
log.Info("getting path %s", path)
node, err := td.PixelAPI.GetFilesystemPath(path)
if err != nil {
if err.Error() == "not_found" || err.Error() == "path_not_found" {
wc.templates.Get().ExecuteTemplate(w, "404", td)
} else {
log.Error("Failed to get path: %s", err)
wc.templates.Get().ExecuteTemplate(w, "500", td)
}
return
}
if node.Base.Type == "file" {
http.Redirect(w, r, "/api/filesystem/"+path, http.StatusSeeOther)
return
}
for _, v := range node.Parents {
fsPath.Path = append(fsPath.Path, convFilesystemNode(node.Bucket.ID, v))
}
fsPath.Base = convFilesystemNode(node.Bucket.ID, node.Base)
for _, v := range node.Base.Children {
fsPath.Children = append(fsPath.Children, convFilesystemNode(node.Bucket.ID, v))
}
}
sort.Slice(fsPath.Children, func(i, j int) (less bool) {
// Directories always come first. Make sure we're comparing apples with
// apples
if fsPath.Children[i].Type != fsPath.Children[j].Type {
return fsPath.Children[i].Type == "dir"
}
return fsPath.Children[i].Name < fsPath.Children[j].Name
})
td.Title = "Filesystem"
td.Other = fsPath
err = wc.templates.Get().ExecuteTemplate(w, "filesystem", td)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template filesystem: %s", err)
}
}
func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var err error
var td = wc.newTemplateData(w, r)
@@ -138,7 +27,7 @@ func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request,
td.Title = fmt.Sprintf("%s ~ pixeldrain", node.Base.Name)
td.Other = node
err = wc.templates.Get().ExecuteTemplate(w, "filesystem_svelte", td)
err = wc.templates.Get().ExecuteTemplate(w, "filesystem", td)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template filesystem: %s", err)
}

View File

@@ -114,6 +114,7 @@ func New(
{GET, "u/:id" /* */, wc.serveFileViewer},
{GET, "u/:id/preview" /* */, wc.serveFilePreview},
{GET, "l/:id" /* */, wc.serveListViewer},
{GET, "d/*path" /* */, wc.serveDirectory},
{GET, "s/:id" /* */, wc.serveSkynetViewer},
{GET, "t" /* */, wc.serveTemplate("text_editor", false)},
{GET, "donation" /* */, wc.serveMarkdown("donation.md", false)},
@@ -150,10 +151,6 @@ func New(
{GET, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, false)},
{PST, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, false)},
// Filesystem
{GET, "fs/*path", wc.serveFilesystem},
{GET, "d/*path", wc.serveDirectory},
{GET, "patreon_activate" /* */, wc.serveForm(wc.patreonLinkForm, true)},
{PST, "patreon_activate" /* */, wc.serveForm(wc.patreonLinkForm, true)},