solve some back end bugs
Former-commit-id: de26609879e875b21de329588ecd1dcb44d152f3 [formerly 78b120bf0d33345808a422980db55d33c52304b7] [formerly d77c47bb41c1a7bf6ee2b2522bf7c86638d087bc [formerly 2819ab24b85a578dc1f572f1e64eccc98cabd6c3]] Former-commit-id: 3ddee564ddd5ed4fde01ed95f30386115a07df78 [formerly a3a1da0357874b7d99a88bd785c8f60f8170f663] Former-commit-id: 0e15a59e28993f8178fa9409a466ce93a9936906
This commit is contained in:
parent
a7b50c2de1
commit
bff33c2c1e
|
@ -75,22 +75,6 @@ buttons.setDone = function (name, success = true) {
|
||||||
* EVENTS *
|
* EVENTS *
|
||||||
* *
|
* *
|
||||||
* * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * */
|
||||||
function closePrompt (event) {
|
|
||||||
let prompt = document.querySelector('.prompt')
|
|
||||||
|
|
||||||
if (!prompt) return
|
|
||||||
|
|
||||||
if (typeof event !== 'undefined') {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelector('.overlay').classList.remove('active')
|
|
||||||
prompt.classList.remove('active')
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
prompt.remove()
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
function notImplemented (event) {
|
function notImplemented (event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@ -194,26 +178,7 @@ function deleteEvent (event) {
|
||||||
* * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function (event) {
|
document.addEventListener('DOMContentLoaded', function (event) {
|
||||||
overlay = document.querySelector('.overlay')
|
|
||||||
clickOverlay = document.querySelector('#click-overlay')
|
|
||||||
|
|
||||||
buttons.logout = document.getElementById('logout')
|
|
||||||
buttons.delete = document.getElementById('delete')
|
|
||||||
buttons.previous = document.getElementById('previous')
|
|
||||||
buttons.info = document.getElementById('info')
|
|
||||||
|
|
||||||
// Attach event listeners
|
|
||||||
buttons.logout.addEventListener('click', logoutEvent)
|
|
||||||
buttons.info.addEventListener('click', infoEvent)
|
|
||||||
|
|
||||||
templates.question = document.querySelector('#question-template')
|
|
||||||
templates.info = document.querySelector('#info-template')
|
|
||||||
templates.message = document.querySelector('#message-template')
|
|
||||||
templates.move = document.querySelector('#move-template')
|
|
||||||
|
|
||||||
if (data.user.AllowEdit) {
|
|
||||||
buttons.delete.addEventListener('click', deleteEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
let dropdownButtons = document.querySelectorAll('.action[data-dropdown]')
|
let dropdownButtons = document.querySelectorAll('.action[data-dropdown]')
|
||||||
Array.from(dropdownButtons).forEach(button => {
|
Array.from(dropdownButtons).forEach(button => {
|
||||||
|
@ -228,15 +193,6 @@ document.addEventListener('DOMContentLoaded', function (event) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
overlay.addEventListener('click', event => {
|
|
||||||
if (document.querySelector('.help.active')) {
|
|
||||||
closeHelp(event)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
closePrompt(event)
|
|
||||||
})
|
|
||||||
|
|
||||||
let mainActions = document.getElementById('main-actions')
|
let mainActions = document.getElementById('main-actions')
|
||||||
|
|
||||||
document.getElementById('more').addEventListener('click', event => {
|
document.getElementById('more').addEventListener('click', event => {
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<info-prompt v-show="showInfo" :class="{ active: showInfo }"></info-prompt>
|
<info-prompt v-show="showInfo" :class="{ active: showInfo }"></info-prompt>
|
||||||
<help v-show="showHelp" :class="{ active: showHelp }"></help>
|
<help v-show="showHelp" :class="{ active: showHelp }"></help>
|
||||||
|
|
||||||
<div v-show="showOverlay()" class="overlay" :class="{ active: showOverlay() }"></div>
|
<div v-show="showOverlay()" @click="resetPrompts" class="overlay" :class="{ active: showOverlay() }"></div>
|
||||||
|
|
||||||
<footer>Served with <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
|
<footer>Served with <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,14 +78,18 @@ function updateColumnSizes () {
|
||||||
items.style.width = `calc(${100 / columns}% - 1em)`
|
items.style.width = `calc(${100 / columns}% - 1em)`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetPrompts () {
|
||||||
|
window.info.showHelp = false
|
||||||
|
window.info.showInfo = false
|
||||||
|
window.info.showDelete = false
|
||||||
|
window.info.showRename = false
|
||||||
|
window.info.showMove = false
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('keydown', (event) => {
|
window.addEventListener('keydown', (event) => {
|
||||||
// Esc!
|
// Esc!
|
||||||
if (event.keyCode === 27) {
|
if (event.keyCode === 27) {
|
||||||
window.info.showHelp = false
|
resetPrompts()
|
||||||
window.info.showInfo = false
|
|
||||||
window.info.showDelete = false
|
|
||||||
window.info.showRename = false
|
|
||||||
window.info.showMove = false
|
|
||||||
|
|
||||||
// Unselect all files and folders.
|
// Unselect all files and folders.
|
||||||
if (window.info.req.kind === 'listing') {
|
if (window.info.req.kind === 'listing') {
|
||||||
|
@ -166,7 +170,8 @@ export default {
|
||||||
showUpload: function () {
|
showUpload: function () {
|
||||||
if (this.req.kind === 'editor') return false
|
if (this.req.kind === 'editor') return false
|
||||||
return this.user.allowNew
|
return this.user.allowNew
|
||||||
}
|
},
|
||||||
|
resetPrompts: resetPrompts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
121
editor.go
121
editor.go
|
@ -1,121 +0,0 @@
|
||||||
package filemanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hacdias/filemanager/frontmatter"
|
|
||||||
"github.com/spf13/hugo/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
// editor contains the information to fill the editor template.
|
|
||||||
type editor struct {
|
|
||||||
*fileInfo
|
|
||||||
Class string `json:"class"`
|
|
||||||
Mode string `json:"mode"`
|
|
||||||
Visual bool `json:"visual"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
FrontMatter struct {
|
|
||||||
Content *frontmatter.Content
|
|
||||||
Rune rune
|
|
||||||
} `json:"frontmatter"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// getEditor gets the editor based on a Info struct
|
|
||||||
func getEditor(r *http.Request, i *fileInfo) (*editor, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Create a new editor variable and set the mode
|
|
||||||
e := &editor{fileInfo: i}
|
|
||||||
e.Mode = editorMode(i.Name)
|
|
||||||
e.Class = editorClass(e.Mode)
|
|
||||||
|
|
||||||
if e.Class == "frontmatter-only" || e.Class == "complete" {
|
|
||||||
e.Visual = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.URL.Query().Get("visual") == "false" {
|
|
||||||
e.Class = "content-only"
|
|
||||||
}
|
|
||||||
|
|
||||||
hasRune := frontmatter.HasRune(i.content)
|
|
||||||
|
|
||||||
if e.Class == "frontmatter-only" && !hasRune {
|
|
||||||
e.FrontMatter.Rune, err = frontmatter.StringFormatToRune(e.Mode)
|
|
||||||
if err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
i.content = frontmatter.AppendRune(i.content, e.FrontMatter.Rune)
|
|
||||||
hasRune = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Class == "frontmatter-only" && hasRune {
|
|
||||||
e.FrontMatter.Content, _, err = frontmatter.Pretty(i.content)
|
|
||||||
if err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Class == "complete" && hasRune {
|
|
||||||
var page parser.Page
|
|
||||||
// Starts a new buffer and parses the file using Hugo's functions
|
|
||||||
buffer := bytes.NewBuffer(i.content)
|
|
||||||
page, err = parser.ReadFrom(buffer)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
goto Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses the page content and the frontmatter
|
|
||||||
e.Content = strings.TrimSpace(string(page.Content()))
|
|
||||||
e.FrontMatter.Rune = rune(i.content[0])
|
|
||||||
e.FrontMatter.Content, _, err = frontmatter.Pretty(page.FrontMatter())
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Class == "complete" && !hasRune {
|
|
||||||
err = errors.New("Complete but without rune")
|
|
||||||
}
|
|
||||||
|
|
||||||
Error:
|
|
||||||
if e.Class == "content-only" || err != nil {
|
|
||||||
e.Class = "content-only"
|
|
||||||
e.Content = i.StringifyContent()
|
|
||||||
}
|
|
||||||
|
|
||||||
return e, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func editorClass(mode string) string {
|
|
||||||
switch mode {
|
|
||||||
case "json", "toml", "yaml":
|
|
||||||
return "frontmatter-only"
|
|
||||||
case "markdown", "asciidoc", "rst":
|
|
||||||
return "complete"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "content-only"
|
|
||||||
}
|
|
||||||
|
|
||||||
func editorMode(filename string) string {
|
|
||||||
mode := strings.TrimPrefix(filepath.Ext(filename), ".")
|
|
||||||
|
|
||||||
switch mode {
|
|
||||||
case "md", "markdown", "mdown", "mmark":
|
|
||||||
mode = "markdown"
|
|
||||||
case "asciidoc", "adoc", "ad":
|
|
||||||
mode = "asciidoc"
|
|
||||||
case "rst":
|
|
||||||
mode = "rst"
|
|
||||||
case "html", "htm":
|
|
||||||
mode = "html"
|
|
||||||
case "js":
|
|
||||||
mode = "javascript"
|
|
||||||
case "go":
|
|
||||||
mode = "golang"
|
|
||||||
}
|
|
||||||
|
|
||||||
return mode
|
|
||||||
}
|
|
219
file.go
219
file.go
|
@ -1,6 +1,7 @@
|
||||||
package filemanager
|
package filemanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
|
@ -19,16 +20,17 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hacdias/filemanager/frontmatter"
|
||||||
|
"github.com/spf13/hugo/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInvalidOption = errors.New("Invalid option")
|
errInvalidOption = errors.New("Invalid option")
|
||||||
)
|
)
|
||||||
|
|
||||||
// fileInfo contains the information about a particular file or directory.
|
// file contains the information about a particular file or directory.
|
||||||
type fileInfo struct {
|
type file struct {
|
||||||
// Used to store the file's content temporarily.
|
|
||||||
content []byte
|
|
||||||
// The name of the file.
|
// The name of the file.
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// The Size of the file.
|
// The Size of the file.
|
||||||
|
@ -50,14 +52,17 @@ type fileInfo struct {
|
||||||
// Indicates the file content type: video, text, image, music or blob.
|
// Indicates the file content type: video, text, image, music or blob.
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
// Stores the content of a text file.
|
// Stores the content of a text file.
|
||||||
Content string `json:"content"`
|
Content string `json:"content,omitempty"`
|
||||||
|
|
||||||
|
Editor *editor `json:"editor,omitempty"`
|
||||||
|
|
||||||
|
*listing `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// A listing is the context used to fill out a template.
|
// A listing is the context used to fill out a template.
|
||||||
type listing struct {
|
type listing struct {
|
||||||
*fileInfo
|
|
||||||
// The items (files and folders) in the path.
|
// The items (files and folders) in the path.
|
||||||
Items []fileInfo `json:"items"`
|
Items []file `json:"items"`
|
||||||
// The number of directories in the listing.
|
// The number of directories in the listing.
|
||||||
NumDirs int `json:"numDirs"`
|
NumDirs int `json:"numDirs"`
|
||||||
// The number of files (items that aren't directories) in the listing.
|
// The number of files (items that aren't directories) in the listing.
|
||||||
|
@ -66,17 +71,30 @@ type listing struct {
|
||||||
Sort string `json:"sort"`
|
Sort string `json:"sort"`
|
||||||
// And which order.
|
// And which order.
|
||||||
Order string `json:"order"`
|
Order string `json:"order"`
|
||||||
// If ≠0 then Items have been limited to that many elements.
|
// Displays in mosaic or list.
|
||||||
ItemsLimitedTo int `json:"ItemsLimitedTo"`
|
Display string `json:"display"`
|
||||||
Display string `json:"display"`
|
}
|
||||||
|
|
||||||
|
// editor contains the information to fill the editor template.
|
||||||
|
type editor struct {
|
||||||
|
// Indicates if the content has only frontmatter, only content, or both.
|
||||||
|
Mode string `json:"type"`
|
||||||
|
// File content language.
|
||||||
|
Language string `json:"language"`
|
||||||
|
// This indicates if the editor should be visual or not.
|
||||||
|
Visual bool `json:"visual"`
|
||||||
|
FrontMatter struct {
|
||||||
|
Content *frontmatter.Content `json:"content"`
|
||||||
|
Rune rune `json:"rune"`
|
||||||
|
} `json:"frontmatter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInfo gets the file information and, in case of error, returns the
|
// getInfo gets the file information and, in case of error, returns the
|
||||||
// respective HTTP error code
|
// respective HTTP error code
|
||||||
func getInfo(url *url.URL, c *FileManager, u *User) (*fileInfo, error) {
|
func getInfo(url *url.URL, c *FileManager, u *User) (*file, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
i := &fileInfo{URL: c.RootURL() + url.Path}
|
i := &file{URL: c.RootURL() + url.Path}
|
||||||
i.VirtualPath = url.Path
|
i.VirtualPath = url.Path
|
||||||
i.VirtualPath = strings.TrimPrefix(i.VirtualPath, "/")
|
i.VirtualPath = strings.TrimPrefix(i.VirtualPath, "/")
|
||||||
i.VirtualPath = "/" + i.VirtualPath
|
i.VirtualPath = "/" + i.VirtualPath
|
||||||
|
@ -99,29 +117,31 @@ func getInfo(url *url.URL, c *FileManager, u *User) (*fileInfo, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getListing gets the information about a specific directory and its files.
|
// getListing gets the information about a specific directory and its files.
|
||||||
func getListing(u *User, filePath string, baseURL string, i *fileInfo) (*listing, error) {
|
func (i *file) getListing(c *requestContext, r *http.Request) error {
|
||||||
|
baseURL := c.fm.RootURL() + r.URL.Path
|
||||||
|
|
||||||
// Gets the directory information using the Virtual File System of
|
// Gets the directory information using the Virtual File System of
|
||||||
// the user configuration.
|
// the user configuration.
|
||||||
file, err := u.fileSystem.OpenFile(context.TODO(), filePath, os.O_RDONLY, 0)
|
f, err := c.us.fileSystem.OpenFile(context.TODO(), c.fi.VirtualPath, os.O_RDONLY, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer f.Close()
|
||||||
|
|
||||||
// Reads the directory and gets the information about the files.
|
// Reads the directory and gets the information about the files.
|
||||||
files, err := file.Readdir(-1)
|
files, err := f.Readdir(-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fileinfos []fileInfo
|
fileinfos []file
|
||||||
dirCount, fileCount int
|
dirCount, fileCount int
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
name := f.Name()
|
name := f.Name()
|
||||||
allowed := u.Allowed("/" + name)
|
allowed := c.us.Allowed("/" + name)
|
||||||
|
|
||||||
if !allowed {
|
if !allowed {
|
||||||
continue
|
continue
|
||||||
|
@ -137,7 +157,7 @@ func getListing(u *User, filePath string, baseURL string, i *fileInfo) (*listing
|
||||||
// Absolute URL
|
// Absolute URL
|
||||||
url := url.URL{Path: baseURL + name}
|
url := url.URL{Path: baseURL + name}
|
||||||
|
|
||||||
i := fileInfo{
|
i := file{
|
||||||
Name: f.Name(),
|
Name: f.Name(),
|
||||||
Size: f.Size(),
|
Size: f.Size(),
|
||||||
ModTime: f.ModTime(),
|
ModTime: f.ModTime(),
|
||||||
|
@ -150,29 +170,101 @@ func getListing(u *User, filePath string, baseURL string, i *fileInfo) (*listing
|
||||||
fileinfos = append(fileinfos, i)
|
fileinfos = append(fileinfos, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &listing{
|
i.listing = &listing{
|
||||||
fileInfo: i,
|
|
||||||
Items: fileinfos,
|
Items: fileinfos,
|
||||||
NumDirs: dirCount,
|
NumDirs: dirCount,
|
||||||
NumFiles: fileCount,
|
NumFiles: fileCount,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEditor gets the editor based on a Info struct
|
||||||
|
func (i *file) getEditor(r *http.Request) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Create a new editor variable and set the mode
|
||||||
|
e := &editor{
|
||||||
|
Language: editorLanguage(i.Extension),
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Mode = editorMode(e.Language)
|
||||||
|
|
||||||
|
if e.Mode == "frontmatter-only" || e.Mode == "complete" {
|
||||||
|
e.Visual = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.URL.Query().Get("visual") == "false" {
|
||||||
|
e.Mode = "content-only"
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRune := frontmatter.HasRune(i.Content)
|
||||||
|
|
||||||
|
if e.Mode == "frontmatter-only" && !hasRune {
|
||||||
|
e.FrontMatter.Rune, err = frontmatter.StringFormatToRune(e.Mode)
|
||||||
|
if err != nil {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
i.Content = frontmatter.AppendRune(i.Content, e.FrontMatter.Rune)
|
||||||
|
hasRune = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Mode == "frontmatter-only" && hasRune {
|
||||||
|
e.FrontMatter.Content, _, err = frontmatter.Pretty([]byte(i.Content))
|
||||||
|
if err != nil {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Mode == "complete" && hasRune {
|
||||||
|
var page parser.Page
|
||||||
|
content := []byte(i.Content)
|
||||||
|
// Starts a new buffer and parses the file using Hugo's functions
|
||||||
|
|
||||||
|
buffer := bytes.NewBuffer(content)
|
||||||
|
page, err = parser.ReadFrom(buffer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses the page content and the frontmatter
|
||||||
|
i.Content = strings.TrimSpace(string(page.Content()))
|
||||||
|
e.FrontMatter.Rune = rune(content[0])
|
||||||
|
e.FrontMatter.Content, _, err = frontmatter.Pretty(page.FrontMatter())
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Mode == "complete" && !hasRune {
|
||||||
|
err = errors.New("Complete but without rune")
|
||||||
|
}
|
||||||
|
|
||||||
|
Error:
|
||||||
|
if e.Mode == "content-only" || err != nil {
|
||||||
|
e.Mode = "content-only"
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Editor = e
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveFileType obtains the mimetype and converts it to a simple
|
// RetrieveFileType obtains the mimetype and converts it to a simple
|
||||||
// type nomenclature.
|
// type nomenclature.
|
||||||
func (i *fileInfo) RetrieveFileType() error {
|
func (i *file) RetrieveFileType() error {
|
||||||
|
var content []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
// Tries to get the file mimetype using its extension.
|
// Tries to get the file mimetype using its extension.
|
||||||
mimetype := mime.TypeByExtension(i.Extension)
|
mimetype := mime.TypeByExtension(i.Extension)
|
||||||
|
|
||||||
if mimetype == "" {
|
if mimetype == "" {
|
||||||
err := i.Read()
|
content, err = ioutil.ReadFile(i.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tries to get the file mimetype using its first
|
// Tries to get the file mimetype using its first
|
||||||
// 512 bytes.
|
// 512 bytes.
|
||||||
mimetype = http.DetectContentType(i.content)
|
mimetype = http.DetectContentType(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(mimetype, "video") {
|
if strings.HasPrefix(mimetype, "video") {
|
||||||
|
@ -192,12 +284,12 @@ func (i *fileInfo) RetrieveFileType() error {
|
||||||
|
|
||||||
if strings.HasPrefix(mimetype, "text") {
|
if strings.HasPrefix(mimetype, "text") {
|
||||||
i.Type = "text"
|
i.Type = "text"
|
||||||
return nil
|
goto End
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(mimetype, "application/javascript") {
|
if strings.HasPrefix(mimetype, "application/javascript") {
|
||||||
i.Type = "text"
|
i.Type = "text"
|
||||||
return nil
|
goto End
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the type isn't text (and is blob for example), it will check some
|
// If the type isn't text (and is blob for example), it will check some
|
||||||
|
@ -210,24 +302,24 @@ func (i *fileInfo) RetrieveFileType() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
i.Type = "blob"
|
i.Type = "blob"
|
||||||
|
|
||||||
|
End:
|
||||||
|
// If the file type is text, save its content.
|
||||||
|
if i.Type == "text" {
|
||||||
|
if len(content) == 0 {
|
||||||
|
content, err = ioutil.ReadFile(i.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Content = string(content)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads the file.
|
func (i file) Checksum(kind string) (string, error) {
|
||||||
func (i *fileInfo) Read() error {
|
|
||||||
if len(i.content) != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
i.content, err = ioutil.ReadFile(i.Path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i fileInfo) Checksum(kind string) (string, error) {
|
|
||||||
file, err := os.Open(i.Path)
|
file, err := os.Open(i.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -258,13 +350,8 @@ func (i fileInfo) Checksum(kind string) (string, error) {
|
||||||
return hex.EncodeToString(h.Sum(nil)), nil
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringifyContent returns a string with the file content.
|
|
||||||
func (i fileInfo) StringifyContent() string {
|
|
||||||
return string(i.content)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanBeEdited checks if the extension of a file is supported by the editor
|
// CanBeEdited checks if the extension of a file is supported by the editor
|
||||||
func (i fileInfo) CanBeEdited() bool {
|
func (i file) CanBeEdited() bool {
|
||||||
return i.Type == "text"
|
return i.Type == "text"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,3 +460,35 @@ var textExtensions = [...]string{
|
||||||
".c", ".cc", ".h", ".hh", ".cpp", ".hpp", ".f90",
|
".c", ".cc", ".h", ".hh", ".cpp", ".hpp", ".f90",
|
||||||
".f", ".bas", ".d", ".ada", ".nim", ".cr", ".java", ".cs", ".vala", ".vapi",
|
".f", ".bas", ".d", ".ada", ".nim", ".cr", ".java", ".cs", ".vala", ".vapi",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func editorMode(language string) string {
|
||||||
|
switch language {
|
||||||
|
case "json", "toml", "yaml":
|
||||||
|
return "frontmatter-only"
|
||||||
|
case "markdown", "asciidoc", "rst":
|
||||||
|
return "complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "content-only"
|
||||||
|
}
|
||||||
|
|
||||||
|
func editorLanguage(mode string) string {
|
||||||
|
mode = strings.TrimPrefix(".", mode)
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case "md", "markdown", "mdown", "mmark":
|
||||||
|
mode = "markdown"
|
||||||
|
case "asciidoc", "adoc", "ad":
|
||||||
|
mode = "asciidoc"
|
||||||
|
case "rst":
|
||||||
|
mode = "rst"
|
||||||
|
case "html", "htm":
|
||||||
|
mode = "html"
|
||||||
|
case "js":
|
||||||
|
mode = "javascript"
|
||||||
|
case "go":
|
||||||
|
mode = "golang"
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode
|
||||||
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ type Block struct {
|
||||||
Type string
|
Type string
|
||||||
HTMLType string
|
HTMLType string
|
||||||
Content *Content
|
Content *Content
|
||||||
Parent *Block
|
Parent *Block `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func rawToPretty(config interface{}, parent *Block) *Content {
|
func rawToPretty(config interface{}, parent *Block) *Content {
|
||||||
|
|
|
@ -1,29 +1,28 @@
|
||||||
package frontmatter
|
package frontmatter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HasRune checks if the file has the frontmatter rune
|
// HasRune checks if the file has the frontmatter rune
|
||||||
func HasRune(file []byte) bool {
|
func HasRune(file string) bool {
|
||||||
return strings.HasPrefix(string(file), "---") ||
|
return strings.HasPrefix(file, "---") ||
|
||||||
strings.HasPrefix(string(file), "+++") ||
|
strings.HasPrefix(file, "+++") ||
|
||||||
strings.HasPrefix(string(file), "{")
|
strings.HasPrefix(file, "{")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendRune appends the frontmatter rune to a file
|
// AppendRune appends the frontmatter rune to a file
|
||||||
func AppendRune(frontmatter []byte, mark rune) []byte {
|
func AppendRune(frontmatter string, mark rune) string {
|
||||||
frontmatter = bytes.TrimSpace(frontmatter)
|
frontmatter = strings.TrimSpace(frontmatter)
|
||||||
|
|
||||||
switch mark {
|
switch mark {
|
||||||
case '-':
|
case '-':
|
||||||
return []byte("---\n" + string(frontmatter) + "\n---")
|
return "---\n" + frontmatter + "\n---"
|
||||||
case '+':
|
case '+':
|
||||||
return []byte("+++\n" + string(frontmatter) + "\n+++")
|
return "+++\n" + frontmatter + "\n+++"
|
||||||
case '{':
|
case '{':
|
||||||
return []byte("{\n" + string(frontmatter) + "\n}")
|
return "{\n" + frontmatter + "\n}"
|
||||||
}
|
}
|
||||||
|
|
||||||
return frontmatter
|
return frontmatter
|
||||||
|
|
4
http.go
4
http.go
|
@ -15,7 +15,7 @@ const assetsURL = "/_"
|
||||||
type requestContext struct {
|
type requestContext struct {
|
||||||
us *User
|
us *User
|
||||||
fm *FileManager
|
fm *FileManager
|
||||||
fi *fileInfo
|
fi *file
|
||||||
pg *page
|
pg *page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func serveHTTP(c *requestContext, w http.ResponseWriter, r *http.Request) (int,
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == http.MethodGet {
|
if r.Method == http.MethodGet {
|
||||||
var f *fileInfo
|
var f *file
|
||||||
|
|
||||||
// Obtains the information of the directory/file.
|
// Obtains the information of the directory/file.
|
||||||
f, err = getInfo(r.URL, c.fm, c.us)
|
f, err = getInfo(r.URL, c.fm, c.us)
|
||||||
|
|
10
page.go
10
page.go
|
@ -26,13 +26,11 @@ type page struct {
|
||||||
User *User `json:"-"`
|
User *User `json:"-"`
|
||||||
BaseURL string `json:"-"`
|
BaseURL string `json:"-"`
|
||||||
WebDavURL string `json:"-"`
|
WebDavURL string `json:"-"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
Name string `json:"name"`
|
Data *file `json:"data"`
|
||||||
Path string `json:"path"`
|
|
||||||
Kind string `json:"kind"` // listing, editor or preview
|
|
||||||
Data interface{} `json:"data"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// breadcrumbItem contains the Name and the URL of a breadcrumb piece.
|
// breadcrumbItem contains the Name and the URL of a breadcrumb piece.
|
||||||
type breadcrumbItem struct {
|
type breadcrumbItem struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -90,7 +88,7 @@ func (p page) PreviousLink() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
return path
|
return path
|
||||||
}
|
} */
|
||||||
|
|
||||||
func (p page) Render(c *requestContext, w http.ResponseWriter, r *http.Request) (int, error) {
|
func (p page) Render(c *requestContext, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
if strings.Contains(r.Header.Get("Accept"), "application/json") {
|
if strings.Contains(r.Header.Get("Accept"), "application/json") {
|
||||||
|
|
2
put.go
2
put.go
|
@ -128,7 +128,7 @@ func parseCompleteFile(data map[string]interface{}, filename string, mark rune)
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
front = frontmatter.AppendRune(front, mark)
|
front = []byte(frontmatter.AppendRune(string(front), mark))
|
||||||
|
|
||||||
// Generates the final file
|
// Generates the final file
|
||||||
f := new(bytes.Buffer)
|
f := new(bytes.Buffer)
|
||||||
|
|
49
serve.go
49
serve.go
|
@ -2,18 +2,17 @@ package filemanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func serveDefault(c *requestContext, w http.ResponseWriter, r *http.Request) (int, error) {
|
func serveDefault(c *requestContext, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Starts building the page.
|
||||||
c.pg = &page{
|
c.pg = &page{
|
||||||
Name: c.fi.Name,
|
|
||||||
Path: c.fi.VirtualPath,
|
|
||||||
User: c.us,
|
User: c.us,
|
||||||
BaseURL: c.fm.RootURL(),
|
BaseURL: c.fm.RootURL(),
|
||||||
WebDavURL: c.fm.WebDavURL(),
|
WebDavURL: c.fm.WebDavURL(),
|
||||||
|
Data: c.fi,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is a dir, go and serve the listing.
|
// If it is a dir, go and serve the listing.
|
||||||
|
@ -26,27 +25,15 @@ func serveDefault(c *requestContext, w http.ResponseWriter, r *http.Request) (in
|
||||||
return errorToHTTP(err, true), err
|
return errorToHTTP(err, true), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is a text file, reads its content.
|
|
||||||
if c.fi.Type == "text" {
|
|
||||||
if err = c.fi.Read(); err != nil {
|
|
||||||
return errorToHTTP(err, true), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it can't be edited or the user isn't allowed to,
|
// If it can't be edited or the user isn't allowed to,
|
||||||
// serve it as a listing, with a preview of the file.
|
// serve it as a listing, with a preview of the file.
|
||||||
if !c.fi.CanBeEdited() || !c.us.AllowEdit {
|
if !c.fi.CanBeEdited() || !c.us.AllowEdit {
|
||||||
if c.fi.Type == "text" {
|
|
||||||
c.fi.Content = string(c.fi.content)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.pg.Kind = "preview"
|
c.pg.Kind = "preview"
|
||||||
c.pg.Data = c.fi
|
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we just bring the editor in!
|
// Otherwise, we just bring the editor in!
|
||||||
c.pg.Kind = "editor"
|
c.pg.Kind = "editor"
|
||||||
|
|
||||||
c.pg.Data, err = getEditor(r, c.fi)
|
err = c.fi.getEditor(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
|
@ -57,40 +44,31 @@ func serveDefault(c *requestContext, w http.ResponseWriter, r *http.Request) (in
|
||||||
|
|
||||||
// serveListing presents the user with a listage of a directory folder.
|
// serveListing presents the user with a listage of a directory folder.
|
||||||
func serveListing(c *requestContext, w http.ResponseWriter, r *http.Request) (int, error) {
|
func serveListing(c *requestContext, w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
var (
|
var err error
|
||||||
err error
|
|
||||||
listing *listing
|
|
||||||
)
|
|
||||||
|
|
||||||
c.pg.Kind = "listing"
|
c.pg.Kind = "listing"
|
||||||
|
|
||||||
listing, err = getListing(c.us, c.fi.VirtualPath, c.fm.RootURL()+r.URL.Path, c.fi)
|
err = c.fi.getListing(c, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorToHTTP(err, true), err
|
return errorToHTTP(err, true), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listing := c.fi.listing
|
||||||
|
|
||||||
cookieScope := c.fm.RootURL()
|
cookieScope := c.fm.RootURL()
|
||||||
if cookieScope == "" {
|
if cookieScope == "" {
|
||||||
cookieScope = "/"
|
cookieScope = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the query values into the Listing struct
|
// Copy the query values into the Listing struct
|
||||||
var limit int
|
listing.Sort, listing.Order, err = handleSortOrder(w, r, cookieScope)
|
||||||
listing.Sort, listing.Order, limit, err = handleSortOrder(w, r, cookieScope)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.StatusBadRequest, err
|
return http.StatusBadRequest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
listing.ApplySort()
|
listing.ApplySort()
|
||||||
|
|
||||||
if limit > 0 && limit <= len(listing.Items) {
|
|
||||||
listing.Items = listing.Items[:limit]
|
|
||||||
listing.ItemsLimitedTo = limit
|
|
||||||
}
|
|
||||||
|
|
||||||
listing.Display = displayMode(w, r, cookieScope)
|
listing.Display = displayMode(w, r, cookieScope)
|
||||||
c.pg.Data = listing
|
|
||||||
|
|
||||||
return c.pg.Render(c, w, r)
|
return c.pg.Render(c, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +99,9 @@ func displayMode(w http.ResponseWriter, r *http.Request, scope string) string {
|
||||||
|
|
||||||
// handleSortOrder gets and stores for a Listing the 'sort' and 'order',
|
// handleSortOrder gets and stores for a Listing the 'sort' and 'order',
|
||||||
// and reads 'limit' if given. The latter is 0 if not given. Sets cookies.
|
// and reads 'limit' if given. The latter is 0 if not given. Sets cookies.
|
||||||
func handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, limit int, err error) {
|
func handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, err error) {
|
||||||
sort = r.URL.Query().Get("sort")
|
sort = r.URL.Query().Get("sort")
|
||||||
order = r.URL.Query().Get("order")
|
order = r.URL.Query().Get("order")
|
||||||
limitQuery := r.URL.Query().Get("limit")
|
|
||||||
|
|
||||||
// If the query 'sort' or 'order' is empty, use defaults or any values
|
// If the query 'sort' or 'order' is empty, use defaults or any values
|
||||||
// previously saved in Cookies.
|
// previously saved in Cookies.
|
||||||
|
@ -158,13 +135,5 @@ func handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if limitQuery != "" {
|
|
||||||
limit, err = strconv.Atoi(limitQuery)
|
|
||||||
// If the 'limit' query can't be interpreted as a number, return err.
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue