filebrowser/listing.go

181 lines
3.7 KiB
Go
Raw Normal View History

2017-06-24 11:12:15 +00:00
package filemanager
import (
"context"
"net/url"
"os"
"path"
"sort"
"strings"
)
2017-06-25 13:48:34 +00:00
// 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).
2017-06-24 11:12:15 +00:00
Name string
2017-06-25 13:48:34 +00:00
// The full path of the request relatively to a File System.
2017-06-24 11:12:15 +00:00
Path string
2017-06-25 13:48:34 +00:00
// The items (files and folders) in the path.
2017-06-25 12:03:59 +00:00
Items []fileInfo
2017-06-25 13:48:34 +00:00
// The number of directories in the listing.
2017-06-24 11:12:15 +00:00
NumDirs int
2017-06-25 13:48:34 +00:00
// The number of files (items that aren't directories) in the listing.
2017-06-24 11:12:15 +00:00
NumFiles int
2017-06-25 13:48:34 +00:00
// Which sorting order is used.
2017-06-24 11:12:15 +00:00
Sort string
2017-06-25 13:48:34 +00:00
// And which order.
2017-06-24 11:12:15 +00:00
Order string
2017-06-25 13:48:34 +00:00
// If ≠0 then Items have been limited to that many elements.
2017-06-27 13:49:46 +00:00
ItemsLimitedTo int
2017-06-24 11:12:15 +00:00
}
2017-06-25 13:48:34 +00:00
// getListing gets the information about a specific directory and its files.
2017-06-25 14:24:16 +00:00
func getListing(u *User, filePath string, baseURL string) (*listing, error) {
2017-06-24 11:12:15 +00:00
// Gets the directory information using the Virtual File System of
// the user configuration.
file, err := u.fileSystem.OpenFile(context.TODO(), filePath, os.O_RDONLY, 0)
2017-06-24 11:12:15 +00:00
if err != nil {
return nil, err
}
defer file.Close()
// Reads the directory and gets the information about the files.
files, err := file.Readdir(-1)
if err != nil {
return nil, err
}
var (
2017-06-25 12:03:59 +00:00
fileinfos []fileInfo
2017-06-24 11:12:15 +00:00
dirCount, fileCount int
)
for _, f := range files {
name := f.Name()
allowed := u.Allowed("/" + name)
if !allowed {
continue
}
if f.IsDir() {
name += "/"
dirCount++
} else {
fileCount++
}
// Absolute URL
url := url.URL{Path: baseURL + name}
2017-06-25 12:03:59 +00:00
i := fileInfo{
2017-06-25 13:48:34 +00:00
Name: f.Name(),
Size: f.Size(),
ModTime: f.ModTime(),
Mode: f.Mode(),
IsDir: f.IsDir(),
URL: url.String(),
2017-06-24 11:12:15 +00:00
}
i.RetrieveFileType()
fileinfos = append(fileinfos, i)
}
2017-06-25 13:48:34 +00:00
return &listing{
2017-06-24 11:12:15 +00:00
Name: path.Base(filePath),
Path: filePath,
Items: fileinfos,
NumDirs: dirCount,
NumFiles: fileCount,
}, nil
}
// ApplySort applies the sort order using .Order and .Sort
2017-06-25 13:48:34 +00:00
func (l listing) ApplySort() {
2017-06-24 11:12:15 +00:00
// 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
}
}
}
2017-06-25 13:48:34 +00:00
// Implement sorting for listing
type byName listing
type bySize listing
type byTime listing
2017-06-24 11:12:15 +00:00
// 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)
}