filebrowser/file/listing.go

183 lines
3.7 KiB
Go
Raw Normal View History

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()
2017-01-01 22:40:12 +00:00
allowed := u.Allowed("/" + name)
if !allowed {
continue
}
2016-10-18 20:06:31 +00:00
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(),
2017-01-01 22:40:12 +00:00
UserAllowed: allowed,
2016-12-30 16:22:26 +00:00
}
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())
}