Compare commits

..

12 Commits

Author SHA1 Message Date
Henrique Dias
40fe081962 fix error
Former-commit-id: 6f9d7c0b4ee819f737395ec1b721a88be1e6c5e6 [formerly 0489f9f724d1165bf63c45df84398f9631ed9833] [formerly 5a3ffb16e3a801dd811b3d05ce853413d1da2ae6 [formerly 5c2166bc15]]
Former-commit-id: 4672f5e091c847482af0ae4741bcb809134a1559 [formerly 96ecde75a3d61b9cc1e77eae6254166ec53b902c]
Former-commit-id: 683218b398c699cd0f6ce15bebb6385d20eee390
2017-09-01 13:26:04 +01:00
Henrique Dias
ebf1325126 build assets
Former-commit-id: 5fd962f1a14d288995f97fdfd22480c0af54cf80 [formerly 1642bbafb9bdb91a58a5fb1532b1168894b5574f] [formerly ad423b6250b27f4eed0d9cbdbe3f09155e343ef0 [formerly 0ffb1fbe39]]
Former-commit-id: c3c687df2340c41f44356cd844a0b5ca0d779f71 [formerly 7238fdf7c28d58a734d4d6d8d71bd47456064dbc]
Former-commit-id: 5be42b6cb65f638508133c56fc6775d71e1dacca
2017-09-01 13:22:35 +01:00
Henrique Dias
ea1b9febb7 Merge branch 'master' of https://github.com/hacdias/filemanager
Former-commit-id: 79aec32eb9ac9b7e75ad00689990a865d5fed5c7 [formerly 96b947bf637adadc04409075b715ee85e4c7376b] [formerly 384abd27c0d7d22e67fd70b57c764b2dcbe69611 [formerly 349b3d02e6]]
Former-commit-id: b58b2e6bc0a7e90a3a77054c686c98f2cfb96978 [formerly 8a6ddadf8af01a29380dad9fa6ea4ca035aa8043]
Former-commit-id: 31856ee921c1fb7d77ba7704ea0af5e2d58ff952
2017-09-01 13:20:11 +01:00
Henrique Dias
f3edf63fb2 fix #226
Former-commit-id: 7a2d0e67b2320af58b04da5e5834c0ed6a0ec137 [formerly 468e34a7bbadffdfc268c072b4460c99beb066d9] [formerly 7a962e989a213218868b02cfdd7c289ca0b6831e [formerly 2a5aa21b8d]]
Former-commit-id: 2f443bbdf9518e66136ab97cf8c2f7e434a65f7b [formerly abcf5d2dc1aaa8b034bbe7b958cdec14feaef1b4]
Former-commit-id: 22cf31bc4973b51fbf9849f537e0191c65bf327a
2017-09-01 13:17:10 +01:00
Equim
f13e7ba940 Update locale (#219)
* i18n: update locale

* i18n: update locale


Former-commit-id: f2e21d0882f071f86bc3c225067313e54bcab1f4 [formerly 8cf56e48e3c40cb7bc1f9df58daa14ebc1d4d459] [formerly 48628588159737a35937da4511c1680600c97f51 [formerly e910f8580a]]
Former-commit-id: 2b0669041ae6f9c48d8163f3e20f69636a9ff291 [formerly 0a23672f8298176a8a03564a20d978225ef1bb6b]
Former-commit-id: 6d9a6057a6c67d645edf3fd617663afa1f42f977
2017-08-28 19:12:35 +01:00
Henrique Dias
3ebe219e96 Option to lock user's password #215
Former-commit-id: e4f0afef51c437bd55c58f12f2f45ce8e8c84bb0 [formerly 549235edf9d6c43d1454a8a00d7b6f832bb8a3ca] [formerly 12d099aa44bff7d995b05680e405d8040f1e1850 [formerly fc9ca4f6a4]]
Former-commit-id: e9666db20e2b473095f21c03d59f2a8fbf07929e [formerly 87ee9eb83daed5180c6a3714c0ddc861668d747b]
Former-commit-id: 5a15b05320c1eb28324e50cad7ca980d3eebcb02
2017-08-24 14:44:53 +01:00
Henrique Dias
d838856711 Popup on copy link.
Former-commit-id: e483df4402733b102d11b10436ff74aad11dfa7c [formerly 6d761c2ee838a9766f755b6c54cdc2ca388b5934] [formerly 1365e9e067af021ad0c680bae3af963dc4a90b28 [formerly 889871ec0a]]
Former-commit-id: ba443a90fded4501c0a6872eb293c14b2923c627 [formerly d21c6b9ab41869d2b10aa99853bc5b6931b63d96]
Former-commit-id: 7c19b231861797c62dc35c1e8a28f4ceeb8761c7
2017-08-24 14:13:29 +01:00
Henrique Dias
610d55c26f Fix override.
Former-commit-id: 515874171ed00bf70db40b2fc302d479d18fe93a [formerly ea9e47252daae3fb211150e2c3ab67ac97067a32] [formerly 2b0378600c957da5e2610f3cf8e82dd079d11f65 [formerly e285195fe1]]
Former-commit-id: 05d03bb250d4bc6bc4b3dbaf388f79a682f09841 [formerly 4ef3ceb2dd3862011a09ae39fbf4a78b889100e4]
Former-commit-id: 448224f282d6b14678f8d5d6629cbbe44ed19644
2017-08-24 12:33:54 +01:00
Henrique Dias
ac044016ee Implement SHIFT+Click to select range of files. #206
Former-commit-id: 15da15e0588d5977b42f71b8c96980248db73889 [formerly 95e464410bb619f4ff70b21d846e937bf9355e8a] [formerly cae63a6f6de24229ff04392fe16260c0d9045e8c [formerly 297a52e606]]
Former-commit-id: de9d291dba6c2e2d750c519f1fcbd59c34538ab1 [formerly 4fb3fcf8ba5726f6dde56fcbd8c1457799539a8e]
Former-commit-id: ad1568f33fa927dc750515856950b95b588d9f68
2017-08-24 09:14:26 +01:00
Henrique Dias
58af3461a8 Fix #216
Former-commit-id: 6d4b0674327746ed46fa4397f2d3ab01b8579bc6 [formerly eafa0d3effab4921e1a57de23f5a189bb75dced9] [formerly db40d686eabe4b78d9a5e8a6b1c20b623880d986 [formerly 9301966eb7]]
Former-commit-id: a148dc60ea64d054567f6d9b5d0c383893851da0 [formerly 02af0aeb73dd88cc157e5e21a9e9dd3658316d2a]
Former-commit-id: 1d4e9c50e20d3689f39224308bfadbe6097f5664
2017-08-23 11:05:54 +01:00
Henrique Dias
4191a6f9e0 Fix login issues regarding basicauth+noauth #214 #204
Former-commit-id: 5c150c622724e7d2c9a9630f60325a5a246b7526 [formerly 6ff5856dc0ab40dbb9a9759c0c23de74a23d2531] [formerly b41c082f0631a33648bc50e81f38d7dbba612d8c [formerly c95b1aaace]]
Former-commit-id: ead207a0e5405ccc0641b3705875b8846846060d [formerly 7c1221485698d524ef679d09166cff7549f7b00a]
Former-commit-id: b01bdc787983c2c48394ef9e3eabed9137c1f31c
2017-08-22 15:25:54 +01:00
Henrique Dias
c18ca4702d Fix #213
Former-commit-id: 57732db98608e0b8fbe0e18f6d77d69b7cdc8807 [formerly 647558cec7e30388a3dc80c002bf2787f27edbf7] [formerly 03295cda86a4ce43a734c4617d7d4429ffeb7570 [formerly 1aa68b0dd0]]
Former-commit-id: 52bc1fdecc89456f7dc32f5ce64a40727070d06c [formerly 8a1a04c67bba6988b448f9a4c612ba62aeaf7e62]
Former-commit-id: 9b4565efca09847c31f6f856eae1aa488461046d
2017-08-21 07:33:16 +01:00
47 changed files with 217 additions and 165 deletions

View File

@@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="base" content="{{ .BaseURL }}">
<meta name="staticgen" content="{{ .StaticGen }}">
<meta name="noauth" content="{{ .NoAuth }}">
<title>File Manager</title>
<link rel="icon" type="image/png" sizes="32x32" href="{{ .BaseURL }}/static/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ .BaseURL }}/static/img/icons/favicon-16x16.png">

View File

@@ -46,7 +46,7 @@
</button>
</div>
<div v-if="!$store.state.user.noAuth">
<div v-if="!$store.state.noAuth">
<router-link class="action" to="/settings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
<i class="material-icons">settings_applications</i>
<span>{{ $t('sidebar.settings') }}</span>

View File

@@ -134,7 +134,7 @@ export default {
})
.catch(error => {
buttons.done(button)
this.$store.commit('showError', error)
this.$showError(error)
this.$store.commit('setSchedule', '')
})
}

View File

@@ -210,17 +210,13 @@ export default {
if (this.$store.state.clipboard.key === 'x') {
api.move(items).then(() => {
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
}).catch(this.$showError)
return
}
api.copy(items).then(() => {
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
}).catch(this.$showError)
},
resizeEvent () {
// Update the columns size based on the window width.
@@ -267,7 +263,7 @@ export default {
.then(req => {
this.checkConflict(files, req.items, base)
})
.catch(error => { console.log(error) })
.catch(this.$showError)
return
}
@@ -348,7 +344,7 @@ export default {
})
.catch(error => {
finish()
this.$store.commit('showError', error)
this.$showError(error)
})
return false

View File

@@ -109,21 +109,24 @@ export default {
.then(() => {
this.$store.commit('setReload', true)
})
.catch(error => {
this.$store.commit('showError', error)
})
.catch(this.$showError)
},
click: function (event) {
if (this.selectedCount !== 0) event.preventDefault()
if (this.$store.state.selected.indexOf(this.index) === -1) {
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
this.addSelected(this.index)
} else {
if (this.$store.state.selected.indexOf(this.index) !== -1) {
this.removeSelected(this.index)
return
}
return false
if (event.shiftKey && this.selected.length === 1) {
let fi = (this.index > this.selected[0]) ? this.selected[0] : this.index
let la = (this.index > this.selected[0]) ? this.index : this.selected[0]
for (; fi <= la; fi++) this.addSelected(fi)
return
}
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
this.addSelected(this.index)
},
touchstart (event) {
setTimeout(() => {

View File

@@ -75,7 +75,7 @@ export default {
this.listing = req
this.updateLinks()
})
.catch(error => { console.log(error) })
.catch(this.$showError)
},
beforeDestroy () {
window.removeEventListener('keyup', this.key)

View File

@@ -56,7 +56,7 @@ export default {
})
.catch(error => {
buttons.done('copy')
this.$store.commit('showError', error)
this.$showError(error)
})
}
}

View File

@@ -43,7 +43,7 @@ export default {
})
.catch(error => {
buttons.done('delete')
this.$store.commit('showError', error)
this.$showError(error)
})
return
@@ -70,7 +70,7 @@ export default {
.catch(error => {
buttons.done('delete')
this.$store.commit('setReload', true)
this.$store.commit('showError', error)
this.$showError(error)
})
}
}

View File

@@ -1,31 +0,0 @@
<template>
<div class="prompt error">
<i class="material-icons">error_outline</i>
<h3>{{ $t('prompts.error') }}</h3>
<pre>{{ $store.state.showMessage }}</pre>
<div>
<button @click="close"
autofocus
:aria-label="$t('buttons.close')"
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
<button @click="reportIssue"
class="cancel"
:aria-label="$t('buttons.reportIssue')"
:title="$t('buttons.reportIssue')">{{ $t('buttons.reportIssue') }}</button>
</div>
</div>
</template>
<script>
export default {
name: 'error',
methods: {
reportIssue () {
window.open('https://github.com/hacdias/filemanager/issues/new')
},
close () {
this.$store.commit('closeHovers')
}
}
}
</script>

View File

@@ -53,7 +53,7 @@ export default {
// so we fetch the data from the previous directory.
api.fetch(url.removeLastDir(this.$route.path))
.then(this.fillOptions)
.catch(this.showError)
.catch(this.$showError)
},
methods: {
fillOptions (req) {
@@ -96,7 +96,7 @@ export default {
api.fetch(uri)
.then(this.fillOptions)
.catch(this.showError)
.catch(this.$showError)
},
touchstart (event) {
let url = event.currentTarget.dataset.url

View File

@@ -111,7 +111,7 @@ export default {
api.checksum(link, hash)
.then((hash) => { event.target.innerHTML = hash })
.catch(error => { this.$store.commit('showError', error) })
.catch(this.$showError)
}
}
}

View File

@@ -56,7 +56,7 @@ export default {
})
.catch(error => {
buttons.done('move')
this.$store.commit('showError', error)
this.$showError(error)
})
event.preventDefault()

View File

