some bug fixes

pull/144/head
Henrique Dias 2016-06-14 20:33:59 +01:00
parent e137f46169
commit d6c81ec07d
11 changed files with 156 additions and 124 deletions

View File

@ -578,6 +578,10 @@ header h1 {
margin: 0;
font-size: 2em;
}
header a,
header a:hover {
color: inherit;
}
header p {
font-size: 1.5em;
}

View File

@ -2,6 +2,13 @@
var selectedItems = [];
Array.prototype.removeElement = function(element) {
var i = this.indexOf(element);
if (i != -1) {
this.splice(i, 1);
}
}
document.addEventListener("DOMContentLoaded", function(event) {
var items = document.getElementsByClassName('item');
Array.from(items).forEach(link => {
@ -12,10 +19,7 @@ document.addEventListener("DOMContentLoaded", function(event) {
selectedItems.push(url);
} else {
link.classList.remove('selected');
var i = selectedItems.indexOf(url);
if (i != -1) {
selectedItems.splice(i, 1);
}
selectedItems.removeElement(url);
}
var event = new CustomEvent('changed-selected');
@ -24,49 +28,69 @@ document.addEventListener("DOMContentLoaded", function(event) {
});
});
document.getElementById("back").addEventListener("click", backEvent);
document.getElementById("open").addEventListener("click", openEvent);
if (document.getElementById("back")) {
document.getElementById("back").addEventListener("click", backEvent)
};
document.getElementById("delete").addEventListener("click", deleteEvent);
document.getElementById("download").addEventListener("click", downloadEvent);
return false;
});
var backEvent = function(event) {
var items = document.getElementsByClassName('item');
Array.from(items).forEach(link => {
link.classList.remove('selected');
});
selectedItems = [];
var openEvent = function(event) {
if (selectedItems.length) {
var event = new CustomEvent('changed-selected');
document.dispatchEvent(event);
return false;
return false;
}
window.open(window.location + "?raw=true");
return false;
}
var backEvent = function(event) {
var items = document.getElementsByClassName('item');
Array.from(items).forEach(link => {
link.classList.remove('selected');
});
selectedItems = [];
var event = new CustomEvent('changed-selected');
document.dispatchEvent(event);
return false;
}
var deleteEvent = function(event) {
Array.from(selectedItems).forEach(item => {
var request = new XMLHttpRequest();
request.open("DELETE", item);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status != 200) {
alert("something wrong happened!");
return false;
}
Array.from(selectedItems).forEach(item => {
var request = new XMLHttpRequest();
request.open("DELETE", item);
request.send();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status != 200) {
alert("something wrong happened!");
return false;
}
alert(item + " deleted");
// Add removing animation
}
}
});
return false;
selectedItems.removeElement(item);
alert(item + " deleted");
// Add removing animation
}
}
});
return false;
}
var downloadEvent = function(event) {
Array.from(selectedItems).forEach(item => {
window.open(item + "?download=true");
});
return false;
if (selectedItems.length) {
Array.from(selectedItems).forEach(item => {
window.open(item + "?download=true");
});
return false;
}
window.open(window.location + "?download=true");
return false;
}
document.addEventListener("changed-selected", function(event) {

View File

@ -0,0 +1,8 @@
{{ define "actions" }}
<div>
<div class="action" id="open"><i class="material-icons">open_in_new</i></div>
<div class="action" id="rename"><i class="material-icons">mode_edit</i></div>
<div class="action" id="download"><i class="material-icons">file_download</i></div>
<div class="action" id="delete"><i class="material-icons">delete</i></div>
</div>
{{ end }}

View File

@ -13,31 +13,32 @@
<header>
<div>
{{ $lnk := .PreviousLink }}
{{ if ne $lnk ""}}<a href="../../{{.PreviousLink}}"><div class="action" id="prev"><i class="material-icons">subdirectory_arrow_left</i></div></a>{{ else }}
{{ if ne $lnk ""}}<a href="{{ if eq $lnk "/" }}/{{else }}../../{{.PreviousLink}}{{ end }}"><div class="action" id="prev"><i class="material-icons">subdirectory_arrow_left</i></div></a>{{ else }}
<div class="action disabled" id="prev"><i class="material-icons">subdirectory_arrow_left</i></div>{{ end }}
<p><a href="{{ if eq .Config.BaseURL "" }}/{{ else }}{{ .Config.BaseURL }}{{ end }}">File Manager</a> {{ if ne .Name "."}}<i class="material-icons">chevron_right</i> {{ .Name }}</p>{{ end }}
<p><a href="{{ if eq .Config.BaseURL "" }}/{{ else }}{{ .Config.BaseURL }}{{ end }}">File Manager</a> {{ if ne .Name "/"}}<i class="material-icons">chevron_right</i> {{ .Name }}</p>{{ end }}
</div>
{{ if .IsDir}}
<div>
<form>
<i class="material-icons">search</i> <input type="text" placeholder="Search">
</form>
<div class="action" id="view"><i class="material-icons">view_comfy</i></div>
<div class="action" id="view"><i class="material-icons">view_headline</i></div>
<div class="action" id="upload"><i class="material-icons">file_upload</i></div>
</div>
{{ else }}
{{ template "actions" . }}
{{ end }}
</header>
{{ if .IsDir }}
<div id="toolbar">
<div>
<div class="action" id="back"><i class="material-icons">arrow_back</i></div>
<p><span id="selected-number">0</span> selected.</p>
</div>
<div>
<div class="action" id="open"><i class="material-icons">open_in_new</i></div>
<div class="action" id="rename"><i class="material-icons">mode_edit</i></div>
<div class="action" id="download"><i class="material-icons">file_download</i></div>
<div class="action" id="delete"><i class="material-icons">delete</i></div>
</div>
{{ template "actions" . }}
</div>
{{ end }}
<main>
{{ template "content" .Data }}

View File

@ -24,64 +24,5 @@
</div>
{{- end}}
</div>
<!--
<table class="container" aria-describedby="summary">
<thead>
<tr>
<th>
{{- if and (eq .Sort "name") (ne .Order "desc")}}
<a href="?sort=name&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "name") (ne .Order "asc")}}
<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=name&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Name</a>
{{- end}}
</th>
<th>
{{- if and (eq .Sort "size") (ne .Order "desc")}}
<a href="?sort=size&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Size <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "size") (ne .Order "asc")}}
<a href="?sort=size&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Size <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=size&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Size</a>
{{- end}}
</th>
<th class="hideable">
{{- if and (eq .Sort "time") (ne .Order "desc")}}
<a href="?sort=time&order=desc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Modified <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#up-arrow"></use></svg></a>
{{- else if and (eq .Sort "time") (ne .Order "asc")}}
<a href="?sort=time&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Modified <svg width="1em" height=".4em" version="1.1" viewBox="0 0 12.922194 6.0358899"><use xlink:href="#down-arrow"></use></svg></a>
{{- else}}
<a href="?sort=time&order=asc{{if ne 0 .ItemsLimitedTo}}&limit={{.ItemsLimitedTo}}{{end}}">Modified</a>
{{- end}}
</th>
</tr>
</thead>
<tbody>
{{- range .Items}}
<tr>
<td>
<a href="{{.URL}}">
{{- if .IsDir}}
<i class="material-icons">folder</i>
{{- else}}
<i class="material-icons">insert_drive_file</i>
{{- end}}
<span class="name">{{.Name}}</span>
</a>
</td>
{{- if .IsDir}}
<td data-order="-1">&mdash;</td>
{{- else}}
<td data-order="{{.Size}}">{{.HumanSize}}</td>
{{- end}}
<td class="hideable"><time datetime="{{.HumanModTime "2006-01-02T15:04:05Z"}}">{{.HumanModTime "01/02/2006 03:04:05 PM -07:00"}}</time></td>
</tr>
{{- end}}
</tbody>
</table> -->
</div>
{{ end }}

View File

@ -3,9 +3,9 @@
<main class="container">
{{ if eq .Type "image" }}
<img src="{{.Base64}}">
<img src="{{ .URL }}?raw=true">
{{ else if eq .Type "audio" }}
<audio src="{{.Base64}}">
<audio src="{{ .URL }}?raw=true">
</audio>
{{ else if eq .Type "video" }}

View File

@ -2,6 +2,7 @@
// sources:
// assets/public/css/styles.css
// assets/public/js/application.js
// assets/templates/actions.tmpl
// assets/templates/base.tmpl
// assets/templates/listing.tmpl
// assets/templates/single.tmpl
@ -67,6 +68,24 @@ func publicJsApplicationJs() (*asset, error) {
return a, err
}
// templatesActionsTmpl reads file data from disk. It returns an error on failure.
func templatesActionsTmpl() (*asset, error) {
path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-filemanager\\assets\\templates\\actions.tmpl"
name := "templates/actions.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
}
// templatesBaseTmpl reads file data from disk. It returns an error on failure.
func templatesBaseTmpl() (*asset, error) {
path := "D:\\Code\\Go\\src\\github.com\\hacdias\\caddy-filemanager\\assets\\templates\\base.tmpl"
@ -175,6 +194,7 @@ func AssetNames() []string {
var _bindata = map[string]func() (*asset, error){
"public/css/styles.css": publicCssStylesCss,
"public/js/application.js": publicJsApplicationJs,
"templates/actions.tmpl": templatesActionsTmpl,
"templates/base.tmpl": templatesBaseTmpl,
"templates/listing.tmpl": templatesListingTmpl,
"templates/single.tmpl": templatesSingleTmpl,
@ -229,6 +249,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
}},
}},
"templates": &bintree{nil, map[string]*bintree{
"actions.tmpl": &bintree{templatesActionsTmpl, map[string]*bintree{}},
"base.tmpl": &bintree{templatesBaseTmpl, map[string]*bintree{}},
"listing.tmpl": &bintree{templatesListingTmpl, map[string]*bintree{}},
"single.tmpl": &bintree{templatesSingleTmpl, map[string]*bintree{}},

View File

@ -22,9 +22,10 @@ type FileInfo struct {
Name string
Size int64
URL string
Path string // The relative Path of the file/directory relative to Caddyfile.
RootPath string // The Path of the file/directory on http.FileSystem.
ModTime time.Time
Mode os.FileMode
Path string
Mimetype string
Content string
Type string
@ -35,12 +36,20 @@ type FileInfo struct {
func GetFileInfo(url *url.URL, c *Config) (*FileInfo, int, error) {
var err error
path := strings.Replace(url.Path, c.BaseURL, "", 1)
path = filepath.Clean(path)
path = strings.Replace(path, "\\", "/", -1)
rootPath := strings.Replace(url.Path, c.BaseURL, "", 1)
rootPath = strings.TrimPrefix(rootPath, "/")
rootPath = "/" + rootPath
file := &FileInfo{Path: path}
f, err := c.Root.Open("/" + path)
path := c.PathScope + rootPath
path = strings.Replace(path, "\\", "/", -1)
path = filepath.Clean(path)
file := &FileInfo{
URL: url.Path,
RootPath: rootPath,
Path: path,
}
f, err := c.Root.Open(rootPath)
if err != nil {
return file, ErrorToHTTPCode(err), err
}
@ -55,8 +64,6 @@ func GetFileInfo(url *url.URL, c *Config) (*FileInfo, int, error) {
file.ModTime = info.ModTime()
file.Name = info.Name()
file.Size = info.Size()
file.URL = url.Path
return file, 0, nil
}
@ -128,7 +135,7 @@ func (fi FileInfo) Rename(w http.ResponseWriter, r *http.Request) (int, error) {
return ErrorToHTTPCode(err), err
}
http.Redirect(w, r, strings.Replace(fi.URL, fi.Name, newname, 1), http.StatusTemporaryRedirect)
http.Redirect(w, r, strings.Replace(r.URL.Path, fi.Name, newname, 1), http.StatusTemporaryRedirect)
return 0, nil
}
@ -149,8 +156,9 @@ func (fi FileInfo) serveSingleFile(w http.ResponseWriter, r *http.Request, c *Co
page := &Page{
Info: &PageInfo{
Name: fi.Path,
Path: fi.Path,
Name: fi.Name,
Path: fi.RootPath,
IsDir: false,
Data: fi,
Config: c,
},
@ -162,7 +170,7 @@ func (fi FileInfo) serveSingleFile(w http.ResponseWriter, r *http.Request, c *Co
func (fi FileInfo) serveListing(w http.ResponseWriter, r *http.Request, c *Config) (int, error) {
var err error
file, err := c.Root.Open("/" + fi.Path)
file, err := c.Root.Open(fi.RootPath)
if err != nil {
return ErrorToHTTPCode(err), err
}
@ -204,7 +212,8 @@ func (fi FileInfo) serveListing(w http.ResponseWriter, r *http.Request, c *Confi
page := &Page{
Info: &PageInfo{
Name: listing.Name,
Path: listing.Path,
Path: fi.RootPath,
IsDir: true,
Config: c,
Data: listing,
},
@ -219,7 +228,7 @@ func (fi FileInfo) loadDirectoryContents(file http.File, c *Config) (*Listing, e
return nil, err
}
listing := directoryListing(files, fi.Path)
listing := directoryListing(files, fi.RootPath)
return &listing, nil
}
@ -260,6 +269,22 @@ func directoryListing(files []os.FileInfo, urlPath string) Listing {
}
}
// ServeRawFile serves raw files
func (fi *FileInfo) ServeRawFile(w http.ResponseWriter, r *http.Request, c *Config) (int, error) {
err := fi.GetExtendedFileInfo()
if err != nil {
return ErrorToHTTPCode(err), err
}
if fi.Type != "text" {
fi.Read()
}
w.Header().Set("Content-Type", fi.Mimetype)
w.Write([]byte(fi.Content))
return 200, nil
}
// SimplifyMimeType returns the base type of a file
func SimplifyMimeType(name string) string {
if strings.HasPrefix(name, "video") {

View File

@ -66,12 +66,12 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
if !fi.IsDir {
query := r.URL.Query()
if val, ok := query["raw"]; ok && val[0] == "true" {
return f.Next.ServeHTTP(w, r)
return fi.ServeRawFile(w, r, c)
}
if val, ok := query["download"]; ok && val[0] == "true" {
w.Header().Set("Content-Disposition", "attachment; filename="+fi.Name)
return f.Next.ServeHTTP(w, r)
return fi.ServeRawFile(w, r, c)
}
}

13
page.go
View File

@ -17,6 +17,7 @@ type Page struct {
type PageInfo struct {
Name string
Path string
IsDir bool
Config *Config
Data interface{}
}
@ -51,18 +52,24 @@ func (p PageInfo) BreadcrumbMap() map[string]string {
// PreviousLink returns the path of the previous folder
func (p PageInfo) PreviousLink() string {
parts := strings.Split(p.Path, "/")
parts := strings.Split(strings.TrimSuffix(p.Path, "/"), "/")
if len(parts) <= 1 {
return ""
}
if parts[len(parts)-2] == "" {
if p.Config.BaseURL == "" {
return "/"
}
return p.Config.BaseURL
}
return parts[len(parts)-2]
}
// PrintAsHTML formats the page in HTML and executes the template
func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) {
templates = append(templates, "base")
templates = append(templates, "actions", "base")
var tpl *template.Template
// For each template, add it to the the tpl variable

View File

@ -63,6 +63,7 @@ func parseConfiguration(c *caddy.Controller) ([]Config, error) {
return configs, c.ArgErr()
}
cfg.PathScope = c.Val()
cfg.PathScope = strings.TrimSuffix(cfg.PathScope, "/")
case "on":
if !c.NextArg() {
return configs, c.ArgErr()