159 lines
3.4 KiB
Go
159 lines
3.4 KiB
Go
// Package index creates metadata for directory listings and generates index
|
|
// pages for every directory which contains other files. This is useful for
|
|
// creating static directory views for downloads, image galleries, etc.
|
|
package index
|
|
|
|
import (
|
|
"bytes"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.foosoft.net/alex/goldsmith"
|
|
)
|
|
|
|
// Entry contains information about a directory item.
|
|
type Entry struct {
|
|
Name string
|
|
Path string
|
|
IsDir bool
|
|
File goldsmith.File
|
|
}
|
|
|
|
// Index chainable plugin context.
|
|
type Index struct {
|
|
indexName string
|
|
filesKey string
|
|
indexProps map[string]interface{}
|
|
|
|
dirLists map[string]*directory
|
|
dirsHandled map[string]bool
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
// New creates a new instance of the Index plugin.
|
|
// The meta parameter allows additional metadata to be provided for generated indices.
|
|
func New(indexProps map[string]interface{}) *Index {
|
|
return &Index{
|
|
indexName: "index.html",
|
|
indexProps: indexProps,
|
|
filesKey: "Files",
|
|
dirsHandled: make(map[string]bool),
|
|
dirLists: make(map[string]*directory),
|
|
}
|
|
}
|
|
|
|
// IndexFilename sets the name of the file to be created as the directory index (default: "index.html").
|
|
func (self *Index) IndexFilename(filename string) *Index {
|
|
self.indexName = filename
|
|
return self
|
|
}
|
|
|
|
// FilesKey sets the metadata key used to access the files in the current directory (default: "Files").
|
|
func (self *Index) FilesKey(key string) *Index {
|
|
self.filesKey = key
|
|
return self
|
|
}
|
|
|
|
func (*Index) Name() string {
|
|
return "index"
|
|
}
|
|
|
|
func (self *Index) Process(context goldsmith.Context, inputFile goldsmith.File) error {
|
|
self.mutex.Lock()
|
|
defer self.mutex.Unlock()
|
|
|
|
currentPath := inputFile.Path()
|
|
currentIsDir := false
|
|
|
|
for {
|
|
if handled, _ := self.dirsHandled[currentPath]; handled {
|
|
break
|
|
}
|
|
|
|
self.dirsHandled[currentPath] = true
|
|
|
|
currentDir := filepath.Dir(currentPath)
|
|
currentBase := filepath.Base(currentPath)
|
|
|
|
list, ok := self.dirLists[currentDir]
|
|
if !ok {
|
|
list = new(directory)
|
|
self.dirLists[currentDir] = list
|
|
}
|
|
|
|
if !currentIsDir {
|
|
if currentBase == self.indexName {
|
|
list.indexFile = inputFile
|
|
} else {
|
|
context.DispatchFile(inputFile)
|
|
}
|
|
}
|
|
|
|
entry := Entry{Name: currentBase, Path: currentPath, IsDir: currentIsDir, File: inputFile}
|
|
list.entries = append(list.entries, entry)
|
|
|
|
if currentDir == "." {
|
|
break
|
|
}
|
|
|
|
currentPath = currentDir
|
|
currentIsDir = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *Index) Finalize(context goldsmith.Context) error {
|
|
for name, list := range self.dirLists {
|
|
sort.Sort(list.entries)
|
|
|
|
indexFile := list.indexFile
|
|
if indexFile == nil {
|
|
var err error
|
|
indexFile, err = context.CreateFileFromReader(filepath.Join(name, self.indexName), bytes.NewReader(nil))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for name, value := range self.indexProps {
|
|
indexFile.SetProp(name, value)
|
|
}
|
|
}
|
|
|
|
indexFile.SetProp(self.filesKey, list.entries)
|
|
context.DispatchFile(indexFile)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type directory struct {
|
|
entries entriesByName
|
|
indexFile goldsmith.File
|
|
}
|
|
|
|
type entriesByName []Entry
|
|
|
|
func (self entriesByName) Len() int {
|
|
return len(self)
|
|
}
|
|
|
|
func (self entriesByName) Less(i, j int) bool {
|
|
e1, e2 := self[i], self[j]
|
|
|
|
if e1.IsDir && !e2.IsDir {
|
|
return true
|
|
}
|
|
if !e1.IsDir && e2.IsDir {
|
|
return false
|
|
}
|
|
|
|
return strings.Compare(e1.Name, e2.Name) == -1
|
|
}
|
|
|
|
func (self entriesByName) Swap(i, j int) {
|
|
self[i], self[j] = self[j], self[i]
|
|
}
|