some updates
This commit is contained in:
parent
0146f4324f
commit
4efb4f2a53
10
README.md
10
README.md
|
@ -2,3 +2,13 @@
|
||||||
|
|
||||||
[](https://forum.caddyserver.com)
|
[](https://forum.caddyserver.com)
|
||||||
[](http://godoc.org/github.com/hacdias/caddy-filemanager)
|
[](http://godoc.org/github.com/hacdias/caddy-filemanager)
|
||||||
|
|
||||||
|
This is just a concept of syntax:
|
||||||
|
|
||||||
|
```
|
||||||
|
filemanager {
|
||||||
|
show path
|
||||||
|
on url
|
||||||
|
styles filepath
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,9 +5,9 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/_filemanager_internal/styles.css">
|
<link rel="stylesheet" href="/_filemanager_internal/styles.css">
|
||||||
<style>
|
<style>
|
||||||
|
{{ .StyleSheet }}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="0" width="0" style="position: absolute;">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="0" width="0" style="position: absolute;">
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package filemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileInfo is the info about a particular file or directory
|
||||||
|
type FileInfo struct {
|
||||||
|
IsDir bool
|
||||||
|
Name string
|
||||||
|
Size int64
|
||||||
|
URL string
|
||||||
|
ModTime time.Time
|
||||||
|
Mode os.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumanSize returns the size of the file as a human-readable string
|
||||||
|
// in IEC format (i.e. power of 2 or base 1024).
|
||||||
|
func (fi FileInfo) HumanSize() string {
|
||||||
|
return humanize.IBytes(uint64(fi.Size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumanModTime returns the modified time of the file as a human-readable string.
|
||||||
|
func (fi FileInfo) HumanModTime(format string) string {
|
||||||
|
return fi.ModTime.Format(format)
|
||||||
|
}
|
229
filemanager.go
229
filemanager.go
|
@ -1,6 +1,6 @@
|
||||||
//go:generate go get github.com/jteeuwen/go-bindata
|
//go:generate go get github.com/jteeuwen/go-bindata
|
||||||
//go:generate go install github.com/jteeuwen/go-bindata/go-bindata
|
//go:generate go install github.com/jteeuwen/go-bindata/go-bindata
|
||||||
//go:generate go-bindata -pkg assets -prefix "assets/source" -o assets/assets.go assets/source/...
|
//go:generate go-bindata -debug -pkg assets -prefix "assets/source" -o assets/assets.go assets/source/...
|
||||||
|
|
||||||
// Package filemanager provides middleware for managing files in a directory
|
// Package filemanager provides middleware for managing files in a directory
|
||||||
// when directory path is requested instead of a specific file. Based on browse
|
// when directory path is requested instead of a specific file. Based on browse
|
||||||
|
@ -10,22 +10,20 @@ package filemanager
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
"github.com/hacdias/caddy-filemanager/assets"
|
|
||||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||||
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Template used to show FileManager
|
||||||
|
var Template *template.Template
|
||||||
|
|
||||||
// FileManager is an http.Handler that can show a file listing when
|
// FileManager is an http.Handler that can show a file listing when
|
||||||
// directories in the given paths are specified.
|
// directories in the given paths are specified.
|
||||||
type FileManager struct {
|
type FileManager struct {
|
||||||
|
@ -36,94 +34,11 @@ type FileManager struct {
|
||||||
|
|
||||||
// Config is a configuration for browsing in a particular path.
|
// Config is a configuration for browsing in a particular path.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
PathScope string
|
PathScope string
|
||||||
Root http.FileSystem
|
Root http.FileSystem
|
||||||
Variables interface{}
|
BaseURL string
|
||||||
Template *template.Template
|
StyleSheet string
|
||||||
}
|
Variables interface{}
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// The full path of the request
|
|
||||||
Path string
|
|
||||||
|
|
||||||
// Whether the parent directory is browsable
|
|
||||||
CanGoUp bool
|
|
||||||
|
|
||||||
// The items (files and folders) in the path
|
|
||||||
Items []FileInfo
|
|
||||||
|
|
||||||
// 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
|
|
||||||
ItemsLimitedTo int
|
|
||||||
|
|
||||||
// Optional custom variables for use in browse templates
|
|
||||||
User interface{}
|
|
||||||
|
|
||||||
httpserver.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// BreadcrumbMap returns l.Path where every element is a map
|
|
||||||
// of URLs and path segment names.
|
|
||||||
func (l Listing) BreadcrumbMap() map[string]string {
|
|
||||||
result := map[string]string{}
|
|
||||||
|
|
||||||
if len(l.Path) == 0 {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip trailing slash
|
|
||||||
lpath := l.Path
|
|
||||||
if lpath[len(lpath)-1] == '/' {
|
|
||||||
lpath = lpath[:len(lpath)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := strings.Split(lpath, "/")
|
|
||||||
for i, part := range parts {
|
|
||||||
if i == 0 && part == "" {
|
|
||||||
// Leading slash (root)
|
|
||||||
result["/"] = "/"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result[strings.Join(parts[:i+1], "/")] = part
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileInfo is the info about a particular file or directory
|
|
||||||
type FileInfo struct {
|
|
||||||
IsDir bool
|
|
||||||
Name string
|
|
||||||
Size int64
|
|
||||||
URL string
|
|
||||||
ModTime time.Time
|
|
||||||
Mode os.FileMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// HumanSize returns the size of the file as a human-readable string
|
|
||||||
// in IEC format (i.e. power of 2 or base 1024).
|
|
||||||
func (fi FileInfo) HumanSize() string {
|
|
||||||
return humanize.IBytes(uint64(fi.Size))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HumanModTime returns the modified time of the file as a human-readable string.
|
|
||||||
func (fi FileInfo) HumanModTime(format string) string {
|
|
||||||
return fi.ModTime.Format(format)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string) (Listing, bool) {
|
func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string) (Listing, bool) {
|
||||||
|
@ -175,64 +90,62 @@ func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string) (Listin
|
||||||
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
|
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
|
||||||
// If so, control is handed over to ServeListing.
|
// If so, control is handed over to ServeListing.
|
||||||
func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
var bc *Config
|
var fmc *Config
|
||||||
// See if there's a browse configuration to match the path
|
// See if there's a browse configuration to match the path
|
||||||
for i := range f.Configs {
|
for i := range f.Configs {
|
||||||
if httpserver.Path(r.URL.Path).Matches(f.Configs[i].PathScope) {
|
if httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) {
|
||||||
bc = &f.Configs[i]
|
fmc = &f.Configs[i]
|
||||||
goto inScope
|
|
||||||
|
// Browse works on existing directories; delegate everything else
|
||||||
|
requestedFilepath, err := fmc.Root.Open(strings.Replace(r.URL.Path, fmc.BaseURL, "", 1))
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case os.IsPermission(err):
|
||||||
|
return http.StatusForbidden, err
|
||||||
|
case os.IsExist(err):
|
||||||
|
return http.StatusNotFound, err
|
||||||
|
default:
|
||||||
|
return f.Next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer requestedFilepath.Close()
|
||||||
|
|
||||||
|
info, err := requestedFilepath.Stat()
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case os.IsPermission(err):
|
||||||
|
return http.StatusForbidden, err
|
||||||
|
case os.IsExist(err):
|
||||||
|
return http.StatusGone, err
|
||||||
|
default:
|
||||||
|
return f.Next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
return f.Next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not reply to anything else because it might be nonsensical
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodGet, http.MethodHead:
|
||||||
|
// proceed, noop
|
||||||
|
case "PROPFIND", http.MethodOptions:
|
||||||
|
return http.StatusNotImplemented, nil
|
||||||
|
default:
|
||||||
|
return f.Next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Browsing navigation gets messed up if browsing a directory
|
||||||
|
// that doesn't end in "/" (which it should, anyway)
|
||||||
|
if !strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.ServeListing(w, r, requestedFilepath, fmc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f.Next.ServeHTTP(w, r)
|
return f.Next.ServeHTTP(w, r)
|
||||||
inScope:
|
|
||||||
|
|
||||||
// Browse works on existing directories; delegate everything else
|
|
||||||
requestedFilepath, err := bc.Root.Open(r.URL.Path)
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case os.IsPermission(err):
|
|
||||||
return http.StatusForbidden, err
|
|
||||||
case os.IsExist(err):
|
|
||||||
return http.StatusNotFound, err
|
|
||||||
default:
|
|
||||||
return f.Next.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer requestedFilepath.Close()
|
|
||||||
|
|
||||||
info, err := requestedFilepath.Stat()
|
|
||||||
if err != nil {
|
|
||||||
switch {
|
|
||||||
case os.IsPermission(err):
|
|
||||||
return http.StatusForbidden, err
|
|
||||||
case os.IsExist(err):
|
|
||||||
return http.StatusGone, err
|
|
||||||
default:
|
|
||||||
return f.Next.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !info.IsDir() {
|
|
||||||
return f.Next.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not reply to anything else because it might be nonsensical
|
|
||||||
switch r.Method {
|
|
||||||
case http.MethodGet, http.MethodHead:
|
|
||||||
// proceed, noop
|
|
||||||
case "PROPFIND", http.MethodOptions:
|
|
||||||
return http.StatusNotImplemented, nil
|
|
||||||
default:
|
|
||||||
return f.Next.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browsing navigation gets messed up if browsing a directory
|
|
||||||
// that doesn't end in "/" (which it should, anyway)
|
|
||||||
if !strings.HasSuffix(r.URL.Path, "/") {
|
|
||||||
http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.ServeListing(w, r, requestedFilepath, bc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FileManager) loadDirectoryContents(requestedFilepath http.File, urlPath string) (*Listing, bool, error) {
|
func (f FileManager) loadDirectoryContents(requestedFilepath http.File, urlPath string) (*Listing, bool, error) {
|
||||||
|
@ -316,26 +229,6 @@ func (f FileManager) ServeListing(w http.ResponseWriter, r *http.Request, reques
|
||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveAssets handles the /{admin}/assets requests
|
|
||||||
func serveAssets(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
||||||
filename := strings.Replace(r.URL.Path, assetsURL, "", 1)
|
|
||||||
file, err := assets.Asset(filename)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 404, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the file extension ant its mime type
|
|
||||||
extension := filepath.Ext(filename)
|
|
||||||
mime := mime.TypeByExtension(extension)
|
|
||||||
|
|
||||||
// Write the header with the Content-Type and write the file
|
|
||||||
// content to the buffer
|
|
||||||
w.Header().Set("Content-Type", mime)
|
|
||||||
w.Write(file)
|
|
||||||
return 200, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FileManager) formatAsJSON(listing *Listing, bc *Config) (*bytes.Buffer, error) {
|
func (f FileManager) formatAsJSON(listing *Listing, bc *Config) (*bytes.Buffer, error) {
|
||||||
marsh, err := json.Marshal(listing.Items)
|
marsh, err := json.Marshal(listing.Items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -349,6 +242,6 @@ func (f FileManager) formatAsJSON(listing *Listing, bc *Config) (*bytes.Buffer,
|
||||||
|
|
||||||
func (f FileManager) formatAsHTML(listing *Listing, bc *Config) (*bytes.Buffer, error) {
|
func (f FileManager) formatAsHTML(listing *Listing, bc *Config) (*bytes.Buffer, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
err := bc.Template.Execute(buf, listing)
|
err := Template.Execute(buf, listing)
|
||||||
return buf, err
|
return buf, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package filemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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
|
||||||
|
|
||||||
|
// The full path of the request
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Whether the parent directory is browsable
|
||||||
|
CanGoUp bool
|
||||||
|
|
||||||
|
// The items (files and folders) in the path
|
||||||
|
Items []FileInfo
|
||||||
|
|
||||||
|
// 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
|
||||||
|
ItemsLimitedTo int
|
||||||
|
|
||||||
|
// Optional custom variables for use in browse templates
|
||||||
|
User interface{}
|
||||||
|
|
||||||
|
// StyleSheet to costumize the page
|
||||||
|
StyleSheet string
|
||||||
|
|
||||||
|
httpserver.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// BreadcrumbMap returns l.Path where every element is a map
|
||||||
|
// of URLs and path segment names.
|
||||||
|
func (l Listing) BreadcrumbMap() map[string]string {
|
||||||
|
result := map[string]string{}
|
||||||
|
|
||||||
|
if len(l.Path) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip trailing slash
|
||||||
|
lpath := l.Path
|
||||||
|
if lpath[len(lpath)-1] == '/' {
|
||||||
|
lpath = lpath[:len(lpath)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(lpath, "/")
|
||||||
|
for i, part := range parts {
|
||||||
|
if i == 0 && part == "" {
|
||||||
|
// Leading slash (root)
|
||||||
|
result["/"] = "/"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[strings.Join(parts[:i+1], "/")] = part
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
88
setup.go
88
setup.go
|
@ -22,6 +22,20 @@ func init() {
|
||||||
|
|
||||||
// setup configures a new Browse middleware instance.
|
// setup configures a new Browse middleware instance.
|
||||||
func setup(c *caddy.Controller) error {
|
func setup(c *caddy.Controller) error {
|
||||||
|
// Second argument would be the template file to use
|
||||||
|
tplBytes, err := assets.Asset("template.tmpl")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tplText := string(tplBytes)
|
||||||
|
|
||||||
|
// Build the template
|
||||||
|
tpl, err := template.New("listing").Parse(tplText)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Template = tpl
|
||||||
|
|
||||||
configs, err := fileManagerParse(c)
|
configs, err := fileManagerParse(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -43,65 +57,63 @@ func setup(c *caddy.Controller) error {
|
||||||
func fileManagerParse(c *caddy.Controller) ([]Config, error) {
|
func fileManagerParse(c *caddy.Controller) ([]Config, error) {
|
||||||
var configs []Config
|
var configs []Config
|
||||||
|
|
||||||
cfg := httpserver.GetConfig(c.Key)
|
appendCfg := func(fmc Config) error {
|
||||||
|
|
||||||
appendCfg := func(bc Config) error {
|
|
||||||
for _, c := range configs {
|
for _, c := range configs {
|
||||||
if c.PathScope == bc.PathScope {
|
if c.PathScope == fmc.PathScope {
|
||||||
return fmt.Errorf("duplicate browsing config for %s", c.PathScope)
|
return fmt.Errorf("duplicate file managing config for %s", c.PathScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configs = append(configs, bc)
|
configs = append(configs, fmc)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
var bc Config
|
var fmc = Config{
|
||||||
|
PathScope: ".",
|
||||||
|
BaseURL: "/",
|
||||||
|
StyleSheet: "",
|
||||||
|
}
|
||||||
|
var styles string
|
||||||
|
|
||||||
// First argument is directory to allow browsing; default is site root
|
for c.NextBlock() {
|
||||||
if c.NextArg() {
|
switch c.Val() {
|
||||||
bc.PathScope = c.Val()
|
case "show":
|
||||||
} else {
|
if !c.NextArg() {
|
||||||
bc.PathScope = "/"
|
return configs, c.ArgErr()
|
||||||
}
|
}
|
||||||
bc.Root = http.Dir(cfg.Root)
|
fmc.PathScope = c.Val()
|
||||||
theRoot, err := bc.Root.Open("/") // catch a missing path early
|
case "on":
|
||||||
if err != nil {
|
if !c.NextArg() {
|
||||||
return configs, err
|
return configs, c.ArgErr()
|
||||||
}
|
}
|
||||||
defer theRoot.Close()
|
fmc.BaseURL = c.Val()
|
||||||
_, err = theRoot.Readdir(-1)
|
case "styles":
|
||||||
if err != nil {
|
if !c.NextArg() {
|
||||||
return configs, err
|
return configs, c.ArgErr()
|
||||||
|
}
|
||||||
|
styles = c.Val()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tplBytes []byte
|
// Get StyleSheet
|
||||||
|
if styles != "" {
|
||||||
// Second argument would be the template file to use
|
tplBytes, err := ioutil.ReadFile(c.Val())
|
||||||
var tplText string
|
|
||||||
if c.NextArg() {
|
|
||||||
tplBytes, err = ioutil.ReadFile(c.Val())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
tplText = string(tplBytes)
|
fmc.StyleSheet = string(tplBytes)
|
||||||
} else {
|
} else {
|
||||||
tplBytes, err = assets.Asset(assetsURL + "template.tmpl")
|
tplBytes, err := assets.Asset("template.tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
tplText = string(tplBytes)
|
fmc.StyleSheet = string(tplBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the template
|
fmc.Root = http.Dir(fmc.PathScope)
|
||||||
tpl, err := template.New("listing").Parse(tplText)
|
|
||||||
if err != nil {
|
|
||||||
return configs, err
|
|
||||||
}
|
|
||||||
bc.Template = tpl
|
|
||||||
|
|
||||||
// Save configuration
|
// Save configuration
|
||||||
err = appendCfg(bc)
|
err := appendCfg(fmc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue