From fe7579966dda1a2e6cec9baa9436f3de0b3cd917 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Tue, 18 Oct 2016 21:49:46 +0100 Subject: [PATCH] Improvements --- editor.go => file/editor.go | 78 +++++++++++-------------------- info.go => file/info.go | 43 +++++++---------- listing.go => file/listing.go | 12 ++--- filemanager.go | 14 +++--- frontmatter/runes.go | 24 ++++++++++ errors/errors.go => page/error.go | 10 ++-- preput.go => preprocess.go | 5 +- 7 files changed, 90 insertions(+), 96 deletions(-) rename editor.go => file/editor.go (50%) rename info.go => file/info.go (71%) rename listing.go => file/listing.go (94%) create mode 100644 frontmatter/runes.go rename errors/errors.go => page/error.go (86%) rename preput.go => preprocess.go (93%) diff --git a/editor.go b/file/editor.go similarity index 50% rename from editor.go rename to file/editor.go index d26efe0d..e0cd3ba8 100644 --- a/editor.go +++ b/file/editor.go @@ -1,4 +1,4 @@ -package filemanager +package file import ( "bytes" @@ -18,7 +18,7 @@ type Editor struct { } // GetEditor gets the editor based on a FileInfo struct -func (i *FileInfo) GetEditor() (*Editor, error) { +func (i *Info) GetEditor() (*Editor, error) { // Create a new editor variable and set the mode editor := new(Editor) editor.Mode = strings.TrimPrefix(filepath.Ext(i.Name()), ".") @@ -41,44 +41,39 @@ func (i *FileInfo) GetEditor() (*Editor, error) { // Handle the content depending on the file extension switch editor.Mode { - case "markdown", "asciidoc", "rst": - if !hasFrontMatterRune(i.Content) { - editor.Class = "content-only" - editor.Content = i.StringifyContent() - break - } - - // Starts a new buffer and parses the file using Hugo's functions - buffer := bytes.NewBuffer(i.Content) - page, err = parser.ReadFrom(buffer) - editor.Class = "complete" - - if err != nil { - editor.Class = "content-only" - editor.Content = i.StringifyContent() - break - } - - // Parses the page content and the frontmatter - editor.Content = strings.TrimSpace(string(page.Content())) - editor.FrontMatter, _, err = frontmatter.Pretty(page.FrontMatter()) case "json", "toml", "yaml": // Defines the class and declares an error editor.Class = "frontmatter-only" // Checks if the file already has the frontmatter rune and parses it - if hasFrontMatterRune(i.Content) { + if frontmatter.HasRune(i.Content) { editor.FrontMatter, _, err = frontmatter.Pretty(i.Content) } else { - editor.FrontMatter, _, err = frontmatter.Pretty(appendFrontMatterRune(i.Content, editor.Mode)) + editor.FrontMatter, _, err = frontmatter.Pretty(frontmatter.AppendRune(i.Content, editor.Mode)) } // Check if there were any errors - if err != nil { - editor.Class = "content-only" - editor.Content = i.StringifyContent() + if err == nil { break } + + fallthrough + case "markdown", "asciidoc", "rst": + if frontmatter.HasRune(i.Content) { + // Starts a new buffer and parses the file using Hugo's functions + buffer := bytes.NewBuffer(i.Content) + page, err = parser.ReadFrom(buffer) + editor.Class = "complete" + + if err == nil { + // Parses the page content and the frontmatter + editor.Content = strings.TrimSpace(string(page.Content())) + editor.FrontMatter, _, err = frontmatter.Pretty(page.FrontMatter()) + break + } + } + + fallthrough default: editor.Class = "content-only" editor.Content = i.StringifyContent() @@ -87,29 +82,12 @@ func (i *FileInfo) GetEditor() (*Editor, error) { return editor, nil } -// hasFrontMatterRune checks if the file has the frontmatter rune -func hasFrontMatterRune(file []byte) bool { - return strings.HasPrefix(string(file), "---") || - strings.HasPrefix(string(file), "+++") || - strings.HasPrefix(string(file), "{") -} - -// appendFrontMatterRune appends the frontmatter rune to a file -func appendFrontMatterRune(frontmatter []byte, language string) []byte { - switch language { - case "yaml": - return []byte("---\n" + string(frontmatter) + "\n---") - case "toml": - return []byte("+++\n" + string(frontmatter) + "\n+++") - case "json": - return frontmatter +// CanBeEdited checks if the extension of a file is supported by the editor +func (i Info) CanBeEdited() bool { + if i.Type == "text" { + return true } - return frontmatter -} - -// canBeEdited checks if the extension of a file is supported by the editor -func canBeEdited(filename string) bool { extensions := [...]string{ "md", "markdown", "mdown", "mmark", "asciidoc", "adoc", "ad", @@ -122,7 +100,7 @@ func canBeEdited(filename string) bool { } for _, extension := range extensions { - if strings.HasSuffix(filename, extension) { + if strings.HasSuffix(i.Name(), extension) { return true } } diff --git a/info.go b/file/info.go similarity index 71% rename from info.go rename to file/info.go index 542c13b3..533bae86 100644 --- a/info.go +++ b/file/info.go @@ -1,4 +1,4 @@ -package filemanager +package file import ( "io/ioutil" @@ -11,10 +11,11 @@ import ( humanize "github.com/dustin/go-humanize" "github.com/hacdias/caddy-filemanager/config" "github.com/hacdias/caddy-filemanager/page" + "github.com/hacdias/caddy-filemanager/utils" ) -// FileInfo contains the information about a particular file or directory -type FileInfo struct { +// Info contains the information about a particular file or directory +type Info struct { os.FileInfo URL string Path string // Relative path to Caddyfile @@ -27,10 +28,10 @@ type FileInfo struct { // GetInfo gets the file information and, in case of error, returns the // respective HTTP error code -func GetInfo(url *url.URL, c *config.Config, u *config.User) (*FileInfo, int, error) { +func GetInfo(url *url.URL, c *config.Config, u *config.User) (*Info, int, error) { var err error - i := &FileInfo{URL: url.Path} + i := &Info{URL: url.Path} i.VirtualPath = strings.Replace(url.Path, c.BaseURL, "", 1) i.VirtualPath = strings.TrimPrefix(i.VirtualPath, "/") i.VirtualPath = "/" + i.VirtualPath @@ -41,50 +42,40 @@ func GetInfo(url *url.URL, c *config.Config, u *config.User) (*FileInfo, int, er i.FileInfo, err = os.Stat(i.Path) if err != nil { - code := http.StatusInternalServerError - - switch { - case os.IsPermission(err): - code = http.StatusForbidden - case os.IsNotExist(err): - code = http.StatusGone - case os.IsExist(err): - code = http.StatusGone - } - - return i, code, err + return i, utils.ErrorToHTTPCode(err, false), err } return i, 0, nil } -func (i *FileInfo) Read() error { +func (i *Info) Read() error { var err error i.Content, err = ioutil.ReadFile(i.Path) if err != nil { return err } i.Mimetype = http.DetectContentType(i.Content) - i.Type = SimplifyMimeType(i.Mimetype) + i.Type = simplifyMediaType(i.Mimetype) return nil } -func (i FileInfo) StringifyContent() string { +// StringifyContent returns the string version of Raw +func (i Info) StringifyContent() string { return string(i.Content) } // HumanSize returns the size of the file as a human-readable string // in IEC format (i.e. power of 2 or base 1024). -func (i FileInfo) HumanSize() string { +func (i Info) HumanSize() string { return humanize.IBytes(uint64(i.Size())) } // HumanModTime returns the modified time of the file as a human-readable string. -func (i FileInfo) HumanModTime(format string) string { +func (i Info) HumanModTime(format string) string { return i.ModTime().Format(format) } -func (i *FileInfo) ServeHTTP(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { +func (i *Info) ServeHTTP(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { if i.IsDir() { return i.serveListing(w, r, c, u) } @@ -92,7 +83,7 @@ func (i *FileInfo) ServeHTTP(w http.ResponseWriter, r *http.Request, c *config.C return i.serveSingleFile(w, r, c, u) } -func (i *FileInfo) serveSingleFile(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { +func (i *Info) serveSingleFile(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { err := i.Read() if err != nil { code := http.StatusInternalServerError @@ -129,7 +120,7 @@ func (i *FileInfo) serveSingleFile(w http.ResponseWriter, r *http.Request, c *co }, } - if (canBeEdited(i.Name()) || i.Type == "text") && u.AllowEdit { + if i.CanBeEdited() && u.AllowEdit { p.Data, err = i.GetEditor() if err != nil { return http.StatusInternalServerError, err @@ -141,7 +132,7 @@ func (i *FileInfo) serveSingleFile(w http.ResponseWriter, r *http.Request, c *co return p.PrintAsHTML(w, "single") } -func SimplifyMimeType(name string) string { +func simplifyMediaType(name string) string { if strings.HasPrefix(name, "video") { return "video" } diff --git a/listing.go b/file/listing.go similarity index 94% rename from listing.go rename to file/listing.go index e57be46f..c7e2c7eb 100644 --- a/listing.go +++ b/file/listing.go @@ -1,4 +1,4 @@ -package filemanager +package file import ( "encoding/json" @@ -25,7 +25,7 @@ type Listing struct { // The full path of the request Path string // The items (files and folders) in the path - Items []FileInfo + Items []Info // The number of directories in the listing NumDirs int // The number of files (items that aren't directories) in the listing @@ -149,7 +149,7 @@ func (l Listing) applySort() { } } -func (i *FileInfo) serveListing(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { +func (i *Info) serveListing(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { var err error file, err := u.FileSystem.OpenFile(i.VirtualPath, os.O_RDONLY, 0) @@ -223,7 +223,7 @@ func (i *FileInfo) serveListing(w http.ResponseWriter, r *http.Request, c *confi return page.PrintAsHTML(w, "listing") } -func (i FileInfo) loadDirectoryContents(file http.File, path string, u *config.User) (*Listing, error) { +func (i Info) loadDirectoryContents(file http.File, path string, u *config.User) (*Listing, error) { files, err := file.Readdir(-1) if err != nil { return nil, err @@ -235,7 +235,7 @@ func (i FileInfo) loadDirectoryContents(file http.File, path string, u *config.U func directoryListing(files []os.FileInfo, urlPath string, basePath string, u *config.User) Listing { var ( - fileinfos []FileInfo + fileinfos []Info dirCount, fileCount int ) @@ -251,7 +251,7 @@ func directoryListing(files []os.FileInfo, urlPath string, basePath string, u *c // Absolute URL url := url.URL{Path: basePath + name} - fileinfos = append(fileinfos, FileInfo{ + fileinfos = append(fileinfos, Info{ FileInfo: f, URL: url.String(), UserAllowed: u.Allowed(url.String()), diff --git a/filemanager.go b/filemanager.go index 8532159f..b5826498 100644 --- a/filemanager.go +++ b/filemanager.go @@ -16,7 +16,7 @@ import ( "github.com/hacdias/caddy-filemanager/assets" "github.com/hacdias/caddy-filemanager/config" - "github.com/hacdias/caddy-filemanager/errors" + "github.com/hacdias/caddy-filemanager/file" "github.com/hacdias/caddy-filemanager/page" "github.com/mholt/caddy/caddyhttp/httpserver" ) @@ -32,7 +32,7 @@ type FileManager struct { func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { var ( c *config.Config - fi *FileInfo + fi *file.Info code int err error user *config.User @@ -71,7 +71,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err } if r.Method == http.MethodPut { - _, err = fi.Update(w, r, c, user) + _, err = processPUT(w, r, c, user, fi) if err != nil { return http.StatusInternalServerError, err } @@ -83,7 +83,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.BaseURL)) { if r.Method == http.MethodGet { - return errors.PrintHTML( + return page.PrintErrorHTML( w, http.StatusForbidden, e.New("You don't have permission to access this page."), @@ -95,10 +95,10 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err if r.Method == http.MethodGet { // Gets the information of the directory/file - fi, code, err = GetInfo(r.URL, c, user) + fi, code, err = file.GetInfo(r.URL, c, user) if err != nil { if r.Method == http.MethodGet { - return errors.PrintHTML(w, code, err) + return page.PrintErrorHTML(w, code, err) } return code, err } @@ -131,7 +131,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err code, err := fi.ServeHTTP(w, r, c, user) if err != nil { - return errors.PrintHTML(w, code, err) + return page.PrintErrorHTML(w, code, err) } return code, err } diff --git a/frontmatter/runes.go b/frontmatter/runes.go new file mode 100644 index 00000000..65d0ddde --- /dev/null +++ b/frontmatter/runes.go @@ -0,0 +1,24 @@ +package frontmatter + +import "strings" + +// HasRune checks if the file has the frontmatter rune +func HasRune(file []byte) bool { + return strings.HasPrefix(string(file), "---") || + strings.HasPrefix(string(file), "+++") || + strings.HasPrefix(string(file), "{") +} + +// AppendRune appends the frontmatter rune to a file +func AppendRune(frontmatter []byte, language string) []byte { + switch language { + case "yaml": + return []byte("---\n" + string(frontmatter) + "\n---") + case "toml": + return []byte("+++\n" + string(frontmatter) + "\n+++") + case "json": + return frontmatter + } + + return frontmatter +} diff --git a/errors/errors.go b/page/error.go similarity index 86% rename from errors/errors.go rename to page/error.go index b859a345..fea2debf 100644 --- a/errors/errors.go +++ b/page/error.go @@ -1,4 +1,4 @@ -package errors +package page import ( "net/http" @@ -6,7 +6,7 @@ import ( "strings" ) -const template = ` +const errTemplate = ` TITLE @@ -48,9 +48,9 @@ const template = ` ` -// PrintHTML prints the error page -func PrintHTML(w http.ResponseWriter, code int, err error) (int, error) { - tpl := template +// PrintErrorHTML prints the error page +func PrintErrorHTML(w http.ResponseWriter, code int, err error) (int, error) { + tpl := errTemplate tpl = strings.Replace(tpl, "TITLE", strconv.Itoa(code)+" "+http.StatusText(code), -1) tpl = strings.Replace(tpl, "CODE", err.Error(), -1) diff --git a/preput.go b/preprocess.go similarity index 93% rename from preput.go rename to preprocess.go index f2285910..0d039252 100644 --- a/preput.go +++ b/preprocess.go @@ -11,11 +11,12 @@ import ( "strings" "github.com/hacdias/caddy-filemanager/config" + "github.com/hacdias/caddy-filemanager/file" "github.com/spf13/hugo/parser" ) -// Update is used to update a file that was edited -func (i *FileInfo) Update(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { +// processPUT is used to update a file that was edited +func processPUT(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User, i *file.Info) (int, error) { var ( data map[string]interface{} file []byte