more updates and renaming
parent
1c703e0364
commit
8c19482190
|
@ -0,0 +1 @@
|
|||
Subproject commit 784ffa862c5351e0d300370f61471b1eb95ebcf1
|
File diff suppressed because it is too large
Load Diff
|
@ -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});
|
|
@ -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;
|
||||
|
||||
}));
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
8317
assets/binary.go
8317
assets/binary.go
File diff suppressed because one or more lines are too long
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
24
http/http.go
24
http/http.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package wrapper
|
||||
package http
|
||||
|
||||
import "net/http"
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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,
|
20
page/page.go
20
page/page.go
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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/... \
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue