2016-10-18 20:49:46 +00:00
|
|
|
package file
|
2016-06-11 22:01:24 +00:00
|
|
|
|
|
|
|
import (
|
2016-11-02 19:35:11 +00:00
|
|
|
"context"
|
2016-10-18 20:06:31 +00:00
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path"
|
2016-10-22 10:47:49 +00:00
|
|
|
"sort"
|
2016-06-11 22:01:24 +00:00
|
|
|
"strings"
|
|
|
|
|
2016-10-18 20:06:31 +00:00
|
|
|
"github.com/hacdias/caddy-filemanager/config"
|
2016-10-18 20:30:10 +00:00
|
|
|
|
2016-06-11 22:01:24 +00:00
|
|
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
|
|
|
)
|
|
|
|
|
|
|
|
// A Listing is the context used to fill out a template.
|
|
|
|
type Listing struct {
|
|
|
|
// The name of the directory (the last element of the path)
|
|
|
|
Name string
|
2016-10-22 10:47:49 +00:00
|
|
|
// The full path of the request relatively to a File System
|
2016-06-11 22:01:24 +00:00
|
|
|
Path string
|
|
|
|
// The items (files and folders) in the path
|
2016-10-18 20:49:46 +00:00
|
|
|
Items []Info
|
2016-06-11 22:01:24 +00:00
|
|
|
// The number of directories in the listing
|
|
|
|
NumDirs int
|
|
|
|
// The number of files (items that aren't directories) in the listing
|
|
|
|
NumFiles int
|
|
|
|
// Which sorting order is used
|
|
|
|
Sort string
|
|
|
|
// And which order
|
|
|
|
Order string
|
|
|
|
// If ≠0 then Items have been limited to that many elements
|
2016-08-11 21:19:21 +00:00
|
|
|
ItemsLimitedTo int
|
|
|
|
httpserver.Context `json:"-"`
|
2016-06-11 22:01:24 +00:00
|
|
|
}
|
|
|
|
|
2016-10-22 10:47:49 +00:00
|
|
|
// GetListing gets the information about a specific directory and its files.
|
|
|
|
func GetListing(u *config.User, filePath string, baseURL string) (*Listing, error) {
|
2016-10-19 19:58:08 +00:00
|
|
|
// Gets the directory information using the Virtual File System of
|
2016-10-22 10:47:49 +00:00
|
|
|
// the user configuration.
|
2016-11-02 19:35:11 +00:00
|
|
|
file, err := u.FileSystem.OpenFile(context.TODO(), filePath, os.O_RDONLY, 0)
|
2016-10-18 20:06:31 +00:00
|
|
|
if err != nil {
|
2016-10-22 10:47:49 +00:00
|
|
|
return nil, err
|
2016-10-18 20:06:31 +00:00
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2016-10-22 10:47:49 +00:00
|
|
|
// Reads the directory and gets the information about the files.
|
2016-10-18 20:06:31 +00:00
|
|
|
files, err := file.Readdir(-1)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2016-10-18 20:49:46 +00:00
|
|
|
fileinfos []Info
|
2016-10-18 20:06:31 +00:00
|
|
|
dirCount, fileCount int
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
name := f.Name()
|
|
|
|
|
|
|
|
if f.IsDir() {
|
|
|
|
name += "/"
|
|
|
|
dirCount++
|
|
|
|
} else {
|
|
|
|
fileCount++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Absolute URL
|
2016-10-22 10:47:49 +00:00
|
|
|
url := url.URL{Path: baseURL + name}
|
2016-12-30 16:22:26 +00:00
|
|
|
|
|
|
|
i := Info{
|
2016-10-18 20:06:31 +00:00
|
|
|
FileInfo: f,
|
|
|
|
URL: url.String(),
|
2016-12-30 16:22:26 +00:00
|
|
|
UserAllowed: u.Allowed("/" + name),
|
|
|
|
}
|
|
|
|
i.RetrieveFileType()
|
|
|
|
|
|
|
|
fileinfos = append(fileinfos, i)
|
2016-10-18 20:06:31 +00:00
|
|
|
}
|
|
|
|
|
2016-10-19 19:58:08 +00:00
|
|
|
return &Listing{
|
2016-10-22 10:47:49 +00:00
|
|
|
Name: path.Base(filePath),
|
|
|
|
Path: filePath,
|
2016-10-18 20:06:31 +00:00
|
|
|
Items: fileinfos,
|
|
|
|
NumDirs: dirCount,
|
|
|
|
NumFiles: fileCount,
|
2016-10-19 19:58:08 +00:00
|
|
|
}, nil
|
2016-10-18 20:06:31 +00:00
|
|
|
}
|
2016-10-22 10:47:49 +00:00
|
|
|
|
|
|
|
// ApplySort applies the sort order using .Order and .Sort
|
|
|
|
func (l Listing) ApplySort() {
|
|
|
|
// Check '.Order' to know how to sort
|
|
|
|
if l.Order == "desc" {
|
|
|
|
switch l.Sort {
|
|
|
|
case "name":
|
|
|
|
sort.Sort(sort.Reverse(byName(l)))
|
|
|
|
case "size":
|
|
|
|
sort.Sort(sort.Reverse(bySize(l)))
|
|
|
|
case "time":
|
|
|
|
sort.Sort(sort.Reverse(byTime(l)))
|
|
|
|
default:
|
|
|
|
// If not one of the above, do nothing
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else { // If we had more Orderings we could add them here
|
|
|
|
switch l.Sort {
|
|
|
|
case "name":
|
|
|
|
sort.Sort(byName(l))
|
|
|
|
case "size":
|
|
|
|
sort.Sort(bySize(l))
|
|
|
|
case "time":
|
|
|
|
sort.Sort(byTime(l))
|
|
|
|
default:
|
|
|
|
sort.Sort(byName(l))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implement sorting for Listing
|
|
|
|
type byName Listing
|
|
|
|
type bySize Listing
|
|
|
|
type byTime Listing
|
|
|
|
|
|
|
|
// By Name
|
|
|
|
func (l byName) Len() int {
|
|
|
|
return len(l.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l byName) Swap(i, j int) {
|
|
|
|
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Treat upper and lower case equally
|
|
|
|
func (l byName) Less(i, j int) bool {
|
|
|
|
if l.Items[i].IsDir() && !l.Items[j].IsDir() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !l.Items[i].IsDir() && l.Items[j].IsDir() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.ToLower(l.Items[i].Name()) < strings.ToLower(l.Items[j].Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
// By Size
|
|
|
|
func (l bySize) Len() int {
|
|
|
|
return len(l.Items)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l bySize) Swap(i, j int) {
|
|
|
|
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
const directoryOffset = -1 << 31 // = math.MinInt32
|
|
|
|
func (l bySize) Less(i, j int) bool {
|
|
|
|
iSize, jSize := l.Items[i].Size(), l.Items[j].Size()
|
|
|
|
if l.Items[i].IsDir() {
|
|
|
|
iSize = directoryOffset + iSize
|
|
|
|
}
|
|
|
|
if l.Items[j].IsDir() {
|
|
|
|
jSize = directoryOffset + jSize
|
|
|
|
}
|
|
|
|
return iSize < jSize
|
|
|
|
}
|
|
|
|
|
|
|
|
// By Time
|
|
|
|
func (l byTime) Len() int {
|
|
|
|
return len(l.Items)
|
|
|
|
}
|
|
|
|
func (l byTime) Swap(i, j int) {
|
|
|
|
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
|
|
|
|
}
|
|
|
|
func (l byTime) Less(i, j int) bool {
|
|
|
|
return l.Items[i].ModTime().Before(l.Items[j].ModTime())
|
|
|
|
}
|