more updates and renaming

pull/144/head
Henrique Dias 2017-06-19 17:10:03 +01:00
parent 1c703e0364
commit 8c19482190
No known key found for this signature in database
GPG Key ID: 936F5EB68D786730
24 changed files with 5163 additions and 5069 deletions

1
_embed/public/ace Submodule

@ -0,0 +1 @@
Subproject commit 784ffa862c5351e0d300370f61471b1eb95ebcf1

1207
_embed/public/css/old.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
!function(e,n){"undefined"!=typeof exports&&"undefined"!=typeof module&&module.exports?module.exports=n():"function"==typeof define&&define.amd?define(n):e.form2js=n()}(this,function(){"use strict";function e(e,r,u,i,a,l){l=!!l,void 0!==u&&null!=u||(u=!0),void 0!==r&&null!=r||(r="."),arguments.length<5&&(a=!1);var o,c=[],s=0;if((e="string"==typeof e?document.getElementById(e):e).constructor==Array||"undefined"!=typeof NodeList&&e.constructor==NodeList)for(;o=e[s++];)c=c.concat(t(o,i,a,l));else c=t(e,i,a,l);return n(c,u,r)}function n(e,n,t){var r,u,i,a,l,o,c,s,f,d,h,m,g={},p={};for(r=0;r<e.length;r++)if(l=e[r].value,!n||""!==l&&null!==l){for(m=e[r].name.split(t),o=[],c=g,s="",u=0;u<m.length;u++)if((h=m[u].split("][")).length>1)for(i=0;i<h.length;i++)if(0==i?h[i]=h[i]+"]":i==h.length-1?h[i]="["+h[i]:h[i]="["+h[i]+"]",d=h[i].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i))for(a=1;a<d.length;a++)d[a]&&o.push(d[a]);else o.push(h[i]);else o=o.concat(h);for(u=0;u<o.length;u++)(h=o[u]).indexOf("[]")>-1&&u==o.length-1?(s+=f=h.substr(0,h.indexOf("[")),c[f]||(c[f]=[]),c[f].push(l)):h.indexOf("[")>-1?(p[s+="_"+(f=h.substr(0,h.indexOf("[")))+"_"+(d=h.replace(/(^([a-z_]+)?\[)|(\]$)/gi,""))]||(p[s]={}),""==f||c[f]||(c[f]=[]),u==o.length-1?""==f?(c.push(l),p[s][d]=c[c.length-1]):(c[f].push(l),p[s][d]=c[f][c[f].length-1]):p[s][d]||(/^[0-9a-z_]+\[?/i.test(o[u+1])?c[f].push({}):c[f].push([]),p[s][d]=c[f][c[f].length-1]),c=p[s][d]):(s+=h,u<o.length-1?(c[h]||(c[h]={}),c=c[h]):c[h]=l)}return g}function t(e,n,t,i){var a=u(e,n,t,i);return a.length>0?a:r(e,n,t,i)}function r(e,n,t,r){for(var i=[],a=e.firstChild;a;)i=i.concat(u(a,n,t,r)),a=a.nextSibling;return i}function u(e,n,t,u){if(e.disabled&&!u)return[];var l,o,c,s=i(e,t);return l=n&&n(e),l&&l.name?c=[l]:""!=s&&e.nodeName.match(/INPUT|TEXTAREA/i)?c=null===(o=a(e,u))?[]:[{name:s,value:o}]:""!=s&&e.nodeName.match(/SELECT/i)?(o=a(e,u),c=[{name:s.replace(/\[\]$/,""),value:o}]):c=r(e,n,t,u),c}function i(e,n){return e.name&&""!=e.name?e.name:n&&e.id&&""!=e.id?e.id:""}function a(e,n){if(e.disabled&&!n)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&"false"===e.value)return!1;case"checkbox":if(e.checked&&"true"===e.value)return!0;if(!e.checked&&"true"===e.value)return!1;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";default:return e.value}break;case"SELECT":return l(e)}return null}function l(e){var n,t,r,u=[];if(!e.multiple)return e.value;for(t=0,r=(n=e.getElementsByTagName("option")).length;t<r;t++)n[t].selected&&u.push(n[t].value);return u}return e});

View File

@ -1,356 +0,0 @@
/**
* Copyright (c) 2010 Maxim Vasiliev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author Maxim Vasiliev
* Date: 09.09.2010
* Time: 19:02:33
*/
(function (root, factory)
{
if (typeof exports !== 'undefined' && typeof module !== 'undefined' && module.exports) {
// NodeJS
module.exports = factory();
}
else if (typeof define === 'function' && define.amd)
{
// AMD. Register as an anonymous module.
define(factory);
}
else
{
// Browser globals
root.form2js = factory();
}
}(this, function ()
{
"use strict";
/**
* Returns form values represented as Javascript object
* "name" attribute defines structure of resulting object
*
* @param rootNode {Element|String} root form element (or it's id) or array of root elements
* @param delimiter {String} structure parts delimiter defaults to '.'
* @param skipEmpty {Boolean} should skip empty text values, defaults to true
* @param nodeCallback {Function} custom function to get node value
* @param useIdIfEmptyName {Boolean} if true value of id attribute of field will be used if name of field is empty
*/
function form2js(rootNode, delimiter, skipEmpty, nodeCallback, useIdIfEmptyName, getDisabled)
{
getDisabled = getDisabled ? true : false;
if (typeof skipEmpty == 'undefined' || skipEmpty == null) skipEmpty = true;
if (typeof delimiter == 'undefined' || delimiter == null) delimiter = '.';
if (arguments.length < 5) useIdIfEmptyName = false;
rootNode = typeof rootNode == 'string' ? document.getElementById(rootNode) : rootNode;
var formValues = [],
currNode,
i = 0;
/* If rootNode is array - combine values */
if (rootNode.constructor == Array || (typeof NodeList != "undefined" && rootNode.constructor == NodeList))
{
while(currNode = rootNode[i++])
{
formValues = formValues.concat(getFormValues(currNode, nodeCallback, useIdIfEmptyName, getDisabled));
}
}
else
{
formValues = getFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
}
return processNameValues(formValues, skipEmpty, delimiter);
}
/**
* Processes collection of { name: 'name', value: 'value' } objects.
* @param nameValues
* @param skipEmpty if true skips elements with value == '' or value == null
* @param delimiter
*/
function processNameValues(nameValues, skipEmpty, delimiter)
{
var result = {},
arrays = {},
i, j, k, l,
value,
nameParts,
currResult,
arrNameFull,
arrName,
arrIdx,
namePart,
name,
_nameParts;
for (i = 0; i < nameValues.length; i++)
{
value = nameValues[i].value;
if (skipEmpty && (value === '' || value === null)) continue;
name = nameValues[i].name;
_nameParts = name.split(delimiter);
nameParts = [];
currResult = result;
arrNameFull = '';
for(j = 0; j < _nameParts.length; j++)
{
namePart = _nameParts[j].split('][');
if (namePart.length > 1)
{
for(k = 0; k < namePart.length; k++)
{
if (k == 0)
{
namePart[k] = namePart[k] + ']';
}
else if (k == namePart.length - 1)
{
namePart[k] = '[' + namePart[k];
}
else
{
namePart[k] = '[' + namePart[k] + ']';
}
arrIdx = namePart[k].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);
if (arrIdx)
{
for(l = 1; l < arrIdx.length; l++)
{
if (arrIdx[l]) nameParts.push(arrIdx[l]);
}
}
else{
nameParts.push(namePart[k]);
}
}
}
else
nameParts = nameParts.concat(namePart);
}
for (j = 0; j < nameParts.length; j++)
{
namePart = nameParts[j];
if (namePart.indexOf('[]') > -1 && j == nameParts.length - 1)
{
arrName = namePart.substr(0, namePart.indexOf('['));
arrNameFull += arrName;
if (!currResult[arrName]) currResult[arrName] = [];
currResult[arrName].push(value);
}
else if (namePart.indexOf('[') > -1)
{
arrName = namePart.substr(0, namePart.indexOf('['));
arrIdx = namePart.replace(/(^([a-z_]+)?\[)|(\]$)/gi, '');
/* Unique array name */
arrNameFull += '_' + arrName + '_' + arrIdx;
/*
* Because arrIdx in field name can be not zero-based and step can be
* other than 1, we can't use them in target array directly.
* Instead we're making a hash where key is arrIdx and value is a reference to
* added array element
*/
if (!arrays[arrNameFull]) arrays[arrNameFull] = {};
if (arrName != '' && !currResult[arrName]) currResult[arrName] = [];
if (j == nameParts.length - 1)
{
if (arrName == '')
{
currResult.push(value);
arrays[arrNameFull][arrIdx] = convertValue(currResult[currResult.length - 1]);
}
else
{
currResult[arrName].push(value);
arrays[arrNameFull][arrIdx] = convertValue(currResult[arrName][currResult[arrName].length - 1]);
}
}
else
{
if (!arrays[arrNameFull][arrIdx])
{
if ((/^[0-9a-z_]+\[?/i).test(nameParts[j+1])) currResult[arrName].push({});
else currResult[arrName].push([]);
arrays[arrNameFull][arrIdx] = convertValue(currResult[arrName][currResult[arrName].length - 1]);
}
}
currResult = convertValue(arrays[arrNameFull][arrIdx]);
}
else
{
arrNameFull += namePart;
if (j < nameParts.length - 1) /* Not the last part of name - means object */
{
if (!currResult[namePart]) currResult[namePart] = {};
currResult = convertValue(currResult[namePart]);
}
else
{
currResult[namePart] = convertValue(value);
}
}
}
}
return result;
}
function convertValue(value) {
if (value == "true") return true;
if (value == "false") return false;
if (!isNaN(value)) return parseInt(value);
return value;
}
function getFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled)
{
var result = extractNodeValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
return result.length > 0 ? result : getSubFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
}
function getSubFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled)
{
var result = [],
currentNode = rootNode.firstChild;
while (currentNode)
{
result = result.concat(extractNodeValues(currentNode, nodeCallback, useIdIfEmptyName, getDisabled));
currentNode = currentNode.nextSibling;
}
return result;
}
function extractNodeValues(node, nodeCallback, useIdIfEmptyName, getDisabled) {
if (node.disabled && !getDisabled) return [];
var callbackResult, fieldValue, result, fieldName = getFieldName(node, useIdIfEmptyName);
callbackResult = nodeCallback && nodeCallback(node);
if (callbackResult && callbackResult.name) {
result = [callbackResult];
}
else if (fieldName != '' && node.nodeName.match(/INPUT|TEXTAREA/i)) {
fieldValue = getFieldValue(node, getDisabled);
if (null === fieldValue) {
result = [];
} else {
result = [ { name: fieldName, value: fieldValue} ];
}
}
else if (fieldName != '' && node.nodeName.match(/SELECT/i)) {
fieldValue = getFieldValue(node, getDisabled);
result = [ { name: fieldName.replace(/\[\]$/, ''), value: fieldValue } ];
}
else {
result = getSubFormValues(node, nodeCallback, useIdIfEmptyName, getDisabled);
}
return result;
}
function getFieldName(node, useIdIfEmptyName)
{
if (node.name && node.name != '') return node.name;
else if (useIdIfEmptyName && node.id && node.id != '') return node.id;
else return '';
}
function getFieldValue(fieldNode, getDisabled)
{
if (fieldNode.disabled && !getDisabled) return null;
switch (fieldNode.nodeName) {
case 'INPUT':
case 'TEXTAREA':
switch (fieldNode.type.toLowerCase()) {
case 'radio':
if (fieldNode.checked && fieldNode.value === "false") return false;
case 'checkbox':
if (fieldNode.checked && fieldNode.value === "true") return true;
if (!fieldNode.checked && fieldNode.value === "true") return false;
if (fieldNode.checked) return fieldNode.value;
break;
case 'button':
case 'reset':
case 'submit':
case 'image':
return '';
break;
default:
return fieldNode.value;
break;
}
break;
case 'SELECT':
return getSelectedOptionValue(fieldNode);
break;
default:
break;
}
return null;
}
function getSelectedOptionValue(selectNode)
{
var multiple = selectNode.multiple,
result = [],
options,
i, l;
if (!multiple) return selectNode.value;
for (options = selectNode.getElementsByTagName("option"), i = 0, l = options.length; i < l; i++)
{
if (options[i].selected) result.push(options[i].value);
}
return result;
}
return form2js;
}));

View File

@ -5,9 +5,9 @@
<title>{{.Name}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<meta charset="utf-8">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/normalize.css">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/fonts.css">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/styles.css">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_internal/css/normalize.css">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_internal/css/fonts.css">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_internal/css/styles.css">
{{- if ne .User.StyleSheet "" -}}
<style>{{ CSS .User.StyleSheet }}</style>
{{- end -}}
@ -18,16 +18,13 @@
baseURL = "{{.Config.AbsoluteURL}}",
prefixURL = "{{ .Config.PrefixURL }}";
</script>
<script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/common.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_internal/js/common.js" defer></script>
{{- if .IsDir }}
<script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/listing.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_internal/js/listing.js" defer></script>
{{- else }}
<script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/ace/src-min/ace.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/vendor/form2js.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/editor.js" defer></script>
{{- end }}
{{- if .Config.HugoEnabled }}
<script src="{{ .Config.AbsoluteURL }}/_hugointernal/js/application.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_internal/ace/src-min/ace.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_internal/js/form2js.js" defer></script>
<script src="{{ .Config.AbsoluteURL }}/_internal/js/editor.js" defer></script>
{{- end }}
</head>
<body>

View File

@ -10,7 +10,7 @@ import (
)
// BaseURL is the url of the assets
const BaseURL = "/_filemanagerinternal"
const BaseURL = "/_internal"
// Serve provides the needed assets for the front-end
func Serve(w http.ResponseWriter, r *http.Request, c *filemanager.Config) (int, error) {

File diff suppressed because one or more lines are too long

View File

@ -2,10 +2,6 @@ package main
import (
"net/http"
"regexp"
"strings"
"golang.org/x/net/webdav"
"github.com/hacdias/filemanager"
handlers "github.com/hacdias/filemanager/http"
@ -18,45 +14,8 @@ func handler(w http.ResponseWriter, r *http.Request) {
}
func main() {
cfg = &filemanager.Config{User: &filemanager.User{}}
cfg.Scope = "."
cfg.FileSystem = webdav.Dir(cfg.Scope)
cfg.BaseURL = "/"
cfg.HugoEnabled = false
cfg.Users = map[string]*filemanager.User{}
cfg.AllowCommands = true
cfg.AllowEdit = true
cfg.AllowNew = true
cfg.Commands = []string{"git", "svn", "hg"}
cfg.BeforeSave = func(r *http.Request, c *filemanager.Config, u *filemanager.User) error { return nil }
cfg.AfterSave = func(r *http.Request, c *filemanager.Config, u *filemanager.User) error { return nil }
cfg.Rules = []*filemanager.Rule{{
Regex: true,
Allow: false,
Regexp: regexp.MustCompile("\\/\\..+"),
}}
cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
cfg.BaseURL = "/" + cfg.BaseURL
cfg.WebDavURL = ""
if cfg.BaseURL == "/" {
cfg.BaseURL = ""
}
if cfg.WebDavURL == "" {
cfg.WebDavURL = "webdav"
}
cfg.PrefixURL = ""
cfg.WebDavURL = cfg.BaseURL + "/" + strings.TrimPrefix(cfg.WebDavURL, "/")
cfg.Handler = &webdav.Handler{
Prefix: cfg.WebDavURL,
FileSystem: cfg.FileSystem,
LockSystem: webdav.NewMemLS(),
}
cfg = filemanager.New("D:\\TEST\\")
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
}

View File

@ -5,22 +5,58 @@ import (
"regexp"
"strings"
rice "github.com/GeertJohan/go.rice"
"golang.org/x/net/webdav"
)
// CommandFunc ...
type CommandFunc func(r *http.Request, c *Config, u *User) error
// Config is a configuration for browsing in a particular path.
type Config struct {
*User
PrefixURL string
BaseURL string
WebDavURL string
HugoEnabled bool // Enables the Hugo plugin for File Manager
Users map[string]*User
BeforeSave CommandFunc
AfterSave CommandFunc
PrefixURL string // A part of the URL that is stripped from the http.Request
BaseURL string // The base URL of FileManager interface
WebDavURL string // The URL of WebDAV
Users map[string]*User
BeforeSave Command
AfterSave Command
Assets struct {
Templates *rice.Box
Static *rice.Box
}
}
// New creates a new FileManager object with the default settings
// for a certain scope.
func New(scope string) *Config {
cfg := &Config{
User: &User{
Scope: scope,
FileSystem: webdav.Dir(scope),
AllowCommands: true,
AllowEdit: true,
AllowNew: true,
Commands: []string{"git", "svn", "hg"},
Rules: []*Rule{{
Regex: true,
Allow: false,
Regexp: regexp.MustCompile("\\/\\..+"),
}},
},
Users: map[string]*User{},
BaseURL: "",
PrefixURL: "",
WebDavURL: "/webdav",
BeforeSave: func(r *http.Request, c *Config, u *User) error { return nil },
AfterSave: func(r *http.Request, c *Config, u *User) error { return nil },
}
cfg.Handler = &webdav.Handler{
Prefix: cfg.WebDavURL,
FileSystem: cfg.FileSystem,
LockSystem: webdav.NewMemLS(),
}
return cfg
}
// AbsoluteURL ...
@ -47,11 +83,11 @@ type User struct {
FileSystem webdav.FileSystem `json:"-"` // The virtual file system the user have access
Handler *webdav.Handler `json:"-"` // The WebDav HTTP Handler
StyleSheet string `json:"-"` // Costum stylesheet
Rules []*Rule `json:"-"` // Access rules
AllowNew bool // Can create files and folders
AllowEdit bool // Can edit/rename files
AllowCommands bool // Can execute commands
Commands []string // Available Commands
Rules []*Rule `json:"-"` // Access rules
}
// Allowed checks if the user has permission to access a directory/file
@ -75,3 +111,6 @@ func (u User) Allowed(url string) bool {
return true
}
// Command is a user-defined command that is executed in some moments.
type Command func(r *http.Request, c *Config, u *User) error

View File

@ -13,7 +13,6 @@ import (
"gopkg.in/yaml.v2"
"github.com/BurntSushi/toml"
"github.com/hacdias/filemanager/utils"
"github.com/spf13/cast"
)
@ -169,9 +168,9 @@ func rawToPretty(config interface{}, parent *Block) *Content {
}
for name, element := range cnf {
if utils.IsMap(element) {
if isMap(element) {
objects = append(objects, handleObjects(element, parent, name))
} else if utils.IsSlice(element) {
} else if isSlice(element) {
arrays = append(arrays, handleArrays(element, parent, name))
} else {
if name == "title" && parent.Name == mainName {

13
frontmatter/types.go Normal file
View File

@ -0,0 +1,13 @@
package frontmatter
import "reflect"
// isMap checks if some variable is a map
func isMap(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Map
}
// isSlice checks if some variable is a slice
func isSlice(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Slice
}

View File

@ -1,4 +1,4 @@
package utils
package frontmatter
import "testing"
@ -20,7 +20,7 @@ var testIsMap = []*interfaceToBool{
func TestIsMap(t *testing.T) {
for _, test := range testIsMap {
if IsMap(test.Value) != test.Result {
if isMap(test.Value) != test.Result {
t.Errorf("Incorrect value on IsMap for %v; want: %v; got: %v", test.Value, test.Result, !test.Result)
}
}
@ -42,7 +42,7 @@ var testIsSlice = []*interfaceToBool{
func TestIsSlice(t *testing.T) {
for _, test := range testIsSlice {
if IsSlice(test.Value) != test.Result {
if isSlice(test.Value) != test.Result {
t.Errorf("Incorrect value on IsSlice for %v; want: %v; got: %v", test.Value, test.Result, !test.Result)
}
}

View File

@ -13,7 +13,6 @@ import (
"os"
fm "github.com/hacdias/filemanager"
"github.com/hacdias/filemanager/utils"
)
// checksum calculates the hash of a filemanager. Supports MD5, SHA1, SHA256 and SHA512.
@ -22,7 +21,7 @@ func checksum(w http.ResponseWriter, r *http.Request, c *fm.Config, i *fm.FileIn
file, err := os.Open(i.Path)
if err != nil {
return utils.ErrorToHTTPCode(err, true), err
return errorToHTTPCode(err, true), err
}
defer file.Close()

View File

@ -10,8 +10,6 @@ import (
fm "github.com/hacdias/filemanager"
"github.com/hacdias/filemanager/assets"
"github.com/hacdias/filemanager/page"
"github.com/hacdias/filemanager/utils"
"github.com/hacdias/filemanager/wrapper"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
@ -74,7 +72,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error
r.Method = "PROPFIND"
if r.Method == "HEAD" {
w = wrapper.NewResponseWriterNoBody(w)
w = NewResponseWriterNoBody(w)
}
}
case "PROPPATCH", "MOVE", "PATCH", "PUT", "DELETE":
@ -133,7 +131,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error
if r.Method == http.MethodGet {
// Gets the information of the directory/file
fi, err = fm.GetInfo(r.URL, c, user)
code = utils.ErrorToHTTPCode(err, false)
code = errorToHTTPCode(err, false)
if err != nil {
if r.Method == http.MethodGet {
return page.PrintErrorHTML(w, code, err)
@ -171,3 +169,21 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error
return http.StatusNotImplemented, nil
}
// errorToHTTPCode converts errors to HTTP Status Code.
func errorToHTTPCode(err error, gone bool) int {
switch {
case os.IsPermission(err):
return http.StatusForbidden
case os.IsNotExist(err):
if !gone {
return http.StatusNotFound
}
return http.StatusGone
case os.IsExist(err):
return http.StatusGone
default:
return http.StatusInternalServerError
}
}

View File

@ -8,7 +8,6 @@ import (
fm "github.com/hacdias/filemanager"
"github.com/hacdias/filemanager/page"
"github.com/hacdias/filemanager/utils"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
@ -19,7 +18,7 @@ func serveListing(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Us
// Loads the content of the directory
listing, err := fm.GetListing(u, i.VirtualPath, c.PrefixURL+r.URL.Path)
if err != nil {
return utils.ErrorToHTTPCode(err, true), err
return errorToHTTPCode(err, true), err
}
listing.Context = httpserver.Context{

View File

@ -1,4 +1,4 @@
package wrapper
package http
import "net/http"

View File

@ -6,7 +6,6 @@ import (
fm "github.com/hacdias/filemanager"
"github.com/hacdias/filemanager/page"
"github.com/hacdias/filemanager/utils"
)
// serveSingle serves a single file in an editor (if it is editable), shows the
@ -15,7 +14,7 @@ func serveSingle(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Use
var err error
if err = i.RetrieveFileType(); err != nil {
return utils.ErrorToHTTPCode(err, true), err
return errorToHTTPCode(err, true), err
}
p := &page.Page{
@ -36,7 +35,7 @@ func serveSingle(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Use
if i.Type == "text" {
if err = i.Read(); err != nil {
return utils.ErrorToHTTPCode(err, true), err
return errorToHTTPCode(err, true), err
}
}

47
page/functions.go Normal file
View File

@ -0,0 +1,47 @@
package page
import (
"encoding/base64"
"encoding/json"
"html/template"
"log"
"reflect"
)
// Create the functions map, then the template, check for erros and
// execute the template if there aren't errors
var functionMap = template.FuncMap{
"Defined": defined,
"CSS": css,
"Marshal": marshal,
"EncodeBase64": encodeBase64,
}
// defined checks if variable is defined in a struct
func defined(data interface{}, field string) bool {
t := reflect.Indirect(reflect.ValueOf(data)).Type()
if t.Kind() != reflect.Struct {
log.Print("Non-struct type not allowed.")
return false
}
_, b := t.FieldByName(field)
return b
}
// css returns the sanitized and safe css
func css(s string) template.CSS {
return template.CSS(s)
}
// marshal converts an interface to json and sanitizes it
func marshal(v interface{}) template.JS {
a, _ := json.Marshal(v)
return template.JS(a)
}
// encodeBase64 encodes a string in base 64
func encodeBase64(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
}

View File

@ -1,4 +1,4 @@
package utils
package page
import "testing"
@ -29,7 +29,7 @@ var testDefinedCases = []testDefined{
func TestDefined(t *testing.T) {
for _, pair := range testDefinedCases {
v := Defined(pair.data, pair.field)
v := defined(pair.data, pair.field)
if v != pair.result {
t.Error(
"For", pair.data,

View File

@ -3,7 +3,6 @@ package page
import (
"bytes"
"encoding/base64"
"encoding/json"
"html/template"
"log"
@ -12,7 +11,6 @@ import (
"github.com/hacdias/filemanager"
"github.com/hacdias/filemanager/assets"
"github.com/hacdias/filemanager/utils"
)
// Page contains the informations and functions needed to show the Page
@ -31,7 +29,6 @@ type Info struct {
Data interface{}
Editor bool
Display string
Token string
}
// BreadcrumbMapItem ...
@ -94,21 +91,6 @@ func (i Info) PreviousLink() string {
// PrintAsHTML formats the page in HTML and executes the template
func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) {
// Create the functions map, then the template, check for erros and
// execute the template if there aren't errors
functions := template.FuncMap{
"Defined": utils.Defined,
"CSS": func(s string) template.CSS {
return template.CSS(s)
},
"Marshal": func(v interface{}) template.JS {
a, _ := json.Marshal(v)
return template.JS(a)
},
"EncodeBase64": func(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
},
}
if p.Minimal {
templates = append(templates, "minimal")
@ -132,7 +114,7 @@ func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, erro
// If it's the first iteration, creates a new template and add the
// functions map
if i == 0 {
tpl, err = template.New(t).Funcs(functions).Parse(string(Page))
tpl, err = template.New(t).Funcs(functionMap).Parse(string(Page))
} else {
tpl, err = tpl.Parse(string(Page))
}

7
pre-build.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
go get github.com/jteeuwen/go-bindata/go-bindata
go-bindata -debug -pkg assets -prefix "_embed" \
-o assets/binary.go -ignore "^.*theme-([^g]|g[^i]|gi[^t]|git[^h]|gith[^u]|githu[^b]).*\.js$" \
_embed/templates/... _embed/public/js/... _embed/public/css/... _embed/public/ace/src-min/... \

View File

@ -1,24 +0,0 @@
package utils
import (
"net/http"
"os"
)
// ErrorToHTTPCode converts errors to HTTP Status Code.
func ErrorToHTTPCode(err error, gone bool) int {
switch {
case os.IsPermission(err):
return http.StatusForbidden
case os.IsNotExist(err):
if !gone {
return http.StatusNotFound
}
return http.StatusGone
case os.IsExist(err):
return http.StatusGone
default:
return http.StatusInternalServerError
}
}

View File

@ -1,13 +0,0 @@
package utils
import "reflect"
// IsMap checks if some variable is a map
func IsMap(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Map
}
// IsSlice checks if some variable is a slice
func IsSlice(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Slice
}

View File

@ -1,47 +0,0 @@
package utils
import (
"errors"
"log"
"reflect"
)
// Defined checks if variable is defined in a struct
func Defined(data interface{}, field string) bool {
t := reflect.Indirect(reflect.ValueOf(data)).Type()
if t.Kind() != reflect.Struct {
log.Print("Non-struct type not allowed.")
return false
}
_, b := t.FieldByName(field)
return b
}
// Dict allows to send more than one variable into a template
func Dict(values ...interface{}) (map[string]interface{}, error) {
if len(values)%2 != 0 {
return nil, errors.New("invalid dict call")
}
dict := make(map[string]interface{}, len(values)/2)
for i := 0; i < len(values); i += 2 {
key, ok := values[i].(string)
if !ok {
return nil, errors.New("dict keys must be strings")
}
dict[key] = values[i+1]
}
return dict, nil
}
// StringInSlice checks if a slice contains a string
func StringInSlice(a string, list []string) (bool, int) {
for i, b := range list {
if b == a {
return true, i
}
}
return false, 0
}