improve file upload, add directory creation
This commit is contained in:
parent
6f663500cc
commit
119d66cd27
6 changed files with 140 additions and 18 deletions
|
@ -32,11 +32,10 @@ type FileListing struct {
|
||||||
|
|
||||||
type FileManager interface {
|
type FileManager interface {
|
||||||
Init(cfg *Config) error
|
Init(cfg *Config) error
|
||||||
// ListTree() FileListing
|
|
||||||
ListSubTree(root string) FileListing
|
ListSubTree(root string) FileListing
|
||||||
GetFileData(slug string) FileData
|
GetFileData(slug string) FileData
|
||||||
AddFile(path string, req *http.Request) error
|
AddFile(path string, req *http.Request) error
|
||||||
// MkDir(path string) error
|
MkDir(path, newDir string) error
|
||||||
Remove(path string) error
|
Remove(path string) error
|
||||||
// Rename(old, new string) error
|
// Rename(old, new string) error
|
||||||
}
|
}
|
||||||
|
@ -169,3 +168,27 @@ func (self *SimpleFileManager) AddFile(path string, req *http.Request) error {
|
||||||
dest.Write(fileData)
|
dest.Write(fileData)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *SimpleFileManager) MkDir(path, newDir string) error {
|
||||||
|
fullPath := filepath.Join(self.Root, path)
|
||||||
|
if !strings.HasPrefix(fullPath, self.Root) {
|
||||||
|
return errors.New("You cannot escape!")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(newDir, "/") || strings.Contains(newDir, "\\") {
|
||||||
|
return errors.New("Cannot create nested directories at once")
|
||||||
|
}
|
||||||
|
|
||||||
|
newDirPath := filepath.Join(fullPath, newDir)
|
||||||
|
_, err = os.Stat(newDirPath)
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return errors.New("Directory exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Mkdir(newDirPath, 0755)
|
||||||
|
}
|
||||||
|
|
32
nirvash.go
32
nirvash.go
|
@ -292,10 +292,36 @@ func main() {
|
||||||
udb,
|
udb,
|
||||||
"/")))
|
"/")))
|
||||||
|
|
||||||
|
rtr.Get(
|
||||||
|
`/mkdir/(?P<Slug>.*)`,
|
||||||
|
Fortify(
|
||||||
|
Protected(
|
||||||
|
WithFileManager(
|
||||||
|
renderer.Template(
|
||||||
|
pathConcat(templateRoot, "file_mkdir.html"),
|
||||||
|
pathConcat(templateRoot, "header.html"),
|
||||||
|
pathConcat(templateRoot, "footer.html")),
|
||||||
|
fileManager),
|
||||||
|
http.MethodGet,
|
||||||
|
udb,
|
||||||
|
"/login")))
|
||||||
|
|
||||||
|
rtr.Post(
|
||||||
|
`/mkdir-process/(?P<Slug>.*)`,
|
||||||
|
Defend(
|
||||||
|
Protected(
|
||||||
|
WithFileManager(
|
||||||
|
renderer.Template(
|
||||||
|
pathConcat(templateRoot, "file_mkdir_process.html"),
|
||||||
|
pathConcat(templateRoot, "header.html"),
|
||||||
|
pathConcat(templateRoot, "footer.html")),
|
||||||
|
fileManager),
|
||||||
|
http.MethodGet,
|
||||||
|
udb,
|
||||||
|
"/login"),
|
||||||
|
udb,
|
||||||
|
"/"))
|
||||||
// TODO:
|
// TODO:
|
||||||
// file upload GET contains form for file upload in current directory
|
|
||||||
// file upload POST performs the action of creating/overwriting
|
|
||||||
// add directory GET contains the form for directory creation
|
|
||||||
// add directory POST performs the action of creating directory
|
// add directory POST performs the action of creating directory
|
||||||
// move-choose POST uses a form to navigate through the file tree
|
// move-choose POST uses a form to navigate through the file tree
|
||||||
// - to use links to navigate, destination location uses the slug,
|
// - to use links to navigate, destination location uses the slug,
|
||||||
|
|
|
@ -180,7 +180,7 @@ h2 {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-list, form.editor, form.build, form.configurator, span.adapter-error, span.adapter-success, .file-move, .danger-zone, .uploader {
|
.page-list, form.editor, form.build, form.configurator, span.adapter-error, span.adapter-success, .file-move, .danger-zone, .uploader, .mkdir {
|
||||||
display: block;
|
display: block;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
@ -196,13 +196,13 @@ span.adapter-error {
|
||||||
border-bottom: 2px solid crimson;
|
border-bottom: 2px solid crimson;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor label, form.build label, .danger-zone label, form.configurator label, form.file-move label {
|
form.editor label, form.build label, .danger-zone label, form.configurator label, form.file-move label, .mkdir label {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
color: lightgray;
|
color: lightgray;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input, form.build input, form.editor textarea, form.configurator input, form.configurator textarea, .danger-zone input[type="submit"], .file-move input[type="submit"], .uploader label, .uploader input[type="submit"] {
|
form.editor input, form.build input, form.editor textarea, form.configurator input, form.configurator textarea, .danger-zone input[type="submit"], .file-move input[type="submit"], .uploader label, .uploader input[type="submit"], .mkdir input {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: 0.2em;
|
margin-top: 0.2em;
|
||||||
|
@ -217,16 +217,18 @@ form.editor input, form.build input, form.editor textarea, form.configurator inp
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploader label {
|
.uploader label {
|
||||||
|
position:relative;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
transition: background 1s, color 1s;
|
transition: background 1s, color 1s;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-warning {
|
.upload-warning {
|
||||||
border-bottom: 2px solid crimson;
|
border-bottom: 2px solid crimson;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input[type="text"], form.configurator input[type="text"], form.configurator input[type="number"], form.build input[type="text"] {
|
form.editor input[type="text"], form.configurator input[type="text"], form.configurator input[type="number"], form.build input[type="text"], .mkdir input[type="text"] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@ -265,7 +267,7 @@ form.configurator input, form.configurator textarea {
|
||||||
font-size: 125%;
|
font-size: 125%;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input[type="submit"], form.build input[type="submit"], .danger-zone input[type="submit"], form.configurator input[type="submit"], .file-move input[type="submit"], .uploader input[type="submit"] {
|
form.editor input[type="submit"], form.build input[type="submit"], .danger-zone input[type="submit"], form.configurator input[type="submit"], .file-move input[type="submit"], .uploader input[type="submit"], .mkdir input[type="submit"] {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
|
@ -273,7 +275,7 @@ form.editor input[type="submit"], form.build input[type="submit"], .danger-zone
|
||||||
transition: background 1s, color 1s;
|
transition: background 1s, color 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .danger-zone input[type="submit"]:hover, form.configurator input[type="submit"]:hover, .file-move input[type="submit"]:hover, .uploader input[type="submit"]:hover, .uploader label:hover {
|
form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .danger-zone input[type="submit"]:hover, form.configurator input[type="submit"]:hover, .file-move input[type="submit"]:hover, .uploader input[type="submit"]:hover, .uploader label:hover, .mkdir input[type="submit"]:hover {
|
||||||
background: lightgray;
|
background: lightgray;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
@ -286,6 +288,7 @@ form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .d
|
||||||
|
|
||||||
.page-list ul {
|
.page-list ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -320,5 +323,31 @@ form input[readonly] {
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
display: none;
|
opacity: 0;
|
||||||
}
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 5px;
|
||||||
|
height: 50px;
|
||||||
|
width: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type="file"]:not(:valid) + .file-feedback::after {
|
||||||
|
content: "No file selected";
|
||||||
|
height: 1em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="file"]:valid + .file-feedback::after {
|
||||||
|
content: "File selected";
|
||||||
|
height: 1em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-feedback {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
18
templates/file_mkdir.html
Normal file
18
templates/file_mkdir.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{{ $slug := ((.Context).Value "params").Slug }}
|
||||||
|
{{ $csrfToken := (.Context).Value "csrfToken" }}
|
||||||
|
|
||||||
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
<h2>Directory Creation</h2>
|
||||||
|
|
||||||
|
<form class="mkdir" method="POST" action="/mkdir-process/{{$slug}}">
|
||||||
|
<input hidden type="text" name="csrfToken" value="{{$csrfToken}}"/>
|
||||||
|
<span>Creating new directory in /{{$slug}}</span><br/>
|
||||||
|
|
||||||
|
<label for="dirname-input">New Directory Name</label>
|
||||||
|
<input required type="text" name="dirname" id="dirname-input"/>
|
||||||
|
|
||||||
|
<input type="submit" value="Create"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
15
templates/file_mkdir_process.html
Normal file
15
templates/file_mkdir_process.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{{ $slug := ((.Context).Value "params").Slug }}
|
||||||
|
{{ $newDir := .FormValue "dirname" }}
|
||||||
|
{{ $mkdirError := ((.Context).Value "file-manager").MkDir $slug $newDir }}
|
||||||
|
|
||||||
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
{{ if $mkdirError }}
|
||||||
|
<h2>Directory Creation Error</h2>
|
||||||
|
<span class="adapter-error">{{($mkdirError).Error}}</span>
|
||||||
|
{{ else }}
|
||||||
|
<h2>Directory Created</h2>
|
||||||
|
<span class="adapter-success">The directory has been created successfully</span>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
|
@ -1,8 +1,16 @@
|
||||||
{{ $slug := ((.Context).Value "params").Slug }}
|
{{ $slug := ((.Context).Value "params").Slug }}
|
||||||
{{ $csrfToken := (.Context).Value "csrfToken" }}
|
{{ $csrfToken := (.Context).Value "csrfToken" }}
|
||||||
|
{{ $fileListing := ((.Context).Value "file-manager").ListSubtree $slug }}
|
||||||
|
|
||||||
{{ template "header" . }}
|
{{ template "header" . }}
|
||||||
|
|
||||||
|
{{ if ($fileListing).Error }}
|
||||||
|
<h2>Error</h2>
|
||||||
|
|
||||||
|
<span class="adapter-error">{{($fileListing).Error}}</span>
|
||||||
|
|
||||||
|
{{ else }}
|
||||||
|
|
||||||
<h2>File Upload</h2>
|
<h2>File Upload</h2>
|
||||||
|
|
||||||
<form class="uploader" enctype="multipart/form-data" method="POST" action="/upload-process/{{$slug}}">
|
<form class="uploader" enctype="multipart/form-data" method="POST" action="/upload-process/{{$slug}}">
|
||||||
|
@ -10,11 +18,14 @@
|
||||||
|
|
||||||
<span>Uploading file to /{{$slug}}</span><br/>
|
<span>Uploading file to /{{$slug}}</span><br/>
|
||||||
<span class="upload-warning">(file with the same name as your upload will be overwritten!)</span><br/>
|
<span class="upload-warning">(file with the same name as your upload will be overwritten!)</span><br/>
|
||||||
|
<div class="upload-wrapper">
|
||||||
<label>Select File<br/>
|
<label for="file-upload">Select File</label><br/>
|
||||||
<input required type="file" name="file"/>
|
<input required type="file" id="file-upload" name="file"/>
|
||||||
</label>
|
<div class="file-feedback"></div>
|
||||||
|
</div>
|
||||||
<input type="submit" value="Upload"/>
|
<input type="submit" value="Upload"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{ template "footer" . }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ template "footer" . }}
|
||||||
|
|
Loading…
Reference in a new issue