all repos — nirvash @ 119d66cd2726ad7bd8058a96dbe162d27da56a61

modular CMS using the quartzgun library

improve file upload, add directory creation
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmKoCJYACgkQO3+8IhRO
Y5iMRQ/+NWg66KtsDWaNzrW9tipXnGDyxqzmVsuTe+npIjHnSNkk1SN6MKzSigI9
lRwcHnhG07yBlGaHLxAywdylXSewRYb2cmCCeaaRxtbGyrLHPSI72ERDhirS2yBf
FnK+Pguz090mCRTsPl89SqNhSCTn9+p2IR3pEM+Pzo+Jw0R/2bTjO+xLDiJdDvwf
1etD6af1jYgviQq4ZTpd8VS1ZgcIlyw3Y1twPdLYrG7BsPlYVGF9sX8RZOq0ktYr
D0SzeSug/r+qtRSQHonRH4oJ7Yo3jCOmOq/C+bSY+0offcduryuqzQUbAREHrv5K
Wph1IZr+g2QYcMbk60y7e5j4hErN54aggcii5bj/AtuKQa3JR2HC05yCFmavfcC8
JCWuyjQUV2P2IyOHqTU0xgpxwgfD3QARYi0yWfTniADGH3q1/hbORD2RO+yxaOZq
oZYXrJ0iKPLyLLEIJg+I2eWAWS1Zg4LuGM1ejcUtioEx2W93A6g5RBdPVjIl5qbj
fz65MTf8ehX5WoT+GpAok6z9nPWBP+kAAmKV96WS09d+ZhvWSo23jhd7b7/P76Xq
9RQLp8F32+2Dw2f2HQImjYK99tNjIzOiSnIftmkh9gWDVBz6G0RBPnTTKHb/qUf8
fhAbbLEKE2wM/ynNFOLMj9SzThNXJ4qq0wMXk7V7b/0fAt3qXFc=
=fnYl
-----END PGP SIGNATURE-----
commit

119d66cd2726ad7bd8058a96dbe162d27da56a61

parent

6f663500cce5096b097cae74736e003c56b0c4e9

M archetype/fileManager.goarchetype/fileManager.go

@@ -32,11 +32,10 @@ }

type FileManager interface { Init(cfg *Config) error - // ListTree() FileListing ListSubTree(root string) FileListing GetFileData(slug string) FileData AddFile(path string, req *http.Request) error - // MkDir(path string) error + MkDir(path, newDir string) error Remove(path string) error // Rename(old, new string) error }

@@ -169,3 +168,27 @@

dest.Write(fileData) 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) +}
M nirvash.gonirvash.go

@@ -292,10 +292,36 @@ "/login"),

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: - // 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 // move-choose POST uses a form to navigate through the file tree // - to use links to navigate, destination location uses the slug,
M static/style.cssstatic/style.css

@@ -180,7 +180,7 @@ padding-left: 8px;

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; overflow-x: hidden; width: 80%;

@@ -196,13 +196,13 @@ span.adapter-error {

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%; color: lightgray; 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; margin: 0; margin-top: 0.2em;

@@ -217,16 +217,18 @@ box-sizing: border-box;

} .uploader label { + position:relative; text-transform: uppercase; display: inline-block; transition: background 1s, color 1s; + z-index: 2; } .upload-warning { 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; width: 100%;

@@ -265,7 +267,7 @@ form.configurator input, form.configurator textarea {

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-right: 0; font-size: 150%;

@@ -273,7 +275,7 @@ text-transform: uppercase;

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; color: black; }

@@ -286,6 +288,7 @@ }

.page-list ul { margin: 0; + padding: 0; position: relative; list-style: none; z-index: 1;

@@ -320,5 +323,31 @@ width: 16px;

} 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; +}
A templates/file_mkdir.html

@@ -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" . }}
A templates/file_mkdir_process.html

@@ -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" . }}
M templates/file_upload.htmltemplates/file_upload.html

@@ -1,8 +1,16 @@

{{ $slug := ((.Context).Value "params").Slug }} {{ $csrfToken := (.Context).Value "csrfToken" }} +{{ $fileListing := ((.Context).Value "file-manager").ListSubtree $slug }} {{ template "header" . }} +{{ if ($fileListing).Error }} +<h2>Error</h2> + +<span class="adapter-error">{{($fileListing).Error}}</span> + +{{ else }} + <h2>File Upload</h2> <form class="uploader" enctype="multipart/form-data" method="POST" action="/upload-process/{{$slug}}">

@@ -10,11 +18,14 @@ <input hidden type="text" name="csrfToken" value="{{$csrfToken}}"/>

<span>Uploading file to /{{$slug}}</span><br/> <span class="upload-warning">(file with the same name as your upload will be overwritten!)</span><br/> - - <label>Select File<br/> - <input required type="file" name="file"/> - </label> + <div class="upload-wrapper"> + <label for="file-upload">Select File</label><br/> + <input required type="file" id="file-upload" name="file"/> + <div class="file-feedback"></div> + </div> <input type="submit" value="Upload"/> </form> -{{ template "footer" . }}+{{ end }} + +{{ template "footer" . }}