@@ -37,9 +37,7 @@ export default {
.then((url) => {
this.$router.push({ path: url })
})
.catch(error => {
this.$store.commit('showError', error)
})
.catch(this.$showError)
},
new (url, type) {
url = removePrefix(url)
@@ -47,7 +45,7 @@ export default {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', `${this.$store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${this.$store.state.jwt}`)
if (!this.$store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${this.$store.state.jwt}`)
request.setRequestHeader('Archetype', encodeURIComponent(type))
request.onload = () => {

View File

@@ -43,7 +43,7 @@ export default {
api.post(uri)
.then(() => { this.$router.push({ path: uri }) })
.catch(error => { this.$store.commit('showError', error) })
.catch(this.$showError)
// Close the prompt
this.$store.commit('closeHovers')

View File

@@ -44,7 +44,7 @@ export default {
// Create the new file.
api.post(uri)
.then(() => { this.$router.push({ path: uri }) })
.catch(error => { this.$store.commit('showError', error) })
.catch(this.$showError)
// Close the prompt.
this.$store.commit('closeHovers')

View File

@@ -9,8 +9,6 @@
<info v-else-if="showInfo"></info>
<move v-else-if="showMove"></move>
<copy v-else-if="showCopy"></copy>
<error v-else-if="showError"></error>
<success v-else-if="showSuccess"></success>
<replace v-else-if="showReplace"></replace>
<schedule v-else-if="show === 'schedule'"></schedule>
<new-archetype v-else-if="show === 'new-archetype'"></new-archetype>
@@ -27,8 +25,6 @@ import Rename from './Rename'
import Download from './Download'
import Move from './Move'
import Copy from './Copy'
import Error from './Error'
import Success from './Success'
import NewFile from './NewFile'
import NewDir from './NewDir'
import NewArchetype from './NewArchetype'
@@ -47,9 +43,7 @@ export default {
NewArchetype,
Schedule,
Rename,
Error,
Download,
Success,
Move,
Copy,
Share,
@@ -70,8 +64,6 @@ export default {
},
computed: {
...mapState(['show', 'plugins']),
showError: function () { return this.show === 'error' },
showSuccess: function () { return this.show === 'success' },
showInfo: function () { return this.show === 'info' },
showHelp: function () { return this.show === 'help' },
showDelete: function () { return this.show === 'delete' },

View File

@@ -68,7 +68,7 @@ export default {
}
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
this.$showError(error)
})
this.$store.commit('closeHovers')

View File

@@ -18,7 +18,7 @@
:aria-label="$t('buttons.delete')"
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
<button class="action copy"
<button class="action copy-clipboard"
:data-clipboard-text="buildLink(link.hash)"
:aria-label="$t('buttons.copyToClipboard')"
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
@@ -54,7 +54,7 @@
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { mapState } from 'vuex'
import { getShare, deleteShare, share } from '@/utils/api'
import moment from 'moment'
import Clipboard from 'clipboard'
@@ -101,20 +101,25 @@ export default {
})
.catch(error => {
if (error === 404) return
this.showError(error)
this.$showError(error)
})
},
mounted () {
this.clip = new Clipboard('.copy')
this.clip = new Clipboard('.copy-clipboard')
this.clip.on('success', (e) => {
this.$showSuccess(this.$t('success.linkCopied'))
})
},
beforeDestroy () {
this.clip.destroy()
},
methods: {
...mapMutations([ 'showError' ]),
submit: function (event) {
if (!this.time) return
share(this.url, this.time, this.unit)
.then(result => { this.links.push(result); this.sort() })
.catch(error => { this.showError(error) })
.catch(this.$showError)
},
getPermalink (event) {
share(this.url)
@@ -123,7 +128,7 @@ export default {
this.sort()
this.hasPermanent = true
})
.catch(error => { this.showError(error) })
.catch(this.$showError)
},
deleteLink (event, link) {
event.preventDefault()
@@ -132,7 +137,7 @@ export default {
if (!link.expires) this.hasPermanent = false
this.links = this.links.filter(item => item.hash !== link.hash)
})
.catch(error => { this.showError(error) })
.catch(this.$showError)
},
humanTime (time) {
return moment(time).fromNow()

View File

@@ -1,23 +0,0 @@
<template>
<div class="prompt success">
<i class="material-icons">done</i>
<h3>{{ $store.state.showMessage }}</h3>
<div>
<button @click="close"
:aria-label="$t('buttons.ok')"
:title="$t('buttons.ok')"
autofocus>{{ $t('buttons.ok') }}</button>
</div>
</div>
</template>
<script>
export default {
name: 'success',
methods: {
close () {
this.$store.commit('closeHovers')
}
}
}
</script>

View File

@@ -2,7 +2,6 @@ body {
font-family: 'Roboto', sans-serif;
padding-top: 4em;
background-color: #f8f8f8;
user-select: none;
color: #212121;
}

View File

@@ -206,3 +206,24 @@
margin-right: .5em;
border: 1px solid #dadada;
}
.prompt#share .action.copy-clipboard::after {
content: 'Copied!';
position: absolute;
left: -25%;
width: 150%;
font-size: .6em;
text-align: center;
background: #44a6f5;
color: #fff;
padding: .5em .2em;
border-radius: .4em;
top: -2em;
transition: .1s ease opacity;
opacity: 0;
}
.prompt#share .action.copy-clipboard.active::after {
opacity: 1;
}

View File

@@ -1,4 +1,5 @@
@import "~normalize.css/normalize.css";
@import "~noty/lib/noty.css";
@import "./fonts.css";
@import "./base.css";
@import "./header.css";
@@ -180,6 +181,17 @@
* PROMPT *
* * * * * * * * * * * * * * * */
.noty_buttons {
text-align: right;
padding: 0 10px 10px !important;
}
.noty_buttons button {
background: rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0,0,0,0.1);
box-shadow: 0 0 0 0;
font-size: 14px;
}
/* * * * * * * * * * * * * * * *
* FOOTER *

View File

@@ -26,11 +26,13 @@ buttons:
publish: Publish
selectMultiple: Select multiple
schedule: Schedule
switchView: Swicth view
switchView: Switch view
toggleSidebar: Toggle sidebar
update: Update
upload: Upload
permalink: Get Permanent Link
success:
linkCopied: Link copied!
errors:
forbidden: You're not welcome here.
internal: Something really went wrong.
@@ -122,6 +124,7 @@ settings:
examples: Examples
globalSettings: Global Settings
language: Language
lockPassword: Prevent the user from changing the password
newPassword: Your new password
newPasswordConfirm: Confirm your new password
newUser: New User

View File

@@ -31,6 +31,8 @@ buttons:
update: 更新
upload: アップロード
permalink: 固定リンク
success:
linkCopied: リンクがコピーされました!
errors:
forbidden: アクセスが拒否されました。
internal: 内部エラーが発生しました。
@@ -122,6 +124,7 @@ settings:
examples:
globalSettings: グローバル設定
language: 言語
lockPassowrd: 新しいパスワードを変更に禁止
newPassword: 新しいパスワード
newPasswordConfirm: 新しいパスワードを確認します
newUser: 新しいユーザー

View File

@@ -31,6 +31,8 @@ buttons:
update: Atualizar
upload: Enviar
permalink: Obter link permanente
success:
linkCopied: Link copiado!
errors:
forbidden: Tu não és bem-vindo aqui.
internal: Algo correu bastante mal.
@@ -143,6 +145,7 @@ settings:
examples: Exemplos
globalSettings: Configurações Globais
language: Linguagem
lockPassword: Não permitir que o utilizador altere a palavra-passe
newPassword: Nova palavra-passe
newPasswordConfirm: Confirme a nova palavra-passe
newUser: Novo Utilizador

View File

@@ -31,6 +31,8 @@ buttons:
update: 更新
upload: 上传
permalink: 获取永久链接
success:
linkCopied: 链接已复制!
errors:
forbidden: 你被禁止访问。
internal: 内部出现麻烦了。
@@ -121,6 +123,7 @@ settings:
examples: 例子
globalSettings: 全局设置
language: 语言
lockPassowrd: 禁止用户修改密码
newPassword: 您的新密码
newPasswordConfirm: 重输一遍新密码
newUser: 新建用户

View File

@@ -31,6 +31,8 @@ buttons:
update: 更新
upload: 上傳
permalink: 獲取永久連結
success:
linkCopied: 連結已複製!
errors:
forbidden: 你被禁止訪問。
internal: 內部出現麻煩了。
@@ -121,6 +123,7 @@ settings:
examples: 例子
globalSettings: 全域設定
language: 語言
lockPassword: 禁止用戶修改密碼
newPassword: 您的新密碼
newPasswordConfirm: 重輸一遍新密碼
newUser: 建立用戶

View File

@@ -3,9 +3,47 @@ import App from './App'
import store from './store'
import router from './router'
import i18n from './i18n'
import Noty from 'noty'
Vue.config.productionTip = true
const notyDefault = {
type: 'info',
layout: 'bottomRight',
timeout: 1000,
progressBar: true
}
Vue.prototype.$noty = function (opts) {
new Noty(Object.assign({}, notyDefault, opts)).show()
}
Vue.prototype.$showSuccess = function (message) {
new Noty(Object.assign({}, notyDefault, {
text: message,
type: 'success'
})).show()
}
Vue.prototype.$showError = function (error) {
// TODO: add btns: close and report issue
let n = new Noty(Object.assign({}, notyDefault, {
text: error,
type: 'error',
timeout: null,
buttons: [
Noty.button(i18n.t('buttons.reportIssue'), 'cancel', function () {
window.open('https://github.com/hacdias/filemanager/issues/new')
}),
Noty.button(i18n.t('buttons.close'), '', function () {
n.close()
})
]
}))
n.show()
}
/* eslint-disable no-new */
new Vue({
el: '#app',

View File

@@ -14,6 +14,7 @@ const state = {
},
staticGen: document.querySelector('meta[name="staticgen"]').getAttribute('content'),
baseURL: document.querySelector('meta[name="base"]').getAttribute('content'),
noAuth: (document.querySelector('meta[name="noauth"]').getAttribute('content') === 'true'),
jwt: '',
progress: 0,
schedule: '',

View File

@@ -18,7 +18,7 @@ export function fetch (url) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -41,7 +41,7 @@ export function remove (url) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
@@ -62,7 +62,7 @@ export function post (url, content = '', overwrite = false, onupload) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (typeof onupload === 'function') {
request.upload.onprogress = onupload
@@ -95,7 +95,7 @@ export function put (url, content = '', publish = false, date = '') {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.setRequestHeader('Publish', publish)
if (date !== '') {
@@ -125,7 +125,7 @@ function moveCopy (items, copy = false) {
promises.push(new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('PATCH', `${store.state.baseURL}/api/resource${from}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.setRequestHeader('Destination', to)
if (copy) {
@@ -162,7 +162,7 @@ export function checksum (url, algo) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/checksum${url}?algo=${algo}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
@@ -226,7 +226,7 @@ export function getSettings () {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/settings/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -255,7 +255,7 @@ export function updateSettings (param, which) {
let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/settings/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -278,7 +278,7 @@ export function getUsers () {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/users/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -299,7 +299,7 @@ export function getUser (id) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/users/${id}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -320,7 +320,7 @@ export function newUser (user) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/users/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -345,7 +345,7 @@ export function updateUser (user, which) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/users/${user.ID}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -370,7 +370,7 @@ export function deleteUser (id) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/users/${id}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
switch (request.status) {
@@ -395,7 +395,7 @@ export function getShare (url) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/share${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
@@ -414,7 +414,7 @@ export function deleteShare (hash) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/share/${hash}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
@@ -439,7 +439,7 @@ export function share (url, expires = '', unit = 'hours') {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', url, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {

View File

@@ -16,7 +16,7 @@ function loggedIn () {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/auth/renew`, true)
request.setRequestHeader('Authorization', `Bearer ${cookie('auth')}`)
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${cookie('auth')}`)
request.onload = () => {
if (request.status === 200) {

View File

@@ -45,7 +45,7 @@
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { mapState } from 'vuex'
import { getSettings, updateSettings } from '@/utils/api'
export default {
@@ -73,10 +73,9 @@ export default {
})
}
})
.catch(error => { this.showError(error) })
.catch(error => { this.$showError(error) })
},
methods: {
...mapMutations([ 'showSuccess', 'showError' ]),
capitalize (name, where = '_') {
if (where === 'caps') where = /(?=[A-Z])/
let splitted = name.split(where)
@@ -103,8 +102,8 @@ export default {
}
updateSettings(commands, 'commands')
.then(() => { this.showSuccess(this.$t('settings.commandsUpdated')) })
.catch(error => { this.showError(error) })
.then(() => { this.$showSuccess(this.$t('settings.commandsUpdated')) })
.catch(error => { this.$showError(error) })
},
saveStaticGen (event) {
event.preventDefault()
@@ -124,8 +123,8 @@ export default {
}
updateSettings(staticGen, 'staticGen')
.then(() => { this.showSuccess(this.$t('settings.settingsUpdated')) })
.catch(error => { this.showError(error) })
.then(() => { this.$showSuccess(this.$t('settings.settingsUpdated')) })
.catch(error => { this.$showError(error) })
},
parseStaticGen (staticgen) {
for (let option of staticgen) {

View File

@@ -18,7 +18,7 @@
<p><input type="submit" :value="$t('buttons.update')"></p>
</form>
<form @submit="updatePassword">
<form v-if="!user.lockPassword" @submit="updatePassword">
<h3>{{ $t('settings.changePassword') }}</h3>
<p><input :class="passwordClass" type="password" :placeholder="$t('settings.newPassword')" v-model="password" name="password"></p>
<p><input :class="passwordClass" type="password" :placeholder="$t('settings.newPasswordConfirm')" v-model="passwordConf" name="password"></p>
@@ -28,7 +28,7 @@
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { mapState } from 'vuex'
import { updateUser } from '@/utils/api'
import Languages from '@/components/Languages'
@@ -64,7 +64,6 @@ export default {
this.locale = this.user.locale
},
methods: {
...mapMutations([ 'showSuccess' ]),
updatePassword (event) {
event.preventDefault()
@@ -78,9 +77,9 @@ export default {
}
updateUser(user, 'password').then(location => {
this.showSuccess(this.$t('settings.passwordUpdated'))
this.$showSuccess(this.$t('settings.passwordUpdated'))
}).catch(e => {
this.$store.commit('showError', e)
this.$showError(e)
})
},
updateSettings (event) {
@@ -93,9 +92,9 @@ export default {
updateUser(user, 'partial').then(location => {
this.$store.commit('setUser', user)
this.$emit('css-updated')
this.showSuccess(this.$t('settings.settingsUpdated'))
this.$showSuccess(this.$t('settings.settingsUpdated'))
}).catch(e => {
this.$store.commit('showError', e)
this.$showError(e)
})
}
}

View File

@@ -21,6 +21,8 @@
<languages id="locale" :selected.sync="locale"></languages>
</p>
<p><input type="checkbox" :disabled="admin" v-model="lockPassword"> {{ $t('settings.lockPassword') }}</p>
<h2>{{ $t('settings.permissions') }}</h2>
<p class="small">{{ $t('settings.permissionsHelp') }}</p>
@@ -93,6 +95,7 @@ export default {
allowEdit: false,
allowCommands: false,
allowPublish: false,
lockPassword: false,
permissions: {},
password: '',
username: '',
@@ -120,6 +123,7 @@ export default {
this.allowEdit = true
this.allowNew = true
this.allowPublish = true
this.lockPassword = false
for (let key in this.permissions) {
this.permissions[key] = true
}
@@ -141,6 +145,7 @@ export default {
this.allowNew = user.allowNew
this.allowEdit = user.allowEdit
this.allowPublish = user.allowPublish
this.lockPassword = user.lockPassword
this.filesystem = user.filesystem
this.username = user.username
this.commands = user.commands.join(' ')
@@ -187,6 +192,7 @@ export default {
this.allowPublish = false
this.permissins = {}
this.allowCommands = false
this.lockPassword = false
this.password = ''
this.username = ''
this.filesystem = ''
@@ -203,9 +209,9 @@ export default {
deleteUser(this.id).then(location => {
this.$router.push({ path: '/users' })
this.$store.commit('showSuccess', this.$t('settings.userDeleted'))
this.$showSuccess(this.$t('settings.userDeleted'))
}).catch(e => {
this.$store.commit('showError', e)
this.$showError(e)
})
},
save (event) {
@@ -215,9 +221,9 @@ export default {
if (this.$route.path === '/users/new') {
newUser(user).then(location => {
this.$router.push({ path: location })
this.$store.commit('showSuccess', this.$t('settings.userCreated'))
this.$showSuccess(this.$t('settings.userCreated'))
}).catch(e => {
this.$store.commit('showError', e)
this.$showError(e)
})
return
@@ -228,9 +234,9 @@ export default {
this.$store.commit('setUser', user)
}
this.$store.commit('showSuccess', this.$t('settings.userUpdated'))
this.$showSuccess(this.$t('settings.userUpdated'))
}).catch(e => {
this.$store.commit('showError', e)
this.$showError(e)
})
},
parseForm () {
@@ -238,6 +244,7 @@ export default {
ID: this.id,
username: this.username,
password: this.password,
lockPassword: this.lockPassword,
filesystem: this.filesystem,
admin: this.admin,
allowCommands: this.allowCommands,

View File

@@ -44,7 +44,7 @@ export default {
api.getUsers().then(users => {
this.users = users
}).catch(error => {
this.$store.commit('showError', error)
this.$showError(error)
})
}
}

View File

@@ -13,24 +13,24 @@ type ShareStore struct {
// Get gets a Share Link from an hash.
func (s ShareStore) Get(hash string) (*fm.ShareLink, error) {
var v *fm.ShareLink
var v fm.ShareLink
err := s.DB.One("Hash", hash, &v)
if err == storm.ErrNotFound {
return v, fm.ErrNotExist
return nil, fm.ErrNotExist
}
return v, err
return &v, err
}
// GetPermanent gets the permanent link from a path.
func (s ShareStore) GetPermanent(path string) (*fm.ShareLink, error) {
var v *fm.ShareLink
var v fm.ShareLink
err := s.DB.Select(q.Eq("Path", path), q.Eq("Expires", false)).First(&v)
if err == storm.ErrNotFound {
return v, fm.ErrNotExist
return nil, fm.ErrNotExist
}
return v, err
return &v, err
}
// GetByPath gets all the links for a specific path.
@@ -48,6 +48,10 @@ func (s ShareStore) GetByPath(hash string) ([]*fm.ShareLink, error) {
func (s ShareStore) Gets() ([]*fm.ShareLink, error) {
var v []*fm.ShareLink
err := s.DB.All(&v)
if err == storm.ErrNotFound {
return v, fm.ErrNotExist
}
return v, err
}

View File

@@ -17,7 +17,6 @@ import (
"github.com/hacdias/fileutils"
"github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
"github.com/spf13/viper"
)
var databases = map[string]*storm.DB{}
@@ -205,7 +204,7 @@ func Parse(c *caddy.Controller, plugin string) ([]*filemanager.FileManager, erro
}
m := &filemanager.FileManager{
NoAuth: viper.GetBool("NoAuth"),
NoAuth: noAuth,
BaseURL: "",
PrefixURL: "",
DefaultUser: u,

View File

@@ -32,6 +32,7 @@ var (
logfile string
staticg string
locale string
baseurl string
port int
noAuth bool
allowCommands bool
@@ -49,6 +50,7 @@ func init() {
flag.StringVarP(&database, "database", "d", "./filemanager.db", "Database file")
flag.StringVarP(&logfile, "log", "l", "stdout", "Errors logger; can use 'stdout', 'stderr' or file")
flag.StringVarP(&scope, "scope", "s", ".", "Default scope option for new users")
flag.StringVarP(&baseurl, "baseurl", "b", "", "Base URL")
flag.StringVar(&commands, "commands", "git svn hg", "Default commands option for new users")
flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users")
flag.BoolVar(&allowEdit, "allow-edit", true, "Default allow edit option for new users")
@@ -74,6 +76,7 @@ func setupViper() {
viper.SetDefault("StaticGen", "")
viper.SetDefault("Locale", "en")
viper.SetDefault("NoAuth", false)
viper.SetDefault("BaseURL", "")
viper.BindPFlag("Port", flag.Lookup("port"))
viper.BindPFlag("Address", flag.Lookup("address"))
@@ -88,6 +91,7 @@ func setupViper() {
viper.BindPFlag("Locale", flag.Lookup("locale"))
viper.BindPFlag("StaticGen", flag.Lookup("staticgen"))
viper.BindPFlag("NoAuth", flag.Lookup("no-auth"))
viper.BindPFlag("BaseURL", flag.Lookup("baseurl"))
viper.SetConfigName("filemanager")
viper.AddConfigPath(".")
@@ -177,7 +181,7 @@ func handler() http.Handler {
fm := &filemanager.FileManager{
NoAuth: viper.GetBool("NoAuth"),
BaseURL: "",
BaseURL: viper.GetString("BaseURL"),
PrefixURL: "",
DefaultUser: &filemanager.User{
AllowCommands: viper.GetBool("AllowCommands"),

View File

@@ -286,6 +286,7 @@ var DefaultUser = User{
AllowEdit: true,
AllowNew: true,
AllowPublish: true,
LockPassword: false,
Commands: []string{},
Rules: []*Rule{},
CSS: "",
@@ -325,6 +326,9 @@ type User struct {
// Locale is the language of the user.
Locale string `json:"locale"`
// Prevents the user to change its password.
LockPassword bool `json:"lockPassword"`
// These indicate if the user can perform certain actions.
AllowNew bool `json:"allowNew"` // Create files and folders
AllowEdit bool `json:"allowEdit"` // Edit/rename files

View File

@@ -59,7 +59,6 @@ func renewAuthHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
// claims is the JWT claims.
type claims struct {
fm.User
NoAuth bool `json:"noAuth"`
jwt.StandardClaims
}
@@ -74,7 +73,6 @@ func printToken(c *fm.Context, w http.ResponseWriter) (int, error) {
// Builds the claims.
claims := claims{
u,
c.NoAuth,
jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
Issuer: "File Manager",

View File

@@ -9,7 +9,6 @@ import (
"strings"
"time"
"github.com/asdine/storm"
fm "github.com/hacdias/filemanager"
)
@@ -224,7 +223,10 @@ func renderFile(c *fm.Context, w http.ResponseWriter, file string, contentType s
tpl := template.Must(template.New("file").Parse(file))
w.Header().Set("Content-Type", contentType+"; charset=utf-8")
data := map[string]interface{}{"BaseURL": c.RootURL()}
data := map[string]interface{}{
"BaseURL": c.RootURL(),
"NoAuth": c.NoAuth,
}
if c.StaticGen != nil {
data["StaticGen"] = c.StaticGen.Name()
@@ -242,7 +244,7 @@ func renderFile(c *fm.Context, w http.ResponseWriter, file string, contentType s
// sharePage build the share page.
func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
s, err := c.Store.Share.Get(r.URL.Path)
if err == storm.ErrNotFound {
if err == fm.ErrNotExist {
return renderFile(
c, w,
c.Assets.MustString("static/share/404.html"),

View File

@@ -160,7 +160,7 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques
}
// Discard any invalid upload before returning to avoid connection
// reset fm.Error.
// reset error.
defer func() {
io.Copy(ioutil.Discard, r.Body)
}()
@@ -179,9 +179,9 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques
}
// If using POST method, we are trying to create a new file so it is not
// desirable to ovfm.Erride an already existent file. Thus, we check
// desirable to override an already existent file. Thus, we check
// if the file already exists. If so, we just return a 409 Conflict.
if r.Method == http.MethodPost && r.Header.Get("Action") != "ovfm.Erride" {
if r.Method == http.MethodPost && r.Header.Get("Action") != "override" {
if _, err := c.User.FileSystem.Stat(r.URL.Path); err == nil {
return http.StatusConflict, errors.New("There is already a file on that path")
}

View File

@@ -8,7 +8,6 @@ import (
"strings"
"time"
"github.com/asdine/storm"
fm "github.com/hacdias/filemanager"
)
@@ -30,7 +29,7 @@ func shareHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, e
func shareGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
path := filepath.Join(c.User.Scope, r.URL.Path)
s, err := c.Store.Share.GetByPath(path)
if err == storm.ErrNotFound {
if err == fm.ErrNotExist {
return http.StatusNotFound, nil
}
@@ -107,7 +106,7 @@ func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
func shareDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
s, err := c.Store.Share.Get(strings.TrimPrefix(r.URL.Path, "/"))
if err == storm.ErrNotFound {
if err == fm.ErrNotExist {
return http.StatusNotFound, nil
}

View File

@@ -287,6 +287,10 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
return http.StatusBadRequest, fm.ErrEmptyPassword
}
if id == c.User.ID && c.User.LockPassword {
return http.StatusForbidden, nil
}
c.User.Password, err = fm.HashPassword(u.Password)
if err != nil {
return http.StatusInternalServerError, err

5
package-lock.json generated
View File

@@ -4941,6 +4941,11 @@
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz",
"integrity": "sha1-q/sd2CRwZ04DIrU86xqvQSk45L8="
},
"noty": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/noty/-/noty-3.1.2.tgz",
"integrity": "sha512-5Rn8695fMcGTJdn8gIrG0sYXXNIEwc8BFq4EpyHF3rFwoAt7/kgN2lKrIZ0bUxtFZuYFcnfqwU+4uF6vm21bVg=="
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",

View File

@@ -14,6 +14,7 @@
"filesize": "^3.5.10",
"moment": "^2.18.1",
"normalize.css": "^7.0.0",
"noty": "^3.1.2",
"vue": "^2.3.3",
"vue-i18n": "^7.1.0",
"vue-router": "^2.7.0",

View File

@@ -1 +1 @@
6a08f1e90218ebb33745a5034efe896329ee9675
3b621522d90af10b96208b839db22160799540b3