chore: add prettier frontent linter
This commit is contained in:
@@ -10,37 +10,34 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import HeaderBar from '@/components/header/HeaderBar'
|
||||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
|
||||
const errors = {
|
||||
403: {
|
||||
icon: 'error',
|
||||
message: 'errors.forbidden'
|
||||
icon: "error",
|
||||
message: "errors.forbidden",
|
||||
},
|
||||
404: {
|
||||
icon: 'gps_off',
|
||||
message: 'errors.notFound'
|
||||
icon: "gps_off",
|
||||
message: "errors.notFound",
|
||||
},
|
||||
500: {
|
||||
icon: 'error_outline',
|
||||
message: 'errors.internal'
|
||||
}
|
||||
}
|
||||
icon: "error_outline",
|
||||
message: "errors.internal",
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'errors',
|
||||
name: "errors",
|
||||
components: {
|
||||
HeaderBar
|
||||
HeaderBar,
|
||||
},
|
||||
props: [
|
||||
'errorCode', 'showHeader'
|
||||
],
|
||||
props: ["errorCode", "showHeader"],
|
||||
data: function () {
|
||||
return {
|
||||
icon: errors[this.errorCode].icon,
|
||||
message: this.$t(errors[this.errorCode].message)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
message: this.$t(errors[this.errorCode].message),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -8,137 +8,137 @@
|
||||
<component v-else-if="currentView" :is="currentView"></component>
|
||||
<div v-else>
|
||||
<h2 class="message">
|
||||
<span>{{ $t('files.loading') }}</span>
|
||||
<span>{{ $t("files.loading") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { files as api } from '@/api'
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import { files as api } from "@/api";
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
|
||||
import HeaderBar from '@/components/header/HeaderBar'
|
||||
import Breadcrumbs from '@/components/Breadcrumbs'
|
||||
import Errors from '@/views/Errors'
|
||||
import Preview from '@/views/files/Preview'
|
||||
import Listing from '@/views/files/Listing'
|
||||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
||||
import Errors from "@/views/Errors";
|
||||
import Preview from "@/views/files/Preview";
|
||||
import Listing from "@/views/files/Listing";
|
||||
|
||||
function clean (path) {
|
||||
return path.endsWith('/') ? path.slice(0, -1) : path
|
||||
function clean(path) {
|
||||
return path.endsWith("/") ? path.slice(0, -1) : path;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'files',
|
||||
name: "files",
|
||||
components: {
|
||||
HeaderBar,
|
||||
Breadcrumbs,
|
||||
Errors,
|
||||
Preview,
|
||||
Listing,
|
||||
Editor: () => import('@/views/files/Editor'),
|
||||
Editor: () => import("@/views/files/Editor"),
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
error: null,
|
||||
width: window.innerWidth
|
||||
}
|
||||
width: window.innerWidth,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'req',
|
||||
'reload',
|
||||
'loading',
|
||||
'show'
|
||||
]),
|
||||
currentView () {
|
||||
...mapState(["req", "reload", "loading", "show"]),
|
||||
currentView() {
|
||||
if (this.req.type == undefined) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.req.isDir) {
|
||||
return 'listing'
|
||||
} else if(this.req.type === 'text' || this.req.type === 'textImmutable') {
|
||||
return 'editor'
|
||||
return "listing";
|
||||
} else if (
|
||||
this.req.type === "text" ||
|
||||
this.req.type === "textImmutable"
|
||||
) {
|
||||
return "editor";
|
||||
} else {
|
||||
return 'preview'
|
||||
return "preview";
|
||||
}
|
||||
},
|
||||
errorCode() {
|
||||
return (this.error.message === '404' || this.error.message === '403') ? parseInt(this.error.message) : 500
|
||||
}
|
||||
return this.error.message === "404" || this.error.message === "403"
|
||||
? parseInt(this.error.message)
|
||||
: 500;
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
watch: {
|
||||
'$route': 'fetchData',
|
||||
'reload': function (value) {
|
||||
if (value === true) {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('keydown', this.keyEvent)
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.keyEvent)
|
||||
},
|
||||
destroyed () {
|
||||
if (this.$store.state.showShell) {
|
||||
this.$store.commit('toggleShell')
|
||||
}
|
||||
this.$store.commit('updateRequest', {})
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([ 'setLoading' ]),
|
||||
async fetchData () {
|
||||
// Reset view information.
|
||||
this.$store.commit('setReload', false)
|
||||
this.$store.commit('resetSelected')
|
||||
this.$store.commit('multiple', false)
|
||||
this.$store.commit('closeHovers')
|
||||
|
||||
// Set loading to true and reset the error.
|
||||
this.setLoading(true)
|
||||
this.error = null
|
||||
|
||||
let url = this.$route.path
|
||||
if (url === '') url = '/'
|
||||
if (url[0] !== '/') url = '/' + url
|
||||
|
||||
try {
|
||||
const res = await api.fetch(url)
|
||||
|
||||
if (clean(res.path) !== clean(`/${this.$route.params.pathMatch}`)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit('updateRequest', res)
|
||||
document.title = res.name
|
||||
} catch (e) {
|
||||
this.error = e
|
||||
} finally {
|
||||
this.setLoading(false)
|
||||
$route: "fetchData",
|
||||
reload: function (value) {
|
||||
if (value === true) {
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
keyEvent (event) {
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.$store.state.showShell) {
|
||||
this.$store.commit("toggleShell");
|
||||
}
|
||||
this.$store.commit("updateRequest", {});
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
async fetchData() {
|
||||
// Reset view information.
|
||||
this.$store.commit("setReload", false);
|
||||
this.$store.commit("resetSelected");
|
||||
this.$store.commit("multiple", false);
|
||||
this.$store.commit("closeHovers");
|
||||
|
||||
// Set loading to true and reset the error.
|
||||
this.setLoading(true);
|
||||
this.error = null;
|
||||
|
||||
let url = this.$route.path;
|
||||
if (url === "") url = "/";
|
||||
if (url[0] !== "/") url = "/" + url;
|
||||
|
||||
try {
|
||||
const res = await api.fetch(url);
|
||||
|
||||
if (clean(res.path) !== clean(`/${this.$route.params.pathMatch}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit("updateRequest", res);
|
||||
document.title = res.name;
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
keyEvent(event) {
|
||||
if (this.show !== null) {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
this.$store.commit('closeHovers')
|
||||
this.$store.commit("closeHovers");
|
||||
}
|
||||
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// F1!
|
||||
if (event.keyCode === 112) {
|
||||
event.preventDefault()
|
||||
this.$store.commit('showHover', 'help')
|
||||
event.preventDefault();
|
||||
this.$store.commit("showHover", "help");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -13,30 +13,31 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Sidebar from '@/components/Sidebar'
|
||||
import Prompts from '@/components/prompts/Prompts'
|
||||
import Shell from '@/components/Shell'
|
||||
import { enableExec } from '@/utils/constants'
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import Sidebar from "@/components/Sidebar";
|
||||
import Prompts from "@/components/prompts/Prompts";
|
||||
import Shell from "@/components/Shell";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
name: 'layout',
|
||||
name: "layout",
|
||||
components: {
|
||||
Sidebar,
|
||||
Prompts,
|
||||
Shell
|
||||
Shell,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([ 'isLogged', 'progress' ]),
|
||||
...mapState([ 'user' ]),
|
||||
isExecEnabled: () => enableExec
|
||||
...mapGetters(["isLogged", "progress"]),
|
||||
...mapState(["user"]),
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
watch: {
|
||||
'$route': function () {
|
||||
this.$store.commit('resetSelected')
|
||||
this.$store.commit('multiple', false)
|
||||
if (this.$store.state.show !== 'success') this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
$route: function () {
|
||||
this.$store.commit("resetSelected");
|
||||
this.$store.commit("multiple", false);
|
||||
if (this.$store.state.show !== "success")
|
||||
this.$store.commit("closeHovers");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,97 +1,127 @@
|
||||
<template>
|
||||
<div id="login" :class="{ recaptcha: recaptcha }">
|
||||
<form @submit="submit">
|
||||
<img :src="logoURL" alt="File Browser">
|
||||
<img :src="logoURL" alt="File Browser" />
|
||||
<h1>{{ name }}</h1>
|
||||
<div v-if="error !== ''" class="wrong">{{ error }}</div>
|
||||
|
||||
<input class="input input--block" type="text" v-model="username" :placeholder="$t('login.username')">
|
||||
<input class="input input--block" type="password" v-model="password" :placeholder="$t('login.password')">
|
||||
<input class="input input--block" v-if="createMode" type="password" v-model="passwordConfirm" :placeholder="$t('login.passwordConfirm')" />
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
v-model="username"
|
||||
:placeholder="$t('login.username')"
|
||||
/>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="password"
|
||||
v-model="password"
|
||||
:placeholder="$t('login.password')"
|
||||
/>
|
||||
<input
|
||||
class="input input--block"
|
||||
v-if="createMode"
|
||||
type="password"
|
||||
v-model="passwordConfirm"
|
||||
:placeholder="$t('login.passwordConfirm')"
|
||||
/>
|
||||
|
||||
<div v-if="recaptcha" id="recaptcha"></div>
|
||||
<input class="button button--block" type="submit" :value="createMode ? $t('login.signup') : $t('login.submit')">
|
||||
<input
|
||||
class="button button--block"
|
||||
type="submit"
|
||||
:value="createMode ? $t('login.signup') : $t('login.submit')"
|
||||
/>
|
||||
|
||||
<p @click="toggleMode" v-if="signup">{{ createMode ? $t('login.loginInstead') : $t('login.createAnAccount') }}</p>
|
||||
<p @click="toggleMode" v-if="signup">
|
||||
{{
|
||||
createMode ? $t("login.loginInstead") : $t("login.createAnAccount")
|
||||
}}
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as auth from '@/utils/auth'
|
||||
import { name, logoURL, recaptcha, recaptchaKey, signup } from '@/utils/constants'
|
||||
import * as auth from "@/utils/auth";
|
||||
import {
|
||||
name,
|
||||
logoURL,
|
||||
recaptcha,
|
||||
recaptchaKey,
|
||||
signup,
|
||||
} from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
name: 'login',
|
||||
name: "login",
|
||||
computed: {
|
||||
signup: () => signup,
|
||||
name: () => name,
|
||||
logoURL: () => logoURL
|
||||
logoURL: () => logoURL,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
createMode: false,
|
||||
error: '',
|
||||
username: '',
|
||||
password: '',
|
||||
error: "",
|
||||
username: "",
|
||||
password: "",
|
||||
recaptcha: recaptcha,
|
||||
passwordConfirm: ''
|
||||
}
|
||||
passwordConfirm: "",
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
if (!recaptcha) return
|
||||
mounted() {
|
||||
if (!recaptcha) return;
|
||||
|
||||
window.grecaptcha.ready(function () {
|
||||
window.grecaptcha.render('recaptcha', {
|
||||
sitekey: recaptchaKey
|
||||
})
|
||||
})
|
||||
window.grecaptcha.render("recaptcha", {
|
||||
sitekey: recaptchaKey,
|
||||
});
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
toggleMode () {
|
||||
this.createMode = !this.createMode
|
||||
toggleMode() {
|
||||
this.createMode = !this.createMode;
|
||||
},
|
||||
async submit (event) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
async submit(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let redirect = this.$route.query.redirect
|
||||
if (redirect === '' || redirect === undefined || redirect === null) {
|
||||
redirect = '/files/'
|
||||
let redirect = this.$route.query.redirect;
|
||||
if (redirect === "" || redirect === undefined || redirect === null) {
|
||||
redirect = "/files/";
|
||||
}
|
||||
|
||||
let captcha = ''
|
||||
let captcha = "";
|
||||
if (recaptcha) {
|
||||
captcha = window.grecaptcha.getResponse()
|
||||
captcha = window.grecaptcha.getResponse();
|
||||
|
||||
if (captcha === '') {
|
||||
this.error = this.$t('login.wrongCredentials')
|
||||
return
|
||||
if (captcha === "") {
|
||||
this.error = this.$t("login.wrongCredentials");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.createMode) {
|
||||
if (this.password !== this.passwordConfirm) {
|
||||
this.error = this.$t('login.passwordsDontMatch')
|
||||
return
|
||||
this.error = this.$t("login.passwordsDontMatch");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.createMode) {
|
||||
await auth.signup(this.username, this.password)
|
||||
await auth.signup(this.username, this.password);
|
||||
}
|
||||
|
||||
await auth.login(this.username, this.password, captcha)
|
||||
this.$router.push({ path: redirect })
|
||||
await auth.login(this.username, this.password, captcha);
|
||||
this.$router.push({ path: redirect });
|
||||
} catch (e) {
|
||||
if (e.message == 409) {
|
||||
this.error = this.$t('login.usernameTaken')
|
||||
this.error = this.$t("login.usernameTaken");
|
||||
} else {
|
||||
this.error = this.$t('login.wrongCredentials')
|
||||
this.error = this.$t("login.wrongCredentials");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -5,10 +5,35 @@
|
||||
<div id="nav">
|
||||
<div class="wrapper">
|
||||
<ul>
|
||||
<router-link to="/settings/profile"><li :class="{ active: $route.path === '/settings/profile' }">{{ $t('settings.profileSettings') }}</li></router-link>
|
||||
<router-link to="/settings/shares"><li :class="{ active: $route.path === '/settings/shares' }">{{ $t('settings.shareManagement') }}</li></router-link>
|
||||
<router-link to="/settings/global"><li :class="{ active: $route.path === '/settings/global' }" v-if="user.perm.admin">{{ $t('settings.globalSettings') }}</li></router-link>
|
||||
<router-link to="/settings/users"><li :class="{ active: $route.path === '/settings/users' || $route.name === 'User' }" v-if="user.perm.admin">{{ $t('settings.userManagement') }}</li></router-link>
|
||||
<router-link to="/settings/profile"
|
||||
><li :class="{ active: $route.path === '/settings/profile' }">
|
||||
{{ $t("settings.profileSettings") }}
|
||||
</li></router-link
|
||||
>
|
||||
<router-link to="/settings/shares"
|
||||
><li :class="{ active: $route.path === '/settings/shares' }">
|
||||
{{ $t("settings.shareManagement") }}
|
||||
</li></router-link
|
||||
>
|
||||
<router-link to="/settings/global"
|
||||
><li
|
||||
:class="{ active: $route.path === '/settings/global' }"
|
||||
v-if="user.perm.admin"
|
||||
>
|
||||
{{ $t("settings.globalSettings") }}
|
||||
</li></router-link
|
||||
>
|
||||
<router-link to="/settings/users"
|
||||
><li
|
||||
:class="{
|
||||
active:
|
||||
$route.path === '/settings/users' || $route.name === 'User',
|
||||
}"
|
||||
v-if="user.perm.admin"
|
||||
>
|
||||
{{ $t("settings.userManagement") }}
|
||||
</li></router-link
|
||||
>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,17 +43,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from "vuex";
|
||||
|
||||
import HeaderBar from '@/components/header/HeaderBar'
|
||||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
|
||||
export default {
|
||||
name: 'settings',
|
||||
name: "settings",
|
||||
components: {
|
||||
HeaderBar
|
||||
HeaderBar,
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'user' ])
|
||||
}
|
||||
}
|
||||
...mapState(["user"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,8 +3,18 @@
|
||||
<header-bar showMenu showLogo>
|
||||
<title />
|
||||
|
||||
<action v-if="selectedCount" icon="file_download" :label="$t('buttons.download')" @action="download" :counter="selectedCount" />
|
||||
<action icon="check_circle" :label="$t('buttons.selectMultiple')" @action="toggleMultipleSelection" />
|
||||
<action
|
||||
v-if="selectedCount"
|
||||
icon="file_download"
|
||||
:label="$t('buttons.download')"
|
||||
@action="download"
|
||||
:counter="selectedCount"
|
||||
/>
|
||||
<action
|
||||
icon="check_circle"
|
||||
:label="$t('buttons.selectMultiple')"
|
||||
@action="toggleMultipleSelection"
|
||||
/>
|
||||
</header-bar>
|
||||
|
||||
<breadcrumbs :base="'/share/' + hash" />
|
||||
@@ -12,34 +22,44 @@
|
||||
<div v-if="!loading">
|
||||
<div class="share">
|
||||
<div class="share__box share__box__info">
|
||||
<div class="share__box__header">
|
||||
{{ req.isDir ? $t('download.downloadFolder') : $t('download.downloadFile') }}
|
||||
</div>
|
||||
<div class="share__box__element share__box__center share__box__icon">
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t('prompts.displayName') }}</strong> {{ req.name }}
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime }}
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t('prompts.size') }}:</strong> {{ humanSize }}
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<a target="_blank" :href="link" class="button button--flat">{{ $t('buttons.download') }}</a>
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<qrcode-vue :value="fullLink" size="200" level="M"></qrcode-vue>
|
||||
</div>
|
||||
<div class="share__box__header">
|
||||
{{
|
||||
req.isDir
|
||||
? $t("download.downloadFolder")
|
||||
: $t("download.downloadFile")
|
||||
}}
|
||||
</div>
|
||||
<div class="share__box__element share__box__center share__box__icon">
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t("prompts.displayName") }}</strong> {{ req.name }}
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t("prompts.size") }}:</strong> {{ humanSize }}
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<a target="_blank" :href="link" class="button button--flat">{{
|
||||
$t("buttons.download")
|
||||
}}</a>
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<qrcode-vue :value="fullLink" size="200" level="M"></qrcode-vue>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="req.isDir && req.items.length > 0" class="share__box share__box__items">
|
||||
<div
|
||||
v-if="req.isDir && req.items.length > 0"
|
||||
class="share__box share__box__items"
|
||||
>
|
||||
<div class="share__box__header" v-if="req.isDir">
|
||||
{{ $t('files.files') }}
|
||||
{{ $t("files.files") }}
|
||||
</div>
|
||||
<div id="listing" class="list">
|
||||
<item v-for="(item) in req.items.slice(0, this.showLimit)"
|
||||
<item
|
||||
v-for="item in req.items.slice(0, this.showLimit)"
|
||||
:key="base64(item.name)"
|
||||
v-bind:index="item.index"
|
||||
v-bind:name="item.name"
|
||||
@@ -48,26 +68,40 @@
|
||||
v-bind:modified="item.modified"
|
||||
v-bind:type="item.type"
|
||||
v-bind:size="item.size"
|
||||
readOnly>
|
||||
readOnly
|
||||
>
|
||||
</item>
|
||||
<div v-if="req.items.length > showLimit" class="item">
|
||||
<div>
|
||||
<p class="name"> + {{ req.items.length - showLimit }} </p>
|
||||
<p class="name">+ {{ req.items.length - showLimit }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="{ active: $store.state.multiple }" id="multiple-selection">
|
||||
<p>{{ $t('files.multipleSelectionEnabled') }}</p>
|
||||
<div @click="$store.commit('multiple', false)" tabindex="0" role="button" :title="$t('files.clear')" :aria-label="$t('files.clear')" class="action">
|
||||
<div
|
||||
:class="{ active: $store.state.multiple }"
|
||||
id="multiple-selection"
|
||||
>
|
||||
<p>{{ $t("files.multipleSelectionEnabled") }}</p>
|
||||
<div
|
||||
@click="$store.commit('multiple', false)"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:title="$t('files.clear')"
|
||||
:aria-label="$t('files.clear')"
|
||||
class="action"
|
||||
>
|
||||
<i class="material-icons">clear</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="req.isDir && req.items.length === 0" class="share__box share__box__items">
|
||||
<div
|
||||
v-else-if="req.isDir && req.items.length === 0"
|
||||
class="share__box share__box__items"
|
||||
>
|
||||
<h2 class="message">
|
||||
<i class="material-icons">sentiment_dissatisfied</i>
|
||||
<span>{{ $t('files.lonely') }}</span>
|
||||
<span>{{ $t("files.lonely") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,19 +109,31 @@
|
||||
<div v-if="error">
|
||||
<div v-if="error.message === '401'">
|
||||
<div class="card floating" id="password">
|
||||
<div v-if="attemptedPasswordLogin" class="share__wrong__password">{{ $t('login.wrongCredentials') }}</div>
|
||||
<div v-if="attemptedPasswordLogin" class="share__wrong__password">
|
||||
{{ $t("login.wrongCredentials") }}
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('login.password') }}</h2>
|
||||
<h2>{{ $t("login.password") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<input v-focus type="password" :placeholder="$t('login.password')" v-model="password" @keyup.enter="fetchData">
|
||||
<input
|
||||
v-focus
|
||||
type="password"
|
||||
:placeholder="$t('login.password')"
|
||||
v-model="password"
|
||||
@keyup.enter="fetchData"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button class="button button--flat"
|
||||
<button
|
||||
class="button button--flat"
|
||||
@click="fetchData"
|
||||
:aria-label="$t('buttons.submit')"
|
||||
:title="$t('buttons.submit')">{{ $t('buttons.submit') }}</button>
|
||||
:title="$t('buttons.submit')"
|
||||
>
|
||||
{{ $t("buttons.submit") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,156 +143,163 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapMutations, mapGetters} from 'vuex';
|
||||
import { pub as api } from '@/api'
|
||||
import { baseURL } from '@/utils/constants'
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
import { mapState, mapMutations, mapGetters } from "vuex";
|
||||
import { pub as api } from "@/api";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import filesize from "filesize";
|
||||
import moment from "moment";
|
||||
|
||||
import HeaderBar from '@/components/header/HeaderBar'
|
||||
import Action from '@/components/header/Action'
|
||||
import Breadcrumbs from '@/components/Breadcrumbs'
|
||||
import Errors from '@/views/Errors'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
import Item from "@/components/files/ListingItem"
|
||||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
import Action from "@/components/header/Action";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
||||
import Errors from "@/views/Errors";
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import Item from "@/components/files/ListingItem";
|
||||
|
||||
export default {
|
||||
name: 'share',
|
||||
name: "share",
|
||||
components: {
|
||||
HeaderBar,
|
||||
Action,
|
||||
Breadcrumbs,
|
||||
Item,
|
||||
QrcodeVue,
|
||||
Errors
|
||||
Errors,
|
||||
},
|
||||
data: () => ({
|
||||
error: null,
|
||||
showLimit: 500,
|
||||
password: '',
|
||||
password: "",
|
||||
attemptedPasswordLogin: false,
|
||||
hash: null,
|
||||
token: null
|
||||
token: null,
|
||||
}),
|
||||
watch: {
|
||||
'$route': 'fetchData'
|
||||
$route: "fetchData",
|
||||
},
|
||||
created: async function () {
|
||||
const hash = this.$route.params.pathMatch.split('/')[0]
|
||||
this.hash = hash
|
||||
await this.fetchData()
|
||||
const hash = this.$route.params.pathMatch.split("/")[0];
|
||||
this.hash = hash;
|
||||
await this.fetchData();
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('keydown', this.keyEvent)
|
||||
mounted() {
|
||||
window.addEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.keyEvent)
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'loading', 'multiple', 'selected']),
|
||||
...mapGetters(['selectedCount', 'selectedCount']),
|
||||
...mapState(["req", "loading", "multiple", "selected"]),
|
||||
...mapGetters(["selectedCount", "selectedCount"]),
|
||||
icon: function () {
|
||||
if (this.req.isDir) return 'folder'
|
||||
if (this.req.type === 'image') return 'insert_photo'
|
||||
if (this.req.type === 'audio') return 'volume_up'
|
||||
if (this.req.type === 'video') return 'movie'
|
||||
return 'insert_drive_file'
|
||||
if (this.req.isDir) return "folder";
|
||||
if (this.req.type === "image") return "insert_photo";
|
||||
if (this.req.type === "audio") return "volume_up";
|
||||
if (this.req.type === "video") return "movie";
|
||||
return "insert_drive_file";
|
||||
},
|
||||
link: function () {
|
||||
let queryArg = '';
|
||||
if (this.token !== ''){
|
||||
queryArg = `?token=${this.token}`
|
||||
let queryArg = "";
|
||||
if (this.token !== "") {
|
||||
queryArg = `?token=${this.token}`;
|
||||
}
|
||||
|
||||
const path = this.$route.path.split('/').splice(2).join('/')
|
||||
return `${baseURL}/api/public/dl/${path}${queryArg}`
|
||||
const path = this.$route.path.split("/").splice(2).join("/");
|
||||
return `${baseURL}/api/public/dl/${path}${queryArg}`;
|
||||
},
|
||||
fullLink: function () {
|
||||
return window.location.origin + this.link
|
||||
return window.location.origin + this.link;
|
||||
},
|
||||
humanSize: function () {
|
||||
if (this.req.isDir) {
|
||||
return this.req.items.length
|
||||
return this.req.items.length;
|
||||
}
|
||||
|
||||
return filesize(this.req.size)
|
||||
return filesize(this.req.size);
|
||||
},
|
||||
humanTime: function () {
|
||||
return moment(this.req.modified).fromNow()
|
||||
return moment(this.req.modified).fromNow();
|
||||
},
|
||||
errorCode() {
|
||||
return (this.error.message === '404' || this.error.message === '403') ? parseInt(this.error.message) : 500
|
||||
}
|
||||
return this.error.message === "404" || this.error.message === "403"
|
||||
? parseInt(this.error.message)
|
||||
: 500;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([ 'resetSelected', 'updateRequest', 'setLoading' ]),
|
||||
...mapMutations(["resetSelected", "updateRequest", "setLoading"]),
|
||||
base64: function (name) {
|
||||
return window.btoa(unescape(encodeURIComponent(name)))
|
||||
return window.btoa(unescape(encodeURIComponent(name)));
|
||||
},
|
||||
fetchData: async function () {
|
||||
// Reset view information.
|
||||
this.$store.commit('setReload', false)
|
||||
this.$store.commit('resetSelected')
|
||||
this.$store.commit('multiple', false)
|
||||
this.$store.commit('closeHovers')
|
||||
this.$store.commit("setReload", false);
|
||||
this.$store.commit("resetSelected");
|
||||
this.$store.commit("multiple", false);
|
||||
this.$store.commit("closeHovers");
|
||||
|
||||
// Set loading to true and reset the error.
|
||||
this.setLoading(true)
|
||||
this.error = null
|
||||
this.setLoading(true);
|
||||
this.error = null;
|
||||
|
||||
if (this.password !== ''){
|
||||
this.attemptedPasswordLogin = true
|
||||
if (this.password !== "") {
|
||||
this.attemptedPasswordLogin = true;
|
||||
}
|
||||
|
||||
let url = this.$route.path
|
||||
if (url === '') url = '/'
|
||||
if (url[0] !== '/') url = '/' + url
|
||||
let url = this.$route.path;
|
||||
if (url === "") url = "/";
|
||||
if (url[0] !== "/") url = "/" + url;
|
||||
|
||||
try {
|
||||
let file = await api.fetch(url, this.password)
|
||||
let file = await api.fetch(url, this.password);
|
||||
|
||||
this.token = file.token || ''
|
||||
this.token = file.token || "";
|
||||
|
||||
this.updateRequest(file)
|
||||
this.setLoading(false)
|
||||
this.updateRequest(file);
|
||||
this.setLoading(false);
|
||||
} catch (e) {
|
||||
this.error = e
|
||||
this.error = e;
|
||||
}
|
||||
},
|
||||
keyEvent (event) {
|
||||
keyEvent(event) {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
// If we're on a listing, unselect all
|
||||
// files and folders.
|
||||
if (this.selectedCount > 0) {
|
||||
this.resetSelected()
|
||||
this.resetSelected();
|
||||
}
|
||||
}
|
||||
},
|
||||
toggleMultipleSelection () {
|
||||
this.$store.commit('multiple', !this.multiple)
|
||||
toggleMultipleSelection() {
|
||||
this.$store.commit("multiple", !this.multiple);
|
||||
},
|
||||
download () {
|
||||
download() {
|
||||
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
|
||||
api.download(null, this.hash, this.token, this.req.items[this.selected[0]].path)
|
||||
return
|
||||
api.download(
|
||||
null,
|
||||
this.hash,
|
||||
this.token,
|
||||
this.req.items[this.selected[0]].path
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit('showHover', {
|
||||
prompt: 'download',
|
||||
this.$store.commit("showHover", {
|
||||
prompt: "download",
|
||||
confirm: (format) => {
|
||||
this.$store.commit('closeHovers')
|
||||
this.$store.commit("closeHovers");
|
||||
|
||||
let files = []
|
||||
let files = [];
|
||||
|
||||
for (let i of this.selected) {
|
||||
files.push(this.req.items[i].path)
|
||||
files.push(this.req.items[i].path);
|
||||
}
|
||||
|
||||
api.download(format, this.hash, this.token, ...files)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
api.download(format, this.hash, this.token, ...files);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
<title>{{ req.name }}</title>
|
||||
|
||||
<template #actions>
|
||||
<action id="save-button" icon="save" :label="$t('buttons.save')" @action="save()" />
|
||||
<action
|
||||
id="save-button"
|
||||
icon="save"
|
||||
:label="$t('buttons.save')"
|
||||
@action="save()"
|
||||
/>
|
||||
</template>
|
||||
</header-bar>
|
||||
|
||||
@@ -16,120 +21,120 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
import { theme } from '@/utils/constants'
|
||||
import buttons from '@/utils/buttons'
|
||||
import url from '@/utils/url'
|
||||
import { mapState } from "vuex";
|
||||
import { files as api } from "@/api";
|
||||
import { theme } from "@/utils/constants";
|
||||
import buttons from "@/utils/buttons";
|
||||
import url from "@/utils/url";
|
||||
|
||||
import ace from 'ace-builds/src-min-noconflict/ace.js'
|
||||
import modelist from 'ace-builds/src-min-noconflict/ext-modelist.js'
|
||||
import 'ace-builds/webpack-resolver'
|
||||
import ace from "ace-builds/src-min-noconflict/ace.js";
|
||||
import modelist from "ace-builds/src-min-noconflict/ext-modelist.js";
|
||||
import "ace-builds/webpack-resolver";
|
||||
|
||||
import HeaderBar from '@/components/header/HeaderBar'
|
||||
import Action from '@/components/header/Action'
|
||||
import Breadcrumbs from '@/components/Breadcrumbs'
|
||||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
import Action from "@/components/header/Action";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
||||
|
||||
export default {
|
||||
name: 'editor',
|
||||
name: "editor",
|
||||
components: {
|
||||
HeaderBar,
|
||||
Action,
|
||||
Breadcrumbs
|
||||
Breadcrumbs,
|
||||
},
|
||||
data: function () {
|
||||
return {}
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'user']),
|
||||
breadcrumbs () {
|
||||
let parts = this.$route.path.split('/')
|
||||
...mapState(["req", "user"]),
|
||||
breadcrumbs() {
|
||||
let parts = this.$route.path.split("/");
|
||||
|
||||
if (parts[0] === '') {
|
||||
parts.shift()
|
||||
if (parts[0] === "") {
|
||||
parts.shift();
|
||||
}
|
||||
|
||||
if (parts[parts.length - 1] === '') {
|
||||
parts.pop()
|
||||
if (parts[parts.length - 1] === "") {
|
||||
parts.pop();
|
||||
}
|
||||
|
||||
let breadcrumbs = []
|
||||
let breadcrumbs = [];
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
breadcrumbs.push({ name: decodeURIComponent(parts[i]) })
|
||||
breadcrumbs.push({ name: decodeURIComponent(parts[i]) });
|
||||
}
|
||||
|
||||
breadcrumbs.shift()
|
||||
breadcrumbs.shift();
|
||||
|
||||
if (breadcrumbs.length > 3) {
|
||||
while (breadcrumbs.length !== 4) {
|
||||
breadcrumbs.shift()
|
||||
breadcrumbs.shift();
|
||||
}
|
||||
|
||||
breadcrumbs[0].name = '...'
|
||||
breadcrumbs[0].name = "...";
|
||||
}
|
||||
|
||||
return breadcrumbs
|
||||
}
|
||||
return breadcrumbs;
|
||||
},
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('keydown', this.keyEvent)
|
||||
created() {
|
||||
window.addEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.keyEvent)
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
this.editor.destroy();
|
||||
},
|
||||
mounted: function () {
|
||||
const fileContent = this.req.content || '';
|
||||
const fileContent = this.req.content || "";
|
||||
|
||||
this.editor = ace.edit('editor', {
|
||||
this.editor = ace.edit("editor", {
|
||||
value: fileContent,
|
||||
showPrintMargin: false,
|
||||
readOnly: this.req.type === 'textImmutable',
|
||||
theme: 'ace/theme/chrome',
|
||||
readOnly: this.req.type === "textImmutable",
|
||||
theme: "ace/theme/chrome",
|
||||
mode: modelist.getModeForPath(this.req.name).mode,
|
||||
wrap: true
|
||||
})
|
||||
wrap: true,
|
||||
});
|
||||
|
||||
if (theme == 'dark') {
|
||||
if (theme == "dark") {
|
||||
this.editor.setTheme("ace/theme/twilight");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back () {
|
||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
||||
this.$router.push({ path: uri })
|
||||
back() {
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
},
|
||||
keyEvent (event) {
|
||||
keyEvent(event) {
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (String.fromCharCode(event.which).toLowerCase() !== 's') {
|
||||
return
|
||||
if (String.fromCharCode(event.which).toLowerCase() !== "s") {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
this.save()
|
||||
event.preventDefault();
|
||||
this.save();
|
||||
},
|
||||
async save () {
|
||||
const button = 'save'
|
||||
buttons.loading('save')
|
||||
async save() {
|
||||
const button = "save";
|
||||
buttons.loading("save");
|
||||
|
||||
try {
|
||||
await api.put(this.$route.path, this.editor.getValue())
|
||||
buttons.success(button)
|
||||
await api.put(this.$route.path, this.editor.getValue());
|
||||
buttons.success(button);
|
||||
} catch (e) {
|
||||
buttons.done(button)
|
||||
this.$showError(e)
|
||||
buttons.done(button);
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
close () {
|
||||
this.$store.commit('updateRequest', {})
|
||||
close() {
|
||||
this.$store.commit("updateRequest", {});
|
||||
|
||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
||||
this.$router.push({ path: uri })
|
||||
}
|
||||
}
|
||||
}
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,45 @@
|
||||
<template>
|
||||
<div id="previewer" @mousemove="toggleNavigation" @touchstart="toggleNavigation">
|
||||
<div
|
||||
id="previewer"
|
||||
@mousemove="toggleNavigation"
|
||||
@touchstart="toggleNavigation"
|
||||
>
|
||||
<header-bar>
|
||||
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
||||
<title>{{ name }}</title>
|
||||
<action :disabled="loading" v-if="isResizeEnabled && req.type === 'image'" :icon="fullSize ? 'photo_size_select_large' : 'hd'" @action="toggleSize" />
|
||||
<action
|
||||
:disabled="loading"
|
||||
v-if="isResizeEnabled && req.type === 'image'"
|
||||
:icon="fullSize ? 'photo_size_select_large' : 'hd'"
|
||||
@action="toggleSize"
|
||||
/>
|
||||
|
||||
<template #actions>
|
||||
<action :disabled="loading" icon="mode_edit" :label="$t('buttons.rename')" show="rename" />
|
||||
<action :disabled="loading" icon="delete" :label="$t('buttons.delete')" @action="deleteFile" id="delete-button" />
|
||||
<action :disabled="loading" icon="file_download" :label="$t('buttons.download')" @action="download" />
|
||||
<action :disabled="loading" icon="info" :label="$t('buttons.info')" show="info" />
|
||||
<action
|
||||
:disabled="loading"
|
||||
icon="mode_edit"
|
||||
:label="$t('buttons.rename')"
|
||||
show="rename"
|
||||
/>
|
||||
<action
|
||||
:disabled="loading"
|
||||
icon="delete"
|
||||
:label="$t('buttons.delete')"
|
||||
@action="deleteFile"
|
||||
id="delete-button"
|
||||
/>
|
||||
<action
|
||||
:disabled="loading"
|
||||
icon="file_download"
|
||||
:label="$t('buttons.download')"
|
||||
@action="download"
|
||||
/>
|
||||
<action
|
||||
:disabled="loading"
|
||||
icon="info"
|
||||
:label="$t('buttons.info')"
|
||||
show="info"
|
||||
/>
|
||||
</template>
|
||||
</header-bar>
|
||||
|
||||
@@ -31,221 +61,249 @@
|
||||
v-for="(sub, index) in subtitles"
|
||||
:key="index"
|
||||
:src="sub"
|
||||
:label="'Subtitle ' + index" :default="index === 0">
|
||||
Sorry, your browser doesn't support embedded videos,
|
||||
but don't worry, you can <a :href="downloadUrl">download it</a>
|
||||
:label="'Subtitle ' + index"
|
||||
:default="index === 0"
|
||||
/>
|
||||
Sorry, your browser doesn't support embedded videos, but don't worry,
|
||||
you can <a :href="downloadUrl">download it</a>
|
||||
and watch it with your favorite video player!
|
||||
</video>
|
||||
<object v-else-if="req.extension.toLowerCase() == '.pdf'" class="pdf" :data="raw"></object>
|
||||
<object
|
||||
v-else-if="req.extension.toLowerCase() == '.pdf'"
|
||||
class="pdf"
|
||||
:data="raw"
|
||||
></object>
|
||||
<a v-else-if="req.type == 'blob'" :href="downloadUrl">
|
||||
<h2 class="message">{{ $t('buttons.download') }} <i class="material-icons">file_download</i></h2>
|
||||
<h2 class="message">
|
||||
{{ $t("buttons.download") }}
|
||||
<i class="material-icons">file_download</i>
|
||||
</h2>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<button @click="prev" @mouseover="hoverNav = true" @mouseleave="hoverNav = false" :class="{ hidden: !hasPrevious || !showNav }" :aria-label="$t('buttons.previous')" :title="$t('buttons.previous')">
|
||||
<button
|
||||
@click="prev"
|
||||
@mouseover="hoverNav = true"
|
||||
@mouseleave="hoverNav = false"
|
||||
:class="{ hidden: !hasPrevious || !showNav }"
|
||||
:aria-label="$t('buttons.previous')"
|
||||
:title="$t('buttons.previous')"
|
||||
>
|
||||
<i class="material-icons">chevron_left</i>
|
||||
</button>
|
||||
<button @click="next" @mouseover="hoverNav = true" @mouseleave="hoverNav = false" :class="{ hidden: !hasNext || !showNav }" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
|
||||
<button
|
||||
@click="next"
|
||||
@mouseover="hoverNav = true"
|
||||
@mouseleave="hoverNav = false"
|
||||
:class="{ hidden: !hasNext || !showNav }"
|
||||
:aria-label="$t('buttons.next')"
|
||||
:title="$t('buttons.next')"
|
||||
>
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
import { baseURL, resizePreview } from '@/utils/constants'
|
||||
import url from '@/utils/url'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { mapState } from "vuex";
|
||||
import { files as api } from "@/api";
|
||||
import { baseURL, resizePreview } from "@/utils/constants";
|
||||
import url from "@/utils/url";
|
||||
import throttle from "lodash.throttle";
|
||||
|
||||
import HeaderBar from '@/components/header/HeaderBar'
|
||||
import Action from '@/components/header/Action'
|
||||
import ExtendedImage from '@/components/files/ExtendedImage'
|
||||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
import Action from "@/components/header/Action";
|
||||
import ExtendedImage from "@/components/files/ExtendedImage";
|
||||
|
||||
const mediaTypes = [
|
||||
"image",
|
||||
"video",
|
||||
"audio",
|
||||
"blob"
|
||||
]
|
||||
const mediaTypes = ["image", "video", "audio", "blob"];
|
||||
|
||||
export default {
|
||||
name: 'preview',
|
||||
name: "preview",
|
||||
components: {
|
||||
HeaderBar,
|
||||
Action,
|
||||
ExtendedImage
|
||||
ExtendedImage,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
previousLink: '',
|
||||
nextLink: '',
|
||||
previousLink: "",
|
||||
nextLink: "",
|
||||
listing: null,
|
||||
name: '',
|
||||
name: "",
|
||||
subtitles: [],
|
||||
fullSize: false,
|
||||
showNav: true,
|
||||
navTimeout: null,
|
||||
hoverNav: false
|
||||
}
|
||||
hoverNav: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'user', 'oldReq', 'jwt', 'loading', 'show']),
|
||||
hasPrevious () {
|
||||
return (this.previousLink !== '')
|
||||
...mapState(["req", "user", "oldReq", "jwt", "loading", "show"]),
|
||||
hasPrevious() {
|
||||
return this.previousLink !== "";
|
||||
},
|
||||
hasNext () {
|
||||
return (this.nextLink !== '')
|
||||
hasNext() {
|
||||
return this.nextLink !== "";
|
||||
},
|
||||
downloadUrl () {
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${this.jwt}`
|
||||
downloadUrl() {
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${
|
||||
this.jwt
|
||||
}`;
|
||||
},
|
||||
previewUrl () {
|
||||
previewUrl() {
|
||||
// reload the image when the file is replaced
|
||||
const key = Date.parse(this.req.modified)
|
||||
const key = Date.parse(this.req.modified);
|
||||
|
||||
if (this.req.type === 'image' && !this.fullSize) {
|
||||
return `${baseURL}/api/preview/big${url.encodePath(this.req.path)}?auth=${this.jwt}&k=${key}`
|
||||
if (this.req.type === "image" && !this.fullSize) {
|
||||
return `${baseURL}/api/preview/big${url.encodePath(
|
||||
this.req.path
|
||||
)}?auth=${this.jwt}&k=${key}`;
|
||||
}
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${this.jwt}&k=${key}`
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${
|
||||
this.jwt
|
||||
}&k=${key}`;
|
||||
},
|
||||
raw () {
|
||||
return `${this.previewUrl}&inline=true`
|
||||
raw() {
|
||||
return `${this.previewUrl}&inline=true`;
|
||||
},
|
||||
showMore () {
|
||||
return this.$store.state.show === 'more'
|
||||
showMore() {
|
||||
return this.$store.state.show === "more";
|
||||
},
|
||||
isResizeEnabled() {
|
||||
return resizePreview;
|
||||
},
|
||||
isResizeEnabled () {
|
||||
return resizePreview
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route: function () {
|
||||
this.updatePreview()
|
||||
this.toggleNavigation()
|
||||
}
|
||||
this.updatePreview();
|
||||
this.toggleNavigation();
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
window.addEventListener('keydown', this.key)
|
||||
this.listing = this.oldReq.items
|
||||
this.updatePreview()
|
||||
async mounted() {
|
||||
window.addEventListener("keydown", this.key);
|
||||
this.listing = this.oldReq.items;
|
||||
this.updatePreview();
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.key)
|
||||
beforeDestroy() {
|
||||
window.removeEventListener("keydown", this.key);
|
||||
},
|
||||
methods: {
|
||||
deleteFile () {
|
||||
this.$store.commit('showHover', {
|
||||
prompt: 'delete',
|
||||
deleteFile() {
|
||||
this.$store.commit("showHover", {
|
||||
prompt: "delete",
|
||||
confirm: () => {
|
||||
this.listing = this.listing.filter(item => item.name !== this.name)
|
||||
this.listing = this.listing.filter((item) => item.name !== this.name);
|
||||
|
||||
if (this.hasNext) {
|
||||
this.next()
|
||||
this.next();
|
||||
} else if (!this.hasPrevious && !this.hasNext) {
|
||||
this.close()
|
||||
this.close();
|
||||
} else {
|
||||
this.prev()
|
||||
this.prev();
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
},
|
||||
prev () {
|
||||
this.hoverNav = false
|
||||
this.$router.push({ path: this.previousLink })
|
||||
prev() {
|
||||
this.hoverNav = false;
|
||||
this.$router.push({ path: this.previousLink });
|
||||
},
|
||||
next () {
|
||||
this.hoverNav = false
|
||||
this.$router.push({ path: this.nextLink })
|
||||
next() {
|
||||
this.hoverNav = false;
|
||||
this.$router.push({ path: this.nextLink });
|
||||
},
|
||||
key (event) {
|
||||
|
||||
key(event) {
|
||||
if (this.show !== null) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.which === 13 || event.which === 39) { // right arrow
|
||||
if (this.hasNext) this.next()
|
||||
} else if (event.which === 37) { // left arrow
|
||||
if (this.hasPrevious) this.prev()
|
||||
} else if (event.which === 27) { // esc
|
||||
this.close()
|
||||
if (event.which === 13 || event.which === 39) {
|
||||
// right arrow
|
||||
if (this.hasNext) this.next();
|
||||
} else if (event.which === 37) {
|
||||
// left arrow
|
||||
if (this.hasPrevious) this.prev();
|
||||
} else if (event.which === 27) {
|
||||
// esc
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
async updatePreview () {
|
||||
async updatePreview() {
|
||||
if (this.req.subtitles) {
|
||||
this.subtitles = this.req.subtitles.map(sub => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`)
|
||||
this.subtitles = this.req.subtitles.map(
|
||||
(sub) => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`
|
||||
);
|
||||
}
|
||||
|
||||
let dirs = this.$route.fullPath.split("/")
|
||||
this.name = decodeURIComponent(dirs[dirs.length - 1])
|
||||
let dirs = this.$route.fullPath.split("/");
|
||||
this.name = decodeURIComponent(dirs[dirs.length - 1]);
|
||||
|
||||
if (!this.listing) {
|
||||
try {
|
||||
const path = url.removeLastDir(this.$route.path)
|
||||
const res = await api.fetch(path)
|
||||
this.listing = res.items
|
||||
const path = url.removeLastDir(this.$route.path);
|
||||
const res = await api.fetch(path);
|
||||
this.listing = res.items;
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.previousLink = ''
|
||||
this.nextLink = ''
|
||||
this.previousLink = "";
|
||||
this.nextLink = "";
|
||||
|
||||
for (let i = 0; i < this.listing.length; i++) {
|
||||
if (this.listing[i].name !== this.name) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = i - 1; j >= 0; j--) {
|
||||
if (mediaTypes.includes(this.listing[j].type)) {
|
||||
this.previousLink = this.listing[j].url
|
||||
break
|
||||
this.previousLink = this.listing[j].url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = i + 1; j < this.listing.length; j++) {
|
||||
if (mediaTypes.includes(this.listing[j].type)) {
|
||||
this.nextLink = this.listing[j].url
|
||||
break
|
||||
this.nextLink = this.listing[j].url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return;
|
||||
}
|
||||
},
|
||||
openMore () {
|
||||
this.$store.commit('showHover', 'more')
|
||||
openMore() {
|
||||
this.$store.commit("showHover", "more");
|
||||
},
|
||||
resetPrompts () {
|
||||
this.$store.commit('closeHovers')
|
||||
resetPrompts() {
|
||||
this.$store.commit("closeHovers");
|
||||
},
|
||||
toggleSize () {
|
||||
this.fullSize = !this.fullSize
|
||||
toggleSize() {
|
||||
this.fullSize = !this.fullSize;
|
||||
},
|
||||
toggleNavigation: throttle(function() {
|
||||
this.showNav = true
|
||||
toggleNavigation: throttle(function () {
|
||||
this.showNav = true;
|
||||
|
||||
if (this.navTimeout) {
|
||||
clearTimeout(this.navTimeout)
|
||||
clearTimeout(this.navTimeout);
|
||||
}
|
||||
|
||||
this.navTimeout = setTimeout(() => {
|
||||
this.showNav = false || this.hoverNav
|
||||
this.navTimeout = null
|
||||
this.showNav = false || this.hoverNav;
|
||||
this.navTimeout = null;
|
||||
}, 1500);
|
||||
}, 500),
|
||||
close () {
|
||||
this.$store.commit('updateRequest', {})
|
||||
close() {
|
||||
this.$store.commit("updateRequest", {});
|
||||
|
||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
||||
this.$router.push({ path: uri })
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
},
|
||||
download() {
|
||||
api.download(null, this.$route.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
api.download(null, this.$route.path);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,54 +3,93 @@
|
||||
<div class="column">
|
||||
<form class="card" @submit.prevent="save">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.globalSettings') }}</h2>
|
||||
<h2>{{ $t("settings.globalSettings") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p><input type="checkbox" v-model="settings.signup"> {{ $t('settings.allowSignup') }}</p>
|
||||
<p>
|
||||
<input type="checkbox" v-model="settings.signup" />
|
||||
{{ $t("settings.allowSignup") }}
|
||||
</p>
|
||||
|
||||
<p><input type="checkbox" v-model="settings.createUserDir"> {{ $t('settings.createUserDir') }}</p>
|
||||
<p>
|
||||
<input type="checkbox" v-model="settings.createUserDir" />
|
||||
{{ $t("settings.createUserDir") }}
|
||||
</p>
|
||||
|
||||
<h3>{{ $t('settings.rules') }}</h3>
|
||||
<p class="small">{{ $t('settings.globalRules') }}</p>
|
||||
<h3>{{ $t("settings.rules") }}</h3>
|
||||
<p class="small">{{ $t("settings.globalRules") }}</p>
|
||||
<rules :rules.sync="settings.rules" />
|
||||
|
||||
<div v-if="isExecEnabled">
|
||||
<h3>{{ $t('settings.executeOnShell') }}</h3>
|
||||
<p class="small">{{ $t('settings.executeOnShellDescription') }}</p>
|
||||
<input class="input input--block" type="text" placeholder="bash -c, cmd /c, ..." v-model="settings.shell" />
|
||||
<h3>{{ $t("settings.executeOnShell") }}</h3>
|
||||
<p class="small">{{ $t("settings.executeOnShellDescription") }}</p>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
placeholder="bash -c, cmd /c, ..."
|
||||
v-model="settings.shell"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3>{{ $t('settings.branding') }}</h3>
|
||||
<h3>{{ $t("settings.branding") }}</h3>
|
||||
|
||||
<i18n path="settings.brandingHelp" tag="p" class="small">
|
||||
<a class="link" target="_blank" href="https://filebrowser.org/configuration/custom-branding">{{ $t('settings.documentation') }}</a>
|
||||
<a
|
||||
class="link"
|
||||
target="_blank"
|
||||
href="https://filebrowser.org/configuration/custom-branding"
|
||||
>{{ $t("settings.documentation") }}</a
|
||||
>
|
||||
</i18n>
|
||||
|
||||
<p>
|
||||
<input type="checkbox" v-model="settings.branding.disableExternal" id="branding-links" />
|
||||
{{ $t('settings.disableExternalLinks') }}
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="settings.branding.disableExternal"
|
||||
id="branding-links"
|
||||
/>
|
||||
{{ $t("settings.disableExternalLinks") }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="theme">{{ $t('settings.themes.title') }}</label>
|
||||
<themes class="input input--block" :theme.sync="settings.branding.theme" id="theme"></themes>
|
||||
<label for="theme">{{ $t("settings.themes.title") }}</label>
|
||||
<themes
|
||||
class="input input--block"
|
||||
:theme.sync="settings.branding.theme"
|
||||
id="theme"
|
||||
></themes>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="branding-name">{{ $t('settings.instanceName') }}</label>
|
||||
<input class="input input--block" type="text" v-model="settings.branding.name" id="branding-name" />
|
||||
<label for="branding-name">{{ $t("settings.instanceName") }}</label>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
v-model="settings.branding.name"
|
||||
id="branding-name"
|
||||
/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="branding-files">{{ $t('settings.brandingDirectoryPath') }}</label>
|
||||
<input class="input input--block" type="text" v-model="settings.branding.files" id="branding-files" />
|
||||
<label for="branding-files">{{
|
||||
$t("settings.brandingDirectoryPath")
|
||||
}}</label>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
v-model="settings.branding.files"
|
||||
id="branding-files"
|
||||
/>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<input class="button button--flat" type="submit" :value="$t('buttons.update')">
|
||||
<input
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:value="$t('buttons.update')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -58,17 +97,25 @@
|
||||
<div class="column">
|
||||
<form class="card" @submit.prevent="save">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.userDefaults') }}</h2>
|
||||
<h2>{{ $t("settings.userDefaults") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p class="small">{{ $t('settings.defaultUserDescription') }}</p>
|
||||
<p class="small">{{ $t("settings.defaultUserDescription") }}</p>
|
||||
|
||||
<user-form :isNew="false" :isDefault="true" :user.sync="settings.defaults" />
|
||||
<user-form
|
||||
:isNew="false"
|
||||
:isDefault="true"
|
||||
:user.sync="settings.defaults"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<input class="button button--flat" type="submit" :value="$t('buttons.update')">
|
||||
<input
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:value="$t('buttons.update')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -76,30 +123,46 @@
|
||||
<div class="column">
|
||||
<form v-if="isExecEnabled" class="card" @submit.prevent="save">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.commandRunner') }}</h2>
|
||||
<h2>{{ $t("settings.commandRunner") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<i18n path="settings.commandRunnerHelp" tag="p" class="small">
|
||||
<code>FILE</code>
|
||||
<code>SCOPE</code>
|
||||
<a class="link" target="_blank" href="https://filebrowser.org/configuration/command-runner">{{ $t('settings.documentation') }}</a>
|
||||
<a
|
||||
class="link"
|
||||
target="_blank"
|
||||
href="https://filebrowser.org/configuration/command-runner"
|
||||
>{{ $t("settings.documentation") }}</a
|
||||
>
|
||||
</i18n>
|
||||
|
||||
<div v-for="command in settings.commands" :key="command.name" class="collapsible">
|
||||
<input :id="command.name" type="checkbox">
|
||||
<div
|
||||
v-for="command in settings.commands"
|
||||
:key="command.name"
|
||||
class="collapsible"
|
||||
>
|
||||
<input :id="command.name" type="checkbox" />
|
||||
<label :for="command.name">
|
||||
<p>{{ capitalize(command.name) }}</p>
|
||||
<i class="material-icons">arrow_drop_down</i>
|
||||
</label>
|
||||
<div class="collapse">
|
||||
<textarea class="input input--block input--textarea" v-model.trim="command.value"></textarea>
|
||||
<textarea
|
||||
class="input input--block input--textarea"
|
||||
v-model.trim="command.value"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<input class="button button--flat" type="submit" :value="$t('buttons.update')">
|
||||
<input
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:value="$t('buttons.update')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -107,80 +170,84 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { settings as api } from '@/api'
|
||||
import UserForm from '@/components/settings/UserForm'
|
||||
import Rules from '@/components/settings/Rules'
|
||||
import Themes from '@/components/settings/Themes'
|
||||
import { enableExec } from '@/utils/constants'
|
||||
import { mapState } from "vuex";
|
||||
import { settings as api } from "@/api";
|
||||
import UserForm from "@/components/settings/UserForm";
|
||||
import Rules from "@/components/settings/Rules";
|
||||
import Themes from "@/components/settings/Themes";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
name: 'settings',
|
||||
name: "settings",
|
||||
components: {
|
||||
Themes,
|
||||
UserForm,
|
||||
Rules
|
||||
Rules,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
originalSettings: null,
|
||||
settings: null
|
||||
}
|
||||
settings: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'user' ]),
|
||||
isExecEnabled: () => enableExec
|
||||
...mapState(["user"]),
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
async created () {
|
||||
async created() {
|
||||
try {
|
||||
const original = await api.get()
|
||||
let settings = { ...original, commands: [] }
|
||||
const original = await api.get();
|
||||
let settings = { ...original, commands: [] };
|
||||
|
||||
for (const key in original.commands) {
|
||||
settings.commands.push({
|
||||
name: key,
|
||||
value: original.commands[key].join('\n')
|
||||
})
|
||||
value: original.commands[key].join("\n"),
|
||||
});
|
||||
}
|
||||
|
||||
settings.shell = settings.shell.join(' ')
|
||||
settings.shell = settings.shell.join(" ");
|
||||
|
||||
this.originalSettings = original
|
||||
this.settings = settings
|
||||
this.originalSettings = original;
|
||||
this.settings = settings;
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
capitalize (name, where = '_') {
|
||||
if (where === 'caps') where = /(?=[A-Z])/
|
||||
let splitted = name.split(where)
|
||||
name = ''
|
||||
capitalize(name, where = "_") {
|
||||
if (where === "caps") where = /(?=[A-Z])/;
|
||||
let splitted = name.split(where);
|
||||
name = "";
|
||||
|
||||
for (let i = 0; i < splitted.length; i++) {
|
||||
name += splitted[i].charAt(0).toUpperCase() + splitted[i].slice(1) + ' '
|
||||
name +=
|
||||
splitted[i].charAt(0).toUpperCase() + splitted[i].slice(1) + " ";
|
||||
}
|
||||
|
||||
return name.slice(0, -1)
|
||||
return name.slice(0, -1);
|
||||
},
|
||||
async save () {
|
||||
async save() {
|
||||
let settings = {
|
||||
...this.settings,
|
||||
shell: this.settings.shell.trim().split(' ').filter(s => s !== ''),
|
||||
commands: {}
|
||||
}
|
||||
shell: this.settings.shell
|
||||
.trim()
|
||||
.split(" ")
|
||||
.filter((s) => s !== ""),
|
||||
commands: {},
|
||||
};
|
||||
|
||||
for (const { name, value } of this.settings.commands) {
|
||||
settings.commands[name] = value.split('\n').filter(cmd => cmd !== '')
|
||||
settings.commands[name] = value.split("\n").filter((cmd) => cmd !== "");
|
||||
}
|
||||
|
||||
try {
|
||||
await api.update(settings)
|
||||
this.$showSuccess(this.$t('settings.settingsUpdated'))
|
||||
await api.update(settings);
|
||||
this.$showSuccess(this.$t("settings.settingsUpdated"));
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,18 +3,31 @@
|
||||
<div class="column">
|
||||
<form class="card" @submit="updateSettings">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.profileSettings') }}</h2>
|
||||
<h2>{{ $t("settings.profileSettings") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p><input type="checkbox" v-model="hideDotfiles"> {{ $t('settings.hideDotfiles') }}</p>
|
||||
<p><input type="checkbox" v-model="singleClick"> {{ $t('settings.singleClick') }}</p>
|
||||
<h3>{{ $t('settings.language') }}</h3>
|
||||
<languages class="input input--block" :locale.sync="locale"></languages>
|
||||
<p>
|
||||
<input type="checkbox" v-model="hideDotfiles" />
|
||||
{{ $t("settings.hideDotfiles") }}
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" v-model="singleClick" />
|
||||
{{ $t("settings.singleClick") }}
|
||||
</p>
|
||||
<h3>{{ $t("settings.language") }}</h3>
|
||||
<languages
|
||||
class="input input--block"
|
||||
:locale.sync="locale"
|
||||
></languages>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<input class="button button--flat" type="submit" :value="$t('buttons.update')">
|
||||
<input
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:value="$t('buttons.update')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -22,16 +35,32 @@
|
||||
<div class="column">
|
||||
<form class="card" v-if="!user.lockPassword" @submit="updatePassword">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.changePassword') }}</h2>
|
||||
<h2>{{ $t("settings.changePassword") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<input :class="passwordClass" type="password" :placeholder="$t('settings.newPassword')" v-model="password" name="password">
|
||||
<input :class="passwordClass" type="password" :placeholder="$t('settings.newPasswordConfirm')" v-model="passwordConf" name="password">
|
||||
<input
|
||||
:class="passwordClass"
|
||||
type="password"
|
||||
:placeholder="$t('settings.newPassword')"
|
||||
v-model="password"
|
||||
name="password"
|
||||
/>
|
||||
<input
|
||||
:class="passwordClass"
|
||||
type="password"
|
||||
:placeholder="$t('settings.newPasswordConfirm')"
|
||||
v-model="passwordConf"
|
||||
name="password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<input class="button button--flat" type="submit" :value="$t('buttons.update')">
|
||||
<input
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:value="$t('buttons.update')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -39,75 +68,80 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import { users as api } from '@/api'
|
||||
import Languages from '@/components/settings/Languages'
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { users as api } from "@/api";
|
||||
import Languages from "@/components/settings/Languages";
|
||||
|
||||
export default {
|
||||
name: 'settings',
|
||||
name: "settings",
|
||||
components: {
|
||||
Languages
|
||||
Languages,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
password: '',
|
||||
passwordConf: '',
|
||||
password: "",
|
||||
passwordConf: "",
|
||||
hideDotfiles: false,
|
||||
singleClick: false,
|
||||
locale: ''
|
||||
}
|
||||
locale: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'user' ]),
|
||||
passwordClass () {
|
||||
const baseClass = 'input input--block'
|
||||
...mapState(["user"]),
|
||||
passwordClass() {
|
||||
const baseClass = "input input--block";
|
||||
|
||||
if (this.password === '' && this.passwordConf === '') {
|
||||
return baseClass
|
||||
if (this.password === "" && this.passwordConf === "") {
|
||||
return baseClass;
|
||||
}
|
||||
|
||||
if (this.password === this.passwordConf) {
|
||||
return `${baseClass} input--green`
|
||||
return `${baseClass} input--green`;
|
||||
}
|
||||
|
||||
return `${baseClass} input--red`
|
||||
}
|
||||
return `${baseClass} input--red`;
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.locale = this.user.locale
|
||||
this.hideDotfiles = this.user.hideDotfiles
|
||||
this.singleClick = this.user.singleClick
|
||||
created() {
|
||||
this.locale = this.user.locale;
|
||||
this.hideDotfiles = this.user.hideDotfiles;
|
||||
this.singleClick = this.user.singleClick;
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([ 'updateUser' ]),
|
||||
async updatePassword (event) {
|
||||
event.preventDefault()
|
||||
...mapMutations(["updateUser"]),
|
||||
async updatePassword(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (this.password !== this.passwordConf || this.password === '') {
|
||||
return
|
||||
if (this.password !== this.passwordConf || this.password === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = { id: this.user.id, password: this.password }
|
||||
await api.update(data, ['password'])
|
||||
this.updateUser(data)
|
||||
this.$showSuccess(this.$t('settings.passwordUpdated'))
|
||||
const data = { id: this.user.id, password: this.password };
|
||||
await api.update(data, ["password"]);
|
||||
this.updateUser(data);
|
||||
this.$showSuccess(this.$t("settings.passwordUpdated"));
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
async updateSettings (event) {
|
||||
event.preventDefault()
|
||||
async updateSettings(event) {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
const data = { id: this.user.id, locale: this.locale, hideDotfiles: this.hideDotfiles, singleClick: this.singleClick }
|
||||
await api.update(data, ['locale', 'hideDotfiles', 'singleClick'])
|
||||
this.updateUser(data)
|
||||
this.$showSuccess(this.$t('settings.settingsUpdated'))
|
||||
const data = {
|
||||
id: this.user.id,
|
||||
locale: this.locale,
|
||||
hideDotfiles: this.hideDotfiles,
|
||||
singleClick: this.singleClick,
|
||||
};
|
||||
await api.update(data, ["locale", "hideDotfiles", "singleClick"]);
|
||||
this.updateUser(data);
|
||||
this.$showSuccess(this.$t("settings.settingsUpdated"));
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,37 +3,51 @@
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.shareManagement') }}</h2>
|
||||
<h2>{{ $t("settings.shareManagement") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content full">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ $t('settings.path') }}</th>
|
||||
<th>{{ $t('settings.shareDuration') }}</th>
|
||||
<th v-if="user.perm.admin">{{ $t('settings.username') }}</th>
|
||||
<th>{{ $t("settings.path") }}</th>
|
||||
<th>{{ $t("settings.shareDuration") }}</th>
|
||||
<th v-if="user.perm.admin">{{ $t("settings.username") }}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
<tr v-for="link in links" :key="link.hash">
|
||||
<td><a :href="buildLink(link.hash)" target="_blank">{{ link.path }}</a></td>
|
||||
<td>
|
||||
<template v-if="link.expire !== 0">{{ humanTime(link.expire) }}</template>
|
||||
<template v-else>{{ $t('permanent') }}</template>
|
||||
<a :href="buildLink(link.hash)" target="_blank">{{
|
||||
link.path
|
||||
}}</a>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="link.expire !== 0">{{
|
||||
humanTime(link.expire)
|
||||
}}</template>
|
||||
<template v-else>{{ $t("permanent") }}</template>
|
||||
</td>
|
||||
<td v-if="user.perm.admin">{{ link.username }}</td>
|
||||
<td class="small">
|
||||
<button class="action"
|
||||
@click="deleteLink($event, link)"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
|
||||
<button
|
||||
class="action"
|
||||
@click="deleteLink($event, link)"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="small">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -44,63 +58,67 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { share as api, users } from '@/api'
|
||||
import moment from 'moment'
|
||||
import {baseURL} from "@/utils/constants"
|
||||
import Clipboard from 'clipboard'
|
||||
import {mapState} from "vuex";
|
||||
import { share as api, users } from "@/api";
|
||||
import moment from "moment";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import Clipboard from "clipboard";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'shares',
|
||||
computed: mapState([ 'user' ]),
|
||||
name: "shares",
|
||||
computed: mapState(["user"]),
|
||||
data: function () {
|
||||
return {
|
||||
links: [],
|
||||
clip: null
|
||||
}
|
||||
clip: null,
|
||||
};
|
||||
},
|
||||
async created () {
|
||||
async created() {
|
||||
try {
|
||||
let links = await api.list()
|
||||
let links = await api.list();
|
||||
if (this.user.perm.admin) {
|
||||
let userMap = new Map()
|
||||
for (let user of await users.getAll()) userMap.set(user.id, user.username)
|
||||
for (let link of links) link.username = userMap.has(link.userID) ? userMap.get(link.userID) : ''
|
||||
let userMap = new Map();
|
||||
for (let user of await users.getAll())
|
||||
userMap.set(user.id, user.username);
|
||||
for (let link of links)
|
||||
link.username = userMap.has(link.userID)
|
||||
? userMap.get(link.userID)
|
||||
: "";
|
||||
}
|
||||
this.links = links
|
||||
this.links = links;
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.clip = new Clipboard('.copy-clipboard')
|
||||
this.clip.on('success', () => {
|
||||
this.$showSuccess(this.$t('success.linkCopied'))
|
||||
})
|
||||
this.clip = new Clipboard(".copy-clipboard");
|
||||
this.clip.on("success", () => {
|
||||
this.$showSuccess(this.$t("success.linkCopied"));
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.clip.destroy()
|
||||
beforeDestroy() {
|
||||
this.clip.destroy();
|
||||
},
|
||||
methods: {
|
||||
deleteLink: async function (event, link) {
|
||||
event.preventDefault()
|
||||
event.preventDefault();
|
||||
|
||||
this.$store.commit('showHover', {
|
||||
prompt: 'share-delete',
|
||||
this.$store.commit("showHover", {
|
||||
prompt: "share-delete",
|
||||
confirm: () => {
|
||||
this.$store.commit('closeHovers')
|
||||
this.$store.commit("closeHovers");
|
||||
|
||||
api.remove(link.hash)
|
||||
this.links = this.links.filter(item => item.hash !== link.hash)
|
||||
}
|
||||
})
|
||||
api.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
},
|
||||
});
|
||||
},
|
||||
humanTime (time) {
|
||||
return moment(time * 1000).fromNow()
|
||||
humanTime(time) {
|
||||
return moment(time * 1000).fromNow();
|
||||
},
|
||||
buildLink (hash) {
|
||||
return `${window.location.origin}${baseURL}/share/${hash}`
|
||||
}
|
||||
}
|
||||
}
|
||||
buildLink(hash) {
|
||||
return `${window.location.origin}${baseURL}/share/${hash}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<div class="column">
|
||||
<form v-if="loaded" @submit="save" class="card">
|
||||
<div class="card-title">
|
||||
<h2 v-if="user.id === 0">{{ $t('settings.newUser') }}</h2>
|
||||
<h2 v-else>{{ $t('settings.user') }} {{ user.username }}</h2>
|
||||
<h2 v-if="user.id === 0">{{ $t("settings.newUser") }}</h2>
|
||||
<h2 v-else>{{ $t("settings.user") }} {{ user.username }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
@@ -18,11 +18,15 @@
|
||||
type="button"
|
||||
class="button button--flat button--red"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
||||
:title="$t('buttons.delete')"
|
||||
>
|
||||
{{ $t("buttons.delete") }}
|
||||
</button>
|
||||
<input
|
||||
class="button button--flat"
|
||||
type="submit"
|
||||
:value="$t('buttons.save')">
|
||||
:value="$t('buttons.save')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -33,16 +37,17 @@
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="button button--flat button--grey"
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="closeHovers"
|
||||
v-focus
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">
|
||||
{{ $t('buttons.cancel') }}
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
{{ $t("buttons.cancel") }}
|
||||
</button>
|
||||
<button class="button button--flat"
|
||||
@click="deleteUser">
|
||||
{{ $t('buttons.delete') }}
|
||||
<button class="button button--flat" @click="deleteUser">
|
||||
{{ $t("buttons.delete") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,101 +55,103 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations } from 'vuex'
|
||||
import { users as api, settings } from '@/api'
|
||||
import UserForm from '@/components/settings/UserForm'
|
||||
import deepClone from 'lodash.clonedeep'
|
||||
import { mapMutations } from "vuex";
|
||||
import { users as api, settings } from "@/api";
|
||||
import UserForm from "@/components/settings/UserForm";
|
||||
import deepClone from "lodash.clonedeep";
|
||||
|
||||
export default {
|
||||
name: 'user',
|
||||
name: "user",
|
||||
components: {
|
||||
UserForm
|
||||
UserForm,
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
originalUser: null,
|
||||
user: {},
|
||||
loaded: false
|
||||
}
|
||||
loaded: false,
|
||||
};
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
created() {
|
||||
this.fetchData();
|
||||
},
|
||||
computed: {
|
||||
isNew () {
|
||||
return this.$route.path === '/settings/users/new'
|
||||
}
|
||||
isNew() {
|
||||
return this.$route.path === "/settings/users/new";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route': 'fetchData',
|
||||
'user.perm.admin': function () {
|
||||
if (!this.user.perm.admin) return
|
||||
this.user.lockPassword = false
|
||||
}
|
||||
$route: "fetchData",
|
||||
"user.perm.admin": function () {
|
||||
if (!this.user.perm.admin) return;
|
||||
this.user.lockPassword = false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([ 'closeHovers', 'showHover', 'setUser' ]),
|
||||
async fetchData () {
|
||||
...mapMutations(["closeHovers", "showHover", "setUser"]),
|
||||
async fetchData() {
|
||||
try {
|
||||
if (this.isNew) {
|
||||
let { defaults } = await settings.get()
|
||||
let { defaults } = await settings.get();
|
||||
this.user = {
|
||||
...defaults,
|
||||
username: '',
|
||||
passsword: '',
|
||||
username: "",
|
||||
passsword: "",
|
||||
rules: [],
|
||||
lockPassword: false,
|
||||
id: 0
|
||||
}
|
||||
id: 0,
|
||||
};
|
||||
} else {
|
||||
const id = this.$route.params.pathMatch
|
||||
this.user = { ...await api.get(id) }
|
||||
const id = this.$route.params.pathMatch;
|
||||
this.user = { ...(await api.get(id)) };
|
||||
}
|
||||
|
||||
this.loaded = true
|
||||
this.loaded = true;
|
||||
} catch (e) {
|
||||
this.$router.push({ path: '/settings/users/new' })
|
||||
this.$router.push({ path: "/settings/users/new" });
|
||||
}
|
||||
},
|
||||
deletePrompt () {
|
||||
this.showHover('deleteUser')
|
||||
deletePrompt() {
|
||||
this.showHover("deleteUser");
|
||||
},
|
||||
async deleteUser (event) {
|
||||
event.preventDefault()
|
||||
async deleteUser(event) {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
await api.remove(this.user.id)
|
||||
this.$router.push({ path: '/settings/users' })
|
||||
this.$showSuccess(this.$t('settings.userDeleted'))
|
||||
await api.remove(this.user.id);
|
||||
this.$router.push({ path: "/settings/users" });
|
||||
this.$showSuccess(this.$t("settings.userDeleted"));
|
||||
} catch (e) {
|
||||
(e.message === "403") ? this.$showError(this.$t("errors.forbidden"), false) : this.$showError(e)
|
||||
e.message === "403"
|
||||
? this.$showError(this.$t("errors.forbidden"), false)
|
||||
: this.$showError(e);
|
||||
}
|
||||
},
|
||||
async save (event) {
|
||||
event.preventDefault()
|
||||
async save(event) {
|
||||
event.preventDefault();
|
||||
let user = {
|
||||
...this.originalUser,
|
||||
...this.user
|
||||
}
|
||||
...this.user,
|
||||
};
|
||||
|
||||
try {
|
||||
if (this.isNew) {
|
||||
const loc = await api.create(user)
|
||||
this.$router.push({ path: loc })
|
||||
this.$showSuccess(this.$t('settings.userCreated'))
|
||||
const loc = await api.create(user);
|
||||
this.$router.push({ path: loc });
|
||||
this.$showSuccess(this.$t("settings.userCreated"));
|
||||
} else {
|
||||
await api.update(user)
|
||||
await api.update(user);
|
||||
|
||||
if (user.id === this.$store.state.user.id) {
|
||||
this.setUser({ ...deepClone(user) })
|
||||
this.setUser({ ...deepClone(user) });
|
||||
}
|
||||
|
||||
this.$showSuccess(this.$t('settings.userUpdated'))
|
||||
this.$showSuccess(this.$t("settings.userUpdated"));
|
||||
}
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,25 +3,34 @@
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('settings.users') }}</h2>
|
||||
<router-link to="/settings/users/new"><button class="button">{{ $t('buttons.new') }}</button></router-link>
|
||||
<h2>{{ $t("settings.users") }}</h2>
|
||||
<router-link to="/settings/users/new"
|
||||
><button class="button">
|
||||
{{ $t("buttons.new") }}
|
||||
</button></router-link
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="card-content full">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ $t('settings.username') }}</th>
|
||||
<th>{{ $t('settings.admin') }}</th>
|
||||
<th>{{ $t('settings.scope') }}</th>
|
||||
<th>{{ $t("settings.username") }}</th>
|
||||
<th>{{ $t("settings.admin") }}</th>
|
||||
<th>{{ $t("settings.scope") }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
<tr v-for="user in users" :key="user.id">
|
||||
<td>{{ user.username }}</td>
|
||||
<td><i v-if="user.perm.admin" class="material-icons">done</i><i v-else class="material-icons">close</i></td>
|
||||
<td>
|
||||
<i v-if="user.perm.admin" class="material-icons">done</i
|
||||
><i v-else class="material-icons">close</i>
|
||||
</td>
|
||||
<td>{{ user.scope }}</td>
|
||||
<td class="small">
|
||||
<router-link :to="'/settings/users/' + user.id"><i class="material-icons">mode_edit</i></router-link>
|
||||
<router-link :to="'/settings/users/' + user.id"
|
||||
><i class="material-icons">mode_edit</i></router-link
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -32,21 +41,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { users as api } from '@/api'
|
||||
import { users as api } from "@/api";
|
||||
|
||||
export default {
|
||||
name: 'users',
|
||||
name: "users",
|
||||
data: function () {
|
||||
return {
|
||||
users: []
|
||||
users: [],
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
this.users = await api.getAll();
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
try {
|
||||
this.users = await api.getAll()
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user