diff --git a/README.md b/README.md index 8fb96e24..b58131b8 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,8 @@ + [ ] Rename files + [ ] Upload files and drag & drop + [ ] Delete files -+ [ ] Show text files -+ [ ] Show pictures -+ [ ] Show videos -+ [ ] Show music ++ [ ] Download files ++ [ ] Preview files ``` filemanager { diff --git a/assets/public/css/styles.css b/assets/public/css/styles.css index f7f01a96..1804d879 100644 --- a/assets/public/css/styles.css +++ b/assets/public/css/styles.css @@ -1,153 +1,146 @@ -* { padding: 0; margin: 0; } - +* { + padding: 0; + margin: 0; +} body { - font-family: sans-serif; - text-rendering: optimizespeed; + font-family: 'Roboto', sans-serif; + text-rendering: optimizespeed; } - a { - color: #006ed3; - text-decoration: none; + color: #006ed3; + text-decoration: none; } - -a:hover, -h1 a:hover { - color: #319cff; +a:hover, h1 a:hover { + color: #319cff; } - -header, -#summary { - padding-left: 7%; - padding-right: 7%; +header, #summary { + padding-left: 7%; + padding-right: 7%; } - -th:first-child, -td:first-child { - padding-left: 7%; +th:first-child, td:first-child { + padding-left: 1em; } - -th:last-child, -td:last-child { - padding-right: 5%; +th:last-child, td:last-child { + padding-right: 1em; } - header { - background-color: #FFC107; - padding-top: 1.5em; - padding-bottom: 1.5em; + background-color: #03A9F4; + color: #fff; + padding: 1.3em 0; } +header i { +margin: 0 .2em; + +color: rgba(255,255,255,0.3); + +font-style: normal; +} h1 { - font-size: 20px; - font-weight: normal; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; + font-size: 1.5em; + font-weight: normal; + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; } - h1 a { - color: inherit; + color: inherit; } - h1 a:hover { - text-decoration: underline; + text-decoration: underline; } - main { - display: block; + display: block; } - .meta { - font-size: 12px; - font-family: Verdana, sans-serif; - /* border-bottom: 1px solid #9C9C9C; */ - padding-top: 15px; - padding-bottom: 15px; - background-color: #FFA000; - color: #fff; + font-size: 12px; + padding-top: 15px; + padding-bottom: 15px; + background-color: #4FC3F7; + color: #fff; } - .meta-item { - margin-right: 1em; + margin-right: 1em; } - table { - width: 100%; - border-collapse: collapse; + width: 100%; + border-collapse: collapse; } - tr { - border-bottom: 1px dashed #dadada; + border-bottom: 1px dashed #dadada; } - tr:not(:first-child):hover { - background-color: #ffffec; + background-color: #ffffec; } - -th, -td { - text-align: left; - padding: 10px 0; +th, td { + text-align: left; + padding: 1em 0; } - th { - padding-top: 15px; - padding-bottom: 15px; - font-size: 16px; - white-space: nowrap; + padding-top: 15px; + padding-bottom: 15px; + font-size: 16px; + white-space: nowrap; } - th a { - color: black; + color: black; } - th svg { - vertical-align: middle; + vertical-align: middle; } - td { - font-size: 14px; + font-size: 14px; } - td:first-child { - width: 50%; + width: 50%; } - -th:last-child, -td:last-child { - text-align: right; +th:last-child, td:last-child { + text-align: right; } - td:first-child svg { - position: absolute; + position: absolute; } - -td .name, -td .goup { - margin-left: 1.75em; - word-break: break-all; - overflow-wrap: break-word; - white-space: pre-wrap; +td .name, td .goup { + margin-left: 1.1em; + word-break: break-all; + overflow-wrap: break-word; + white-space: pre-wrap; + color: #424242; + vertical-align: middle; } - footer { - padding: 40px 20px; - font-size: 12px; - text-align: center; + padding: 40px 20px; + font-size: 12px; + text-align: center; +} + +.container { + margin: 0 auto; + width: 95%; + max-width: 960px; +} + + +.listing i { + vertical-align: middle; +} + +pre { + border: 1px solid #e6e6e6; + background-color: #f5f5f5; + border-radius: .5em; + padding: 1em; } @media (max-width: 600px) { - .hideable { - display: none; - } - - td:first-child { - width: auto; - } - - th:nth-child(2), - td:nth-child(2) { - padding-right: 5%; - text-align: right; - } + .hideable { + display: none; + } + td:first-child { + width: auto; + } + th:nth-child(2), td:nth-child(2) { + padding-right: 5%; + text-align: right; + } } diff --git a/assets/templates/base.tmpl b/assets/templates/base.tmpl index 6126d05e..f9ebb2fa 100644 --- a/assets/templates/base.tmpl +++ b/assets/templates/base.tmpl @@ -4,16 +4,19 @@ {{.Name}} + - +
-

- {{range $url, $name := .BreadcrumbMap}}{{$name}}{{if ne $url "/"}}/{{end}}{{end}} -

+
+

+ {{range $url, $name := .BreadcrumbMap}}{{if eq $url "/"}}Home{{end}}{{if ne $url "/"}}>{{$name}}{{end}}{{end}} +

+
{{ template "content" .Data }} diff --git a/assets/templates/listing.tmpl b/assets/templates/listing.tmpl index 0fb519df..5c52187d 100644 --- a/assets/templates/listing.tmpl +++ b/assets/templates/listing.tmpl @@ -1,6 +1,6 @@ {{ define "content" }}
-
+
{{.NumDirs}} director{{if eq 1 .NumDirs}}y{{else}}ies{{end}} {{.NumFiles}} file{{if ne 1 .NumFiles}}s{{end}} {{- if ne 0 .ItemsLimitedTo}} @@ -8,8 +8,10 @@ {{- end}}
+ +
- +
@@ -58,9 +60,9 @@
@@ -46,7 +48,7 @@
- Go up + Go up {{- if .IsDir}} - + {{- else}} - + {{- end}} {{.Name}} diff --git a/assets/templates/single.tmpl b/assets/templates/single.tmpl new file mode 100644 index 00000000..29493cb5 --- /dev/null +++ b/assets/templates/single.tmpl @@ -0,0 +1,18 @@ +{{ define "content" }} + +
+ + {{ if eq .Type "image" }} + + {{ else if eq .Type "audio" }} + + {{ else if eq .Type "video" }} + + {{ else}} +
{{ .Content }}
+ {{ end }} + +
+ +{{ end }} diff --git a/binary.go b/binary.go index 49961337..58cbcbae 100644 --- a/binary.go +++ b/binary.go @@ -4,6 +4,7 @@ // assets/public/js/application.js // assets/templates/base.tmpl // assets/templates/listing.tmpl +// assets/templates/single.tmpl // DO NOT EDIT! package filemanager @@ -102,6 +103,24 @@ func templatesListingTmpl() (*asset, error) { return a, err } +// templatesSingleTmpl reads file data from disk. It returns an error on failure. +func templatesSingleTmpl() (*asset, error) { + path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-filemanager\\assets\\templates\\single.tmpl" + name := "templates/single.tmpl" + bytes, err := bindataRead(path, name) + if err != nil { + return nil, err + } + + fi, err := os.Stat(path) + if err != nil { + err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err) + } + + a := &asset{bytes: bytes, info: fi} + return a, err +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -158,6 +177,7 @@ var _bindata = map[string]func() (*asset, error){ "public/js/application.js": publicJsApplicationJs, "templates/base.tmpl": templatesBaseTmpl, "templates/listing.tmpl": templatesListingTmpl, + "templates/single.tmpl": templatesSingleTmpl, } // AssetDir returns the file names below a certain @@ -211,6 +231,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "templates": &bintree{nil, map[string]*bintree{ "base.tmpl": &bintree{templatesBaseTmpl, map[string]*bintree{}}, "listing.tmpl": &bintree{templatesListingTmpl, map[string]*bintree{}}, + "single.tmpl": &bintree{templatesSingleTmpl, map[string]*bintree{}}, }}, }} diff --git a/filemanager.go b/filemanager.go index b34a01c5..e8d2bf2f 100644 --- a/filemanager.go +++ b/filemanager.go @@ -72,7 +72,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err if file.Info.IsDir() { return f.ServeListing(w, r, file.File, c) } - return f.ServeSingleFile(w, r, file.File, c) + return f.ServeSingleFile(w, r, file, c) case http.MethodPost: // Create new file or directory @@ -100,6 +100,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err type InfoRequest struct { Info os.FileInfo File http.File + Path string Code int Err error } @@ -107,9 +108,8 @@ type InfoRequest struct { // GetFileInfo gets the file information and, in case of error, returns the // respective HTTP error code func GetFileInfo(path string, c *Config) *InfoRequest { - request := &InfoRequest{} + request := &InfoRequest{Path: path} request.File, request.Err = c.Root.Open(path) - if request.Err != nil { switch { case os.IsPermission(request.Err): diff --git a/page.go b/page.go index 047a8a73..7606f504 100644 --- a/page.go +++ b/page.go @@ -2,10 +2,10 @@ package filemanager import ( "encoding/json" - "html/template" "log" "net/http" "strings" + "text/template" ) // PageInfo contains the information of a page diff --git a/single.go b/single.go index ad3482de..c1e59676 100644 --- a/single.go +++ b/single.go @@ -1,10 +1,67 @@ package filemanager -import "net/http" +import ( + "encoding/base64" + "html" + "io/ioutil" + "mime" + "net/http" + "path/filepath" + "regexp" +) + +var ( + videoRegex = regexp.MustCompile("video[/]") + audioRegex = regexp.MustCompile("audio[/]") + imageRegex = regexp.MustCompile("image[/]") +) + +type File struct { + *FileInfo + Content string +} // ServeSingleFile redirects the request for the respective method -func (f FileManager) ServeSingleFile(w http.ResponseWriter, r *http.Request, file http.File, c *Config) (int, error) { - w.Header().Set("Content-Type", "text/plain") - w.Write([]byte("Hello")) - return 200, nil +func (f FileManager) ServeSingleFile(w http.ResponseWriter, r *http.Request, file *InfoRequest, c *Config) (int, error) { + fullpath := c.PathScope + file.Path + fullpath = filepath.Clean(fullpath) + + raw, err := ioutil.ReadFile(fullpath) + if err != nil { + return http.StatusInternalServerError, err + } + + base := base64.StdEncoding.EncodeToString(raw) + mimetype := mime.TypeByExtension(filepath.Ext(file.Path)) + data := "data:" + mimetype + ";base64," + base + + page := &Page{ + Info: &PageInfo{ + Name: file.Path, + Path: file.Path, + Data: map[string]string{ + "Type": RetrieveContentType(mimetype), + "Base64": data, + "Content": html.EscapeString(string(raw)), + }, + }, + } + + return page.PrintAsHTML(w, "single") +} + +func RetrieveContentType(name string) string { + if videoRegex.FindString(name) != "" { + return "video" + } + + if audioRegex.FindString(name) != "" { + return "audio" + } + + if imageRegex.FindString(name) != "" { + return "image" + } + + return "text" }