feat:Support compress image view
This commit is contained in:
parent
6d899a6335
commit
aadfce0e65
198
frontend/.eslintrc.js
Normal file
198
frontend/.eslintrc.js
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
extends: ['plugin:vue/recommended', 'eslint:recommended'],
|
||||||
|
|
||||||
|
// add your custom rules here
|
||||||
|
// it is base on https://github.com/vuejs/eslint-config-vue
|
||||||
|
rules: {
|
||||||
|
'vue/max-attributes-per-line': [2, {
|
||||||
|
'singleline': 10,
|
||||||
|
'multiline': {
|
||||||
|
'max': 1,
|
||||||
|
'allowFirstLine': false
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/multiline-html-element-content-newline': 'off',
|
||||||
|
'vue/name-property-casing': ['error', 'PascalCase'],
|
||||||
|
'vue/no-v-html': 'off',
|
||||||
|
'accessor-pairs': 2,
|
||||||
|
'arrow-spacing': [2, {
|
||||||
|
'before': true,
|
||||||
|
'after': true
|
||||||
|
}],
|
||||||
|
'block-spacing': [2, 'always'],
|
||||||
|
'brace-style': [2, '1tbs', {
|
||||||
|
'allowSingleLine': true
|
||||||
|
}],
|
||||||
|
'camelcase': [0, {
|
||||||
|
'properties': 'always'
|
||||||
|
}],
|
||||||
|
'comma-dangle': [2, 'never'],
|
||||||
|
'comma-spacing': [2, {
|
||||||
|
'before': false,
|
||||||
|
'after': true
|
||||||
|
}],
|
||||||
|
'comma-style': [2, 'last'],
|
||||||
|
'constructor-super': 2,
|
||||||
|
'curly': [2, 'multi-line'],
|
||||||
|
'dot-location': [2, 'property'],
|
||||||
|
'eol-last': 2,
|
||||||
|
'eqeqeq': ['error', 'always', { 'null': 'ignore' }],
|
||||||
|
'generator-star-spacing': [2, {
|
||||||
|
'before': true,
|
||||||
|
'after': true
|
||||||
|
}],
|
||||||
|
'handle-callback-err': [2, '^(err|error)$'],
|
||||||
|
'indent': [2, 2, {
|
||||||
|
'SwitchCase': 1
|
||||||
|
}],
|
||||||
|
'jsx-quotes': [2, 'prefer-single'],
|
||||||
|
'key-spacing': [2, {
|
||||||
|
'beforeColon': false,
|
||||||
|
'afterColon': true
|
||||||
|
}],
|
||||||
|
'keyword-spacing': [2, {
|
||||||
|
'before': true,
|
||||||
|
'after': true
|
||||||
|
}],
|
||||||
|
'new-cap': [2, {
|
||||||
|
'newIsCap': true,
|
||||||
|
'capIsNew': false
|
||||||
|
}],
|
||||||
|
'new-parens': 2,
|
||||||
|
'no-array-constructor': 2,
|
||||||
|
'no-caller': 2,
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-class-assign': 2,
|
||||||
|
'no-cond-assign': 2,
|
||||||
|
'no-const-assign': 2,
|
||||||
|
'no-control-regex': 0,
|
||||||
|
'no-delete-var': 2,
|
||||||
|
'no-dupe-args': 2,
|
||||||
|
'no-dupe-class-members': 2,
|
||||||
|
'no-dupe-keys': 2,
|
||||||
|
'no-duplicate-case': 2,
|
||||||
|
'no-empty-character-class': 2,
|
||||||
|
'no-empty-pattern': 2,
|
||||||
|
'no-eval': 2,
|
||||||
|
'no-ex-assign': 2,
|
||||||
|
'no-extend-native': 2,
|
||||||
|
'no-extra-bind': 2,
|
||||||
|
'no-extra-boolean-cast': 2,
|
||||||
|
'no-extra-parens': [2, 'functions'],
|
||||||
|
'no-fallthrough': 2,
|
||||||
|
'no-floating-decimal': 2,
|
||||||
|
'no-func-assign': 2,
|
||||||
|
'no-implied-eval': 2,
|
||||||
|
'no-inner-declarations': [2, 'functions'],
|
||||||
|
'no-invalid-regexp': 2,
|
||||||
|
'no-irregular-whitespace': 2,
|
||||||
|
'no-iterator': 2,
|
||||||
|
'no-label-var': 2,
|
||||||
|
'no-labels': [2, {
|
||||||
|
'allowLoop': false,
|
||||||
|
'allowSwitch': false
|
||||||
|
}],
|
||||||
|
'no-lone-blocks': 2,
|
||||||
|
'no-mixed-spaces-and-tabs': 2,
|
||||||
|
'no-multi-spaces': 2,
|
||||||
|
'no-multi-str': 2,
|
||||||
|
'no-multiple-empty-lines': [2, {
|
||||||
|
'max': 1
|
||||||
|
}],
|
||||||
|
'no-native-reassign': 2,
|
||||||
|
'no-negated-in-lhs': 2,
|
||||||
|
'no-new-object': 2,
|
||||||
|
'no-new-require': 2,
|
||||||
|
'no-new-symbol': 2,
|
||||||
|
'no-new-wrappers': 2,
|
||||||
|
'no-obj-calls': 2,
|
||||||
|
'no-octal': 2,
|
||||||
|
'no-octal-escape': 2,
|
||||||
|
'no-path-concat': 2,
|
||||||
|
'no-proto': 2,
|
||||||
|
'no-redeclare': 2,
|
||||||
|
'no-regex-spaces': 2,
|
||||||
|
'no-return-assign': [2, 'except-parens'],
|
||||||
|
'no-self-assign': 2,
|
||||||
|
'no-self-compare': 2,
|
||||||
|
'no-sequences': 2,
|
||||||
|
'no-shadow-restricted-names': 2,
|
||||||
|
'no-spaced-func': 2,
|
||||||
|
'no-sparse-arrays': 2,
|
||||||
|
'no-this-before-super': 2,
|
||||||
|
'no-throw-literal': 2,
|
||||||
|
'no-trailing-spaces': 2,
|
||||||
|
'no-undef': 2,
|
||||||
|
'no-undef-init': 2,
|
||||||
|
'no-unexpected-multiline': 2,
|
||||||
|
'no-unmodified-loop-condition': 2,
|
||||||
|
'no-unneeded-ternary': [2, {
|
||||||
|
'defaultAssignment': false
|
||||||
|
}],
|
||||||
|
'no-unreachable': 2,
|
||||||
|
'no-unsafe-finally': 2,
|
||||||
|
'no-unused-vars': [2, {
|
||||||
|
'vars': 'all',
|
||||||
|
'args': 'none'
|
||||||
|
}],
|
||||||
|
'no-useless-call': 2,
|
||||||
|
'no-useless-computed-key': 2,
|
||||||
|
'no-useless-constructor': 2,
|
||||||
|
'no-useless-escape': 0,
|
||||||
|
'no-whitespace-before-property': 2,
|
||||||
|
'no-with': 2,
|
||||||
|
'one-var': [2, {
|
||||||
|
'initialized': 'never'
|
||||||
|
}],
|
||||||
|
'operator-linebreak': [2, 'after', {
|
||||||
|
'overrides': {
|
||||||
|
'?': 'before',
|
||||||
|
':': 'before'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
'padded-blocks': [2, 'never'],
|
||||||
|
'quotes': [2, 'single', {
|
||||||
|
'avoidEscape': true,
|
||||||
|
'allowTemplateLiterals': true
|
||||||
|
}],
|
||||||
|
'semi': [2, 'never'],
|
||||||
|
'semi-spacing': [2, {
|
||||||
|
'before': false,
|
||||||
|
'after': true
|
||||||
|
}],
|
||||||
|
'space-before-blocks': [2, 'always'],
|
||||||
|
'space-before-function-paren': [2, 'never'],
|
||||||
|
'space-in-parens': [2, 'never'],
|
||||||
|
'space-infix-ops': 2,
|
||||||
|
'space-unary-ops': [2, {
|
||||||
|
'words': true,
|
||||||
|
'nonwords': false
|
||||||
|
}],
|
||||||
|
'spaced-comment': [2, 'always', {
|
||||||
|
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
|
||||||
|
}],
|
||||||
|
'template-curly-spacing': [2, 'never'],
|
||||||
|
'use-isnan': 2,
|
||||||
|
'valid-typeof': 2,
|
||||||
|
'wrap-iife': [2, 'any'],
|
||||||
|
'yield-star-spacing': [2, 'both'],
|
||||||
|
'yoda': [2, 'never'],
|
||||||
|
'prefer-const': 2,
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
|
'object-curly-spacing': [2, 'always', {
|
||||||
|
objectsInObjects: false
|
||||||
|
}],
|
||||||
|
'array-bracket-spacing': [2, 'never']
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<router-view></router-view>
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: 'App',
|
||||||
mounted () {
|
mounted() {
|
||||||
const loading = document.getElementById('loading')
|
const loading = document.getElementById('loading')
|
||||||
loading.classList.add('done')
|
loading.classList.add('done')
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
loading.parentNode.removeChild(loading)
|
loading.parentNode.removeChild(loading)
|
||||||
}, 200)
|
}, 200)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export default function command(url, command, onmessage, onclose) {
|
|||||||
url = removePrefix(url)
|
url = removePrefix(url)
|
||||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`
|
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`
|
||||||
|
|
||||||
let conn = new window.WebSocket(url)
|
const conn = new window.WebSocket(url)
|
||||||
conn.onopen = () => conn.send(command)
|
conn.onopen = () => conn.send(command)
|
||||||
conn.onmessage = onmessage
|
conn.onmessage = onmessage
|
||||||
conn.onclose = onclose
|
conn.onclose = onclose
|
||||||
|
|||||||
@ -2,13 +2,13 @@ import { fetchURL, removePrefix } from './utils'
|
|||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from '@/utils/constants'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
|
||||||
export async function fetch (url) {
|
export async function fetch(url) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url)
|
||||||
|
|
||||||
const res = await fetchURL(`/api/resources${url}`, {})
|
const res = await fetchURL(`/api/resources${url}`, {})
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
let data = await res.json()
|
const data = await res.json()
|
||||||
data.url = `/files${url}`
|
data.url = `/files${url}`
|
||||||
|
|
||||||
if (data.isDir) {
|
if (data.isDir) {
|
||||||
@ -31,10 +31,10 @@ export async function fetch (url) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resourceAction (url, method, content) {
|
async function resourceAction(url, method, content) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url)
|
||||||
|
|
||||||
let opts = { method }
|
const opts = { method }
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
opts.body = content
|
opts.body = content
|
||||||
@ -49,15 +49,15 @@ async function resourceAction (url, method, content) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove (url) {
|
export async function remove(url) {
|
||||||
return resourceAction(url, 'DELETE')
|
return resourceAction(url, 'DELETE')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function put (url, content = '') {
|
export async function put(url, content = '') {
|
||||||
return resourceAction(url, 'PUT', content)
|
return resourceAction(url, 'PUT', content)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function download (format, ...files) {
|
export function download(format, ...files) {
|
||||||
let url = `${baseURL}/api/raw`
|
let url = `${baseURL}/api/raw`
|
||||||
|
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
@ -65,7 +65,7 @@ export function download (format, ...files) {
|
|||||||
} else {
|
} else {
|
||||||
let arg = ''
|
let arg = ''
|
||||||
|
|
||||||
for (let file of files) {
|
for (const file of files) {
|
||||||
arg += removePrefix(file) + ','
|
arg += removePrefix(file) + ','
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,11 +82,11 @@ export function download (format, ...files) {
|
|||||||
window.open(url)
|
window.open(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function post (url, content = '', overwrite = false, onupload) {
|
export async function post(url, content = '', overwrite = false, onupload) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let request = new XMLHttpRequest()
|
const request = new XMLHttpRequest()
|
||||||
request.open('POST', `${baseURL}/api/resources${url}?override=${overwrite}`, true)
|
request.open('POST', `${baseURL}/api/resources${url}?override=${overwrite}`, true)
|
||||||
request.setRequestHeader('X-Auth', store.state.jwt)
|
request.setRequestHeader('X-Auth', store.state.jwt)
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ export async function post (url, content = '', overwrite = false, onupload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send a message to user before closing the tab during file upload
|
// Send a message to user before closing the tab during file upload
|
||||||
window.onbeforeunload = () => "Files are being uploaded."
|
window.onbeforeunload = () => 'Files are being uploaded.'
|
||||||
|
|
||||||
request.onload = () => {
|
request.onload = () => {
|
||||||
if (request.status === 200) {
|
if (request.status === 200) {
|
||||||
@ -116,10 +116,10 @@ export async function post (url, content = '', overwrite = false, onupload) {
|
|||||||
}).finally(() => { window.onbeforeunload = null })
|
}).finally(() => { window.onbeforeunload = null })
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveCopy (items, copy = false) {
|
function moveCopy(items, copy = false) {
|
||||||
let promises = []
|
const promises = []
|
||||||
|
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
const from = removePrefix(item.from)
|
const from = removePrefix(item.from)
|
||||||
const to = encodeURIComponent(removePrefix(item.to))
|
const to = encodeURIComponent(removePrefix(item.to))
|
||||||
const url = `${from}?action=${copy ? 'copy' : 'rename'}&destination=${to}`
|
const url = `${from}?action=${copy ? 'copy' : 'rename'}&destination=${to}`
|
||||||
@ -129,15 +129,15 @@ function moveCopy (items, copy = false) {
|
|||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function move (items) {
|
export function move(items) {
|
||||||
return moveCopy(items)
|
return moveCopy(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copy (items) {
|
export function copy(items) {
|
||||||
return moveCopy(items, true)
|
return moveCopy(items, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checksum (url, algo) {
|
export async function checksum(url, algo) {
|
||||||
const data = await resourceAction(`${url}?checksum=${algo}`, 'GET')
|
const data = await resourceAction(`${url}?checksum=${algo}`, 'GET')
|
||||||
return (await data.json()).checksums[algo]
|
return (await data.json()).checksums[algo]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { fetchJSON, removePrefix } from './utils'
|
import { fetchJSON, removePrefix } from './utils'
|
||||||
|
|
||||||
export default async function search (url, query) {
|
export default async function search(url, query) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url)
|
||||||
query = encodeURIComponent(query)
|
query = encodeURIComponent(query)
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { fetchURL, fetchJSON } from './utils'
|
import { fetchURL, fetchJSON } from './utils'
|
||||||
|
|
||||||
export function get () {
|
export function get() {
|
||||||
return fetchJSON(`/api/settings`, {})
|
return fetchJSON(`/api/settings`, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update (settings) {
|
export async function update(settings) {
|
||||||
const res = await fetchURL(`/api/settings`, {
|
const res = await fetchURL(`/api/settings`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify(settings)
|
body: JSON.stringify(settings)
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { fetchURL, fetchJSON } from './utils'
|
import { fetchURL, fetchJSON } from './utils'
|
||||||
|
|
||||||
export async function getAll () {
|
export async function getAll() {
|
||||||
return fetchJSON(`/api/users`, {})
|
return fetchJSON(`/api/users`, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get (id) {
|
export async function get(id) {
|
||||||
return fetchJSON(`/api/users/${id}`, {})
|
return fetchJSON(`/api/users/${id}`, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create (user) {
|
export async function create(user) {
|
||||||
const res = await fetchURL(`/api/users`, {
|
const res = await fetchURL(`/api/users`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -23,10 +23,9 @@ export async function create (user) {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(res.status)
|
throw new Error(res.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update (user, which = ['all']) {
|
export async function update(user, which = ['all']) {
|
||||||
const res = await fetchURL(`/api/users/${user.id}`, {
|
const res = await fetchURL(`/api/users/${user.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -41,7 +40,7 @@ export async function update (user, which = ['all']) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove (id) {
|
export async function remove(id) {
|
||||||
const res = await fetchURL(`/api/users/${id}`, {
|
const res = await fetchURL(`/api/users/${id}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import store from '@/store'
|
|||||||
import { renew } from '@/utils/auth'
|
import { renew } from '@/utils/auth'
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from '@/utils/constants'
|
||||||
|
|
||||||
export async function fetchURL (url, opts) {
|
export async function fetchURL(url, opts) {
|
||||||
opts = opts || {}
|
opts = opts || {}
|
||||||
opts.headers = opts.headers || {}
|
opts.headers = opts.headers || {}
|
||||||
|
|
||||||
let { headers, ...rest } = opts
|
const { headers, ...rest } = opts
|
||||||
|
|
||||||
const res = await fetch(`${baseURL}${url}`, {
|
const res = await fetch(`${baseURL}${url}`, {
|
||||||
headers: {
|
headers: {
|
||||||
@ -23,7 +23,7 @@ export async function fetchURL (url, opts) {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchJSON (url, opts) {
|
export async function fetchJSON(url, opts) {
|
||||||
const res = await fetchURL(url, opts)
|
const res = await fetchURL(url, opts)
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
@ -33,7 +33,7 @@ export async function fetchJSON (url, opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removePrefix (url) {
|
export function removePrefix(url) {
|
||||||
if (url.startsWith('/files')) {
|
if (url.startsWith('/files')) {
|
||||||
url = url.slice(6)
|
url = url.slice(6)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,60 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<header>
|
<header>
|
||||||
<div>
|
<div>
|
||||||
<button @click="openSidebar" :aria-label="$t('buttons.toggleSidebar')" :title="$t('buttons.toggleSidebar')" class="action">
|
<button :aria-label="$t('buttons.toggleSidebar')" :title="$t('buttons.toggleSidebar')" class="action" @click="openSidebar">
|
||||||
<i class="material-icons">menu</i>
|
<i class="material-icons">menu</i>
|
||||||
</button>
|
</button>
|
||||||
<img :src="logoURL" alt="File Browser">
|
<img :src="logoURL" alt="File Browser">
|
||||||
<search v-if="isLogged"></search>
|
<search v-if="isLogged" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<template v-if="isLogged">
|
<template v-if="isLogged">
|
||||||
<button @click="openSearch" :aria-label="$t('buttons.search')" :title="$t('buttons.search')" class="search-button action">
|
<button :aria-label="$t('buttons.search')" :title="$t('buttons.search')" class="search-button action" @click="openSearch">
|
||||||
<i class="material-icons">search</i>
|
<i class="material-icons">search</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button v-show="showSaveButton" :aria-label="$t('buttons.save')" :title="$t('buttons.save')" class="action" id="save-button">
|
<button v-show="showSaveButton" id="save-button" :aria-label="$t('buttons.save')" :title="$t('buttons.save')" class="action">
|
||||||
<i class="material-icons">save</i>
|
<i class="material-icons">save</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="openMore" id="more" :aria-label="$t('buttons.more')" :title="$t('buttons.more')" class="action">
|
<button id="more" :aria-label="$t('buttons.more')" :title="$t('buttons.more')" class="action" @click="openMore">
|
||||||
<i class="material-icons">more_vert</i>
|
<i class="material-icons">more_vert</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Menu that shows on listing AND mobile when there are files selected -->
|
<!-- Menu that shows on listing AND mobile when there are files selected -->
|
||||||
<div id="file-selection" v-if="isMobile && isListing">
|
<div v-if="isMobile && isListing" id="file-selection">
|
||||||
<span v-if="selectedCount > 0">{{ selectedCount }} selected</span>
|
<span v-if="selectedCount > 0">{{ selectedCount }} selected</span>
|
||||||
<share-button v-show="showShareButton"></share-button>
|
<share-button v-show="showShareButton" />
|
||||||
<rename-button v-show="showRenameButton"></rename-button>
|
<rename-button v-show="showRenameButton" />
|
||||||
<copy-button v-show="showCopyButton"></copy-button>
|
<copy-button v-show="showCopyButton" />
|
||||||
<move-button v-show="showMoveButton"></move-button>
|
<move-button v-show="showMoveButton" />
|
||||||
<delete-button v-show="showDeleteButton"></delete-button>
|
<delete-button v-show="showDeleteButton" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- This buttons are shown on a dropdown on mobile phones -->
|
<!-- This buttons are shown on a dropdown on mobile phones -->
|
||||||
<div id="dropdown" :class="{ active: showMore }">
|
<div id="dropdown" :class="{ active: showMore }">
|
||||||
<div v-if="!isListing || !isMobile">
|
<div v-if="!isListing || !isMobile">
|
||||||
<share-button v-show="showShareButton"></share-button>
|
<share-button v-show="showShareButton" />
|
||||||
<rename-button v-show="showRenameButton"></rename-button>
|
<rename-button v-show="showRenameButton" />
|
||||||
<copy-button v-show="showCopyButton"></copy-button>
|
<copy-button v-show="showCopyButton" />
|
||||||
<move-button v-show="showMoveButton"></move-button>
|
<move-button v-show="showMoveButton" />
|
||||||
<delete-button v-show="showDeleteButton"></delete-button>
|
<delete-button v-show="showDeleteButton" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<shell-button v-show="user.perm.execute" />
|
<shell-button v-show="user.perm.execute" />
|
||||||
<switch-button v-show="isListing"></switch-button>
|
<switch-button v-show="isListing" />
|
||||||
<download-button v-show="showDownloadButton"></download-button>
|
<download-button v-show="showDownloadButton" />
|
||||||
<upload-button v-show="showUpload"></upload-button>
|
<upload-button v-show="showUpload" />
|
||||||
<info-button v-show="isFiles"></info-button>
|
<info-button v-show="isFiles" />
|
||||||
|
|
||||||
<button v-show="isListing" @click="toggleMultipleSelection" :aria-label="$t('buttons.selectMultiple')" :title="$t('buttons.selectMultiple')" class="action" >
|
<button v-show="isListing" :aria-label="$t('buttons.selectMultiple')" :title="$t('buttons.selectMultiple')" class="action" @click="toggleMultipleSelection">
|
||||||
<i class="material-icons">check_circle</i>
|
<i class="material-icons">check_circle</i>
|
||||||
<span>{{ $t('buttons.select') }}</span>
|
<span>{{ $t('buttons.select') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
<div v-show="showOverlay" class="overlay" @click="resetPrompts" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
@ -71,13 +71,13 @@ import MoveButton from './buttons/Move'
|
|||||||
import CopyButton from './buttons/Copy'
|
import CopyButton from './buttons/Copy'
|
||||||
import ShareButton from './buttons/Share'
|
import ShareButton from './buttons/Share'
|
||||||
import ShellButton from './buttons/Shell'
|
import ShellButton from './buttons/Shell'
|
||||||
import {mapGetters, mapState} from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import { logoURL } from '@/utils/constants'
|
import { logoURL } from '@/utils/constants'
|
||||||
import * as api from '@/api'
|
import * as api from '@/api'
|
||||||
import buttons from '@/utils/buttons'
|
import buttons from '@/utils/buttons'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'header-layout',
|
name: 'HeaderLayout',
|
||||||
components: {
|
components: {
|
||||||
Search,
|
Search,
|
||||||
InfoButton,
|
InfoButton,
|
||||||
@ -91,7 +91,7 @@ export default {
|
|||||||
MoveButton,
|
MoveButton,
|
||||||
ShellButton
|
ShellButton
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
pluginData: {
|
pluginData: {
|
||||||
@ -102,7 +102,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created() {
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
this.width = window.innerWidth
|
this.width = window.innerWidth
|
||||||
})
|
})
|
||||||
@ -123,65 +123,65 @@ export default {
|
|||||||
'multiple'
|
'multiple'
|
||||||
]),
|
]),
|
||||||
logoURL: () => logoURL,
|
logoURL: () => logoURL,
|
||||||
isMobile () {
|
isMobile() {
|
||||||
return this.width <= 736
|
return this.width <= 736
|
||||||
},
|
},
|
||||||
showUpload () {
|
showUpload() {
|
||||||
return this.isListing && this.user.perm.create
|
return this.isListing && this.user.perm.create
|
||||||
},
|
},
|
||||||
showSaveButton () {
|
showSaveButton() {
|
||||||
return this.isEditor && this.user.perm.modify
|
return this.isEditor && this.user.perm.modify
|
||||||
},
|
},
|
||||||
showDownloadButton () {
|
showDownloadButton() {
|
||||||
return this.isFiles && this.user.perm.download
|
return this.isFiles && this.user.perm.download
|
||||||
},
|
},
|
||||||
showDeleteButton () {
|
showDeleteButton() {
|
||||||
return this.isFiles && (this.isListing
|
return this.isFiles && (this.isListing
|
||||||
? (this.selectedCount !== 0 && this.user.perm.delete)
|
? (this.selectedCount !== 0 && this.user.perm.delete)
|
||||||
: this.user.perm.delete)
|
: this.user.perm.delete)
|
||||||
},
|
},
|
||||||
showRenameButton () {
|
showRenameButton() {
|
||||||
return this.isFiles && (this.isListing
|
return this.isFiles && (this.isListing
|
||||||
? (this.selectedCount === 1 && this.user.perm.rename)
|
? (this.selectedCount === 1 && this.user.perm.rename)
|
||||||
: this.user.perm.rename)
|
: this.user.perm.rename)
|
||||||
},
|
},
|
||||||
showShareButton () {
|
showShareButton() {
|
||||||
return this.isFiles && (this.isListing
|
return this.isFiles && (this.isListing
|
||||||
? (this.selectedCount === 1 && this.user.perm.share)
|
? (this.selectedCount === 1 && this.user.perm.share)
|
||||||
: this.user.perm.share)
|
: this.user.perm.share)
|
||||||
},
|
},
|
||||||
showMoveButton () {
|
showMoveButton() {
|
||||||
return this.isFiles && (this.isListing
|
return this.isFiles && (this.isListing
|
||||||
? (this.selectedCount > 0 && this.user.perm.rename)
|
? (this.selectedCount > 0 && this.user.perm.rename)
|
||||||
: this.user.perm.rename)
|
: this.user.perm.rename)
|
||||||
},
|
},
|
||||||
showCopyButton () {
|
showCopyButton() {
|
||||||
return this.isFiles && (this.isListing
|
return this.isFiles && (this.isListing
|
||||||
? (this.selectedCount > 0 && this.user.perm.create)
|
? (this.selectedCount > 0 && this.user.perm.create)
|
||||||
: this.user.perm.create)
|
: this.user.perm.create)
|
||||||
},
|
},
|
||||||
showMore () {
|
showMore() {
|
||||||
return this.isFiles && this.$store.state.show === 'more'
|
return this.isFiles && this.$store.state.show === 'more'
|
||||||
},
|
},
|
||||||
showOverlay () {
|
showOverlay() {
|
||||||
return this.showMore
|
return this.showMore
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openSidebar () {
|
openSidebar() {
|
||||||
this.$store.commit('showHover', 'sidebar')
|
this.$store.commit('showHover', 'sidebar')
|
||||||
},
|
},
|
||||||
openMore () {
|
openMore() {
|
||||||
this.$store.commit('showHover', 'more')
|
this.$store.commit('showHover', 'more')
|
||||||
},
|
},
|
||||||
openSearch () {
|
openSearch() {
|
||||||
this.$store.commit('showHover', 'search')
|
this.$store.commit('showHover', 'search')
|
||||||
},
|
},
|
||||||
toggleMultipleSelection () {
|
toggleMultipleSelection() {
|
||||||
this.$store.commit('multiple', !this.multiple)
|
this.$store.commit('multiple', !this.multiple)
|
||||||
this.resetPrompts()
|
this.resetPrompts()
|
||||||
},
|
},
|
||||||
resetPrompts () {
|
resetPrompts() {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="search" @click="open" v-bind:class="{ active , ongoing }">
|
<div id="search" :class="{ active , ongoing }" @click="open">
|
||||||
<div id="input">
|
<div id="input">
|
||||||
<button
|
<button
|
||||||
v-if="active"
|
v-if="active"
|
||||||
class="action"
|
class="action"
|
||||||
@click="close"
|
|
||||||
:aria-label="$t('buttons.close')"
|
:aria-label="$t('buttons.close')"
|
||||||
:title="$t('buttons.close')"
|
:title="$t('buttons.close')"
|
||||||
|
@click="close"
|
||||||
>
|
>
|
||||||
<i class="material-icons">arrow_back</i>
|
<i class="material-icons">arrow_back</i>
|
||||||
</button>
|
</button>
|
||||||
<i v-else class="material-icons">search</i>
|
<i v-else class="material-icons">search</i>
|
||||||
<input
|
<input
|
||||||
type="text"
|
|
||||||
@keyup.exact="keyup"
|
|
||||||
@keyup.enter="submit"
|
|
||||||
ref="input"
|
ref="input"
|
||||||
:autofocus="active"
|
|
||||||
v-model.trim="value"
|
v-model.trim="value"
|
||||||
|
type="text"
|
||||||
|
:autofocus="active"
|
||||||
:aria-label="$t('search.search')"
|
:aria-label="$t('search.search')"
|
||||||
:placeholder="$t('search.search')"
|
:placeholder="$t('search.search')"
|
||||||
|
@keyup.exact="keyup"
|
||||||
|
@keyup.enter="submit"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -33,14 +33,14 @@
|
|||||||
<h3>{{ $t('search.types') }}</h3>
|
<h3>{{ $t('search.types') }}</h3>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
|
||||||
v-for="(v,k) in boxes"
|
v-for="(v,k) in boxes"
|
||||||
:key="k"
|
:key="k"
|
||||||
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
@click="init('type:'+k)"
|
|
||||||
:aria-label="$t('search.'+v.label)"
|
:aria-label="$t('search.'+v.label)"
|
||||||
|
@click="init('type:'+k)"
|
||||||
>
|
>
|
||||||
<i class="material-icons">{{v.icon}}</i>
|
<i class="material-icons">{{ v.icon }}</i>
|
||||||
<p>{{ $t('search.'+v.label) }}</p>
|
<p>{{ $t('search.'+v.label) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<ul v-show="results.length > 0">
|
<ul v-show="results.length > 0">
|
||||||
<li v-for="(s,k) in filteredResults" :key="k">
|
<li v-for="(s,k) in filteredResults" :key="k">
|
||||||
<router-link @click.native="close" :to="'./' + s.path">
|
<router-link :to="'./' + s.path" @click.native="close">
|
||||||
<i v-if="s.dir" class="material-icons">folder</i>
|
<i v-if="s.dir" class="material-icons">folder</i>
|
||||||
<i v-else class="material-icons">insert_drive_file</i>
|
<i v-else class="material-icons">insert_drive_file</i>
|
||||||
<span>./{{ s.path }}</span>
|
<span>./{{ s.path }}</span>
|
||||||
@ -65,22 +65,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters, mapMutations } from "vuex"
|
import { mapState, mapGetters, mapMutations } from 'vuex'
|
||||||
import url from "@/utils/url"
|
import url from '@/utils/url'
|
||||||
import { search } from "@/api"
|
import { search } from '@/api'
|
||||||
|
|
||||||
var boxes = {
|
var boxes = {
|
||||||
image: { label: "images", icon: "insert_photo" },
|
image: { label: 'images', icon: 'insert_photo' },
|
||||||
audio: { label: "music", icon: "volume_up" },
|
audio: { label: 'music', icon: 'volume_up' },
|
||||||
video: { label: "video", icon: "movie" },
|
video: { label: 'video', icon: 'movie' },
|
||||||
pdf: { label: "pdf", icon: "picture_as_pdf" }
|
pdf: { label: 'pdf', icon: 'picture_as_pdf' }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "search",
|
name: 'Search',
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
value: "",
|
value: '',
|
||||||
active: false,
|
active: false,
|
||||||
ongoing: false,
|
ongoing: false,
|
||||||
results: [],
|
results: [],
|
||||||
@ -90,15 +90,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
show (val, old) {
|
show(val, old) {
|
||||||
this.active = val === "search"
|
this.active = val === 'search'
|
||||||
|
|
||||||
if (old === "search" && !this.active) {
|
if (old === 'search' && !this.active) {
|
||||||
if (this.reload) {
|
if (this.reload) {
|
||||||
this.setReload(true)
|
this.setReload(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.style.overflow = "auto"
|
document.body.style.overflow = 'auto'
|
||||||
this.reset()
|
this.reset()
|
||||||
this.value = ''
|
this.value = ''
|
||||||
this.active = false
|
this.active = false
|
||||||
@ -106,18 +106,18 @@ export default {
|
|||||||
} else if (this.active) {
|
} else if (this.active) {
|
||||||
this.reload = false
|
this.reload = false
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus()
|
||||||
document.body.style.overflow = "hidden"
|
document.body.style.overflow = 'hidden'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
value () {
|
value() {
|
||||||
if (this.results.length) {
|
if (this.results.length) {
|
||||||
this.reset()
|
this.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["user", "show"]),
|
...mapState(['user', 'show']),
|
||||||
...mapGetters(["isListing"]),
|
...mapGetters(['isListing']),
|
||||||
boxes() {
|
boxes() {
|
||||||
return boxes
|
return boxes
|
||||||
},
|
},
|
||||||
@ -126,17 +126,17 @@ export default {
|
|||||||
},
|
},
|
||||||
text() {
|
text() {
|
||||||
if (this.ongoing) {
|
if (this.ongoing) {
|
||||||
return ""
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.value === '' ? this.$t("search.typeToSearch") : this.$t("search.pressToSearch")
|
return this.value === '' ? this.$t('search.typeToSearch') : this.$t('search.pressToSearch')
|
||||||
},
|
},
|
||||||
filteredResults () {
|
filteredResults() {
|
||||||
return this.results.slice(0, this.resultsCount)
|
return this.results.slice(0, this.resultsCount)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
window.addEventListener("keydown", event => {
|
window.addEventListener('keydown', event => {
|
||||||
if (event.keyCode === 27) {
|
if (event.keyCode === 27) {
|
||||||
this.closeHovers()
|
this.closeHovers()
|
||||||
}
|
}
|
||||||
@ -149,9 +149,9 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(["showHover", "closeHovers", "setReload"]),
|
...mapMutations(['showHover', 'closeHovers', 'setReload']),
|
||||||
open() {
|
open() {
|
||||||
this.showHover("search")
|
this.showHover('search')
|
||||||
},
|
},
|
||||||
close(event) {
|
close(event) {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@ -166,11 +166,11 @@ export default {
|
|||||||
|
|
||||||
this.results.length = 0
|
this.results.length = 0
|
||||||
},
|
},
|
||||||
init (string) {
|
init(string) {
|
||||||
this.value = `${string} `
|
this.value = `${string} `
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus()
|
||||||
},
|
},
|
||||||
reset () {
|
reset() {
|
||||||
this.ongoing = false
|
this.ongoing = false
|
||||||
this.resultsCount = 50
|
this.resultsCount = 50
|
||||||
this.results = []
|
this.results = []
|
||||||
@ -184,12 +184,11 @@ export default {
|
|||||||
|
|
||||||
let path = this.$route.path
|
let path = this.$route.path
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
path = url.removeLastDir(path) + "/"
|
path = url.removeLastDir(path) + '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ongoing = true
|
this.ongoing = true
|
||||||
|
|
||||||
|
|
||||||
this.results = await search(path, this.value)
|
this.results = await search(path, this.value)
|
||||||
this.ongoing = false
|
this.ongoing = false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div @click="focus" class="shell" ref="scrollable" :class="{ ['shell--hidden']: !showShell}">
|
<div ref="scrollable" class="shell" :class="{ ['shell--hidden']: !showShell}" @click="focus">
|
||||||
<div v-for="(c, index) in content" :key="index" class="shell__result" >
|
<div v-for="(c, index) in content" :key="index" class="shell__result">
|
||||||
<div class="shell__prompt"><i class="material-icons">chevron_right</i></div>
|
<div class="shell__prompt"><i class="material-icons">chevron_right</i></div>
|
||||||
<pre class="shell__text">{{ c.text }}</pre>
|
<pre class="shell__text">{{ c.text }}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shell__result" :class="{ 'shell__result--hidden': !canInput }" >
|
<div class="shell__result" :class="{ 'shell__result--hidden': !canInput }">
|
||||||
<div class="shell__prompt"><i class="material-icons">chevron_right</i></div>
|
<div class="shell__prompt"><i class="material-icons">chevron_right</i></div>
|
||||||
<pre
|
<pre
|
||||||
tabindex="0"
|
|
||||||
ref="input"
|
ref="input"
|
||||||
|
tabindex="0"
|
||||||
class="shell__text"
|
class="shell__text"
|
||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
@keydown.prevent.38="historyUp"
|
@keydown.prevent.38="historyUp"
|
||||||
@keydown.prevent.40="historyDown"
|
@keydown.prevent.40="historyDown"
|
||||||
@keypress.prevent.enter="submit" />
|
@keypress.prevent.enter="submit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -24,11 +25,11 @@ import { mapMutations, mapState, mapGetters } from 'vuex'
|
|||||||
import { commands } from '@/api'
|
import { commands } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shell',
|
name: 'Shell',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user', 'showShell' ]),
|
...mapState(['user', 'showShell']),
|
||||||
...mapGetters([ 'isFiles', 'isLogged' ]),
|
...mapGetters(['isFiles', 'isLogged']),
|
||||||
path: function () {
|
path: function() {
|
||||||
if (this.isFiles) {
|
if (this.isFiles) {
|
||||||
return this.$route.path
|
return this.$route.path
|
||||||
}
|
}
|
||||||
@ -43,20 +44,20 @@ export default {
|
|||||||
canInput: true
|
canInput: true
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'toggleShell' ]),
|
...mapMutations(['toggleShell']),
|
||||||
scroll: function () {
|
scroll: function() {
|
||||||
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight
|
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight
|
||||||
},
|
},
|
||||||
focus: function () {
|
focus: function() {
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus()
|
||||||
},
|
},
|
||||||
historyUp () {
|
historyUp() {
|
||||||
if (this.historyPos > 0) {
|
if (this.historyPos > 0) {
|
||||||
this.$refs.input.innerText = this.history[--this.historyPos]
|
this.$refs.input.innerText = this.history[--this.historyPos]
|
||||||
this.focus()
|
this.focus()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
historyDown () {
|
historyDown() {
|
||||||
if (this.historyPos >= 0 && this.historyPos < this.history.length - 1) {
|
if (this.historyPos >= 0 && this.historyPos < this.history.length - 1) {
|
||||||
this.$refs.input.innerText = this.history[++this.historyPos]
|
this.$refs.input.innerText = this.history[++this.historyPos]
|
||||||
this.focus()
|
this.focus()
|
||||||
@ -65,7 +66,7 @@ export default {
|
|||||||
this.$refs.input.innerText = ''
|
this.$refs.input.innerText = ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
submit: function (event) {
|
submit: function(event) {
|
||||||
const cmd = event.target.innerText.trim()
|
const cmd = event.target.innerText.trim()
|
||||||
|
|
||||||
if (cmd === '') {
|
if (cmd === '') {
|
||||||
@ -87,7 +88,7 @@ export default {
|
|||||||
this.canInput = false
|
this.canInput = false
|
||||||
event.target.innerHTML = ''
|
event.target.innerHTML = ''
|
||||||
|
|
||||||
let results = {
|
const results = {
|
||||||
text: `${cmd}\n\n`
|
text: `${cmd}\n\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,12 +7,12 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<div v-if="user.perm.create">
|
<div v-if="user.perm.create">
|
||||||
<button @click="$store.commit('showHover', 'newDir')" class="action" :aria-label="$t('sidebar.newFolder')" :title="$t('sidebar.newFolder')">
|
<button class="action" :aria-label="$t('sidebar.newFolder')" :title="$t('sidebar.newFolder')" @click="$store.commit('showHover', 'newDir')">
|
||||||
<i class="material-icons">create_new_folder</i>
|
<i class="material-icons">create_new_folder</i>
|
||||||
<span>{{ $t('sidebar.newFolder') }}</span>
|
<span>{{ $t('sidebar.newFolder') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="$store.commit('showHover', 'newFile')" class="action" :aria-label="$t('sidebar.newFile')" :title="$t('sidebar.newFile')">
|
<button class="action" :aria-label="$t('sidebar.newFile')" :title="$t('sidebar.newFile')" @click="$store.commit('showHover', 'newFile')">
|
||||||
<i class="material-icons">note_add</i>
|
<i class="material-icons">note_add</i>
|
||||||
<span>{{ $t('sidebar.newFile') }}</span>
|
<span>{{ $t('sidebar.newFile') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
<span>{{ $t('sidebar.settings') }}</span>
|
<span>{{ $t('sidebar.settings') }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<button v-if="authMethod == 'json'" @click="logout" class="action" id="logout" :aria-label="$t('sidebar.logout')" :title="$t('sidebar.logout')">
|
<button v-if="authMethod == 'json'" id="logout" class="action" :aria-label="$t('sidebar.logout')" :title="$t('sidebar.logout')" @click="logout">
|
||||||
<i class="material-icons">exit_to_app</i>
|
<i class="material-icons">exit_to_app</i>
|
||||||
<span>{{ $t('sidebar.logout') }}</span>
|
<span>{{ $t('sidebar.logout') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -59,11 +59,11 @@ import * as auth from '@/utils/auth'
|
|||||||
import { version, signup, disableExternal, noAuth, authMethod } from '@/utils/constants'
|
import { version, signup, disableExternal, noAuth, authMethod } from '@/utils/constants'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sidebar',
|
name: 'Sidebar',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user' ]),
|
...mapState(['user']),
|
||||||
...mapGetters([ 'isLogged' ]),
|
...mapGetters(['isLogged']),
|
||||||
active () {
|
active() {
|
||||||
return this.$store.state.show === 'sidebar'
|
return this.$store.state.show === 'sidebar'
|
||||||
},
|
},
|
||||||
signup: () => signup,
|
signup: () => signup,
|
||||||
@ -73,7 +73,7 @@ export default {
|
|||||||
authMethod: () => authMethod
|
authMethod: () => authMethod
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
help () {
|
help() {
|
||||||
this.$store.commit('showHover', 'help')
|
this.$store.commit('showHover', 'help')
|
||||||
},
|
},
|
||||||
logout: auth.logout
|
logout: auth.logout
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="show" :aria-label="$t('buttons.copy')" :title="$t('buttons.copy')" class="action" id="copy-button">
|
<button id="copy-button" :aria-label="$t('buttons.copy')" :title="$t('buttons.copy')" class="action" @click="show">
|
||||||
<i class="material-icons">content_copy</i>
|
<i class="material-icons">content_copy</i>
|
||||||
<span>{{ $t('buttons.copyFile') }}</span>
|
<span>{{ $t('buttons.copyFile') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'copy-button',
|
name: 'CopyButton',
|
||||||
methods: {
|
methods: {
|
||||||
show: function () {
|
show: function() {
|
||||||
this.$store.commit('showHover', 'copy')
|
this.$store.commit('showHover', 'copy')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="show" :aria-label="$t('buttons.delete')" :title="$t('buttons.delete')" class="action" id="delete-button">
|
<button id="delete-button" :aria-label="$t('buttons.delete')" :title="$t('buttons.delete')" class="action" @click="show">
|
||||||
<i class="material-icons">delete</i>
|
<i class="material-icons">delete</i>
|
||||||
<span>{{ $t('buttons.delete') }}</span>
|
<span>{{ $t('buttons.delete') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'delete-button',
|
name: 'DeleteButton',
|
||||||
methods: {
|
methods: {
|
||||||
show: function () {
|
show: function() {
|
||||||
this.$store.commit('showHover', 'delete')
|
this.$store.commit('showHover', 'delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="download" :aria-label="$t('buttons.download')" :title="$t('buttons.download')" id="download-button" class="action">
|
<button id="download-button" :aria-label="$t('buttons.download')" :title="$t('buttons.download')" class="action" @click="download">
|
||||||
<i class="material-icons">file_download</i>
|
<i class="material-icons">file_download</i>
|
||||||
<span>{{ $t('buttons.download') }}</span>
|
<span>{{ $t('buttons.download') }}</span>
|
||||||
<span v-if="selectedCount > 0" class="counter">{{ selectedCount }}</span>
|
<span v-if="selectedCount > 0" class="counter">{{ selectedCount }}</span>
|
||||||
@ -7,17 +7,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters, mapState} from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'download-button',
|
name: 'DownloadButton',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'selected']),
|
...mapState(['req', 'selected']),
|
||||||
...mapGetters(['isListing', 'selectedCount'])
|
...mapGetters(['isListing', 'selectedCount'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
download: function () {
|
download: function() {
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
api.download(null, this.$route.path)
|
api.download(null, this.$route.path)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'info-button',
|
name: 'InfoButton',
|
||||||
methods: {
|
methods: {
|
||||||
show: function () {
|
show: function() {
|
||||||
this.$store.commit('showHover', 'info')
|
this.$store.commit('showHover', 'info')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="show" :aria-label="$t('buttons.move')" :title="$t('buttons.move')" class="action" id="move-button">
|
<button id="move-button" :aria-label="$t('buttons.move')" :title="$t('buttons.move')" class="action" @click="show">
|
||||||
<i class="material-icons">forward</i>
|
<i class="material-icons">forward</i>
|
||||||
<span>{{ $t('buttons.moveFile') }}</span>
|
<span>{{ $t('buttons.moveFile') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'move-button',
|
name: 'MoveButton',
|
||||||
methods: {
|
methods: {
|
||||||
show: function () {
|
show: function() {
|
||||||
this.$store.commit('showHover', 'move')
|
this.$store.commit('showHover', 'move')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="show" :aria-label="$t('buttons.rename')" :title="$t('buttons.rename')" class="action" id="rename-button">
|
<button id="rename-button" :aria-label="$t('buttons.rename')" :title="$t('buttons.rename')" class="action" @click="show">
|
||||||
<i class="material-icons">mode_edit</i>
|
<i class="material-icons">mode_edit</i>
|
||||||
<span>{{ $t('buttons.rename') }}</span>
|
<span>{{ $t('buttons.rename') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'rename-button',
|
name: 'RenameButton',
|
||||||
methods: {
|
methods: {
|
||||||
show: function () {
|
show: function() {
|
||||||
this.$store.commit('showHover', 'rename')
|
this.$store.commit('showHover', 'rename')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="show" :aria-label="$t('buttons.share')" :title="$t('buttons.share')" class="action">
|
<button :aria-label="$t('buttons.share')" :title="$t('buttons.share')" class="action" @click="show">
|
||||||
<i class="material-icons">share</i>
|
<i class="material-icons">share</i>
|
||||||
<span>{{ $t('buttons.share') }}</span>
|
<span>{{ $t('buttons.share') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'share-button',
|
name: 'ShareButton',
|
||||||
methods: {
|
methods: {
|
||||||
show () {
|
show() {
|
||||||
this.$store.commit('showHover', 'share')
|
this.$store.commit('showHover', 'share')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="show" :aria-label="$t('buttons.shell')" :title="$t('buttons.shell')" class="action">
|
<button :aria-label="$t('buttons.shell')" :title="$t('buttons.shell')" class="action" @click="show">
|
||||||
<i class="material-icons">code</i>
|
<i class="material-icons">code</i>
|
||||||
<span>{{ $t('buttons.shell') }}</span>
|
<span>{{ $t('buttons.shell') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'shell-button',
|
name: 'ShellButton',
|
||||||
methods: {
|
methods: {
|
||||||
show: function () {
|
show: function() {
|
||||||
this.$store.commit('toggleShell')
|
this.$store.commit('toggleShell')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="change" :aria-label="$t('buttons.switchView')" :title="$t('buttons.switchView')" class="action" id="switch-view-button">
|
<button id="switch-view-button" :aria-label="$t('buttons.switchView')" :title="$t('buttons.switchView')" class="action" @click="change">
|
||||||
<i class="material-icons">{{ icon }}</i>
|
<i class="material-icons">{{ icon }}</i>
|
||||||
<span>{{ $t('buttons.switchView') }}</span>
|
<span>{{ $t('buttons.switchView') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -10,17 +10,17 @@ import { mapState, mapMutations } from 'vuex'
|
|||||||
import { users as api } from '@/api'
|
import { users as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'switch-button',
|
name: 'SwitchButton',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['user']),
|
...mapState(['user']),
|
||||||
icon: function () {
|
icon: function() {
|
||||||
if (this.user.viewMode === 'mosaic') return 'view_list'
|
if (this.user.viewMode === 'mosaic') return 'view_list'
|
||||||
return 'view_module'
|
return 'view_module'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'updateUser', 'closeHovers' ]),
|
...mapMutations(['updateUser', 'closeHovers']),
|
||||||
change: async function () {
|
change: async function() {
|
||||||
this.closeHovers()
|
this.closeHovers()
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="upload" :aria-label="$t('buttons.upload')" :title="$t('buttons.upload')" class="action" id="upload-button">
|
<button id="upload-button" :aria-label="$t('buttons.upload')" :title="$t('buttons.upload')" class="action" @click="upload">
|
||||||
<i class="material-icons">file_upload</i>
|
<i class="material-icons">file_upload</i>
|
||||||
<span>{{ $t('buttons.upload') }}</span>
|
<span>{{ $t('buttons.upload') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'upload-button',
|
name: 'UploadButton',
|
||||||
methods: {
|
methods: {
|
||||||
upload: function () {
|
upload: function() {
|
||||||
document.getElementById('upload-input').click()
|
document.getElementById('upload-input').click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<form id="editor"></form>
|
<form id="editor" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -13,24 +13,24 @@ import 'ace-builds/webpack-resolver'
|
|||||||
import { theme } from '@/utils/constants'
|
import { theme } from '@/utils/constants'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'editor',
|
name: 'Editor',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req'])
|
...mapState(['req'])
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
created () {
|
created() {
|
||||||
window.addEventListener('keydown', this.keyEvent)
|
window.addEventListener('keydown', this.keyEvent)
|
||||||
document.getElementById('save-button').addEventListener('click', this.save)
|
document.getElementById('save-button').addEventListener('click', this.save)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
window.removeEventListener('keydown', this.keyEvent)
|
window.removeEventListener('keydown', this.keyEvent)
|
||||||
document.getElementById('save-button').removeEventListener('click', this.save)
|
document.getElementById('save-button').removeEventListener('click', this.save)
|
||||||
this.editor.destroy();
|
this.editor.destroy()
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function() {
|
||||||
const fileContent = this.req.content || '';
|
const fileContent = this.req.content || ''
|
||||||
|
|
||||||
this.editor = ace.edit('editor', {
|
this.editor = ace.edit('editor', {
|
||||||
maxLines: Infinity,
|
maxLines: Infinity,
|
||||||
@ -43,12 +43,12 @@ export default {
|
|||||||
wrap: true
|
wrap: true
|
||||||
})
|
})
|
||||||
|
|
||||||
if (theme == 'dark') {
|
if (theme === 'dark') {
|
||||||
this.editor.setTheme("ace/theme/twilight");
|
this.editor.setTheme('ace/theme/twilight')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
keyEvent (event) {
|
keyEvent(event) {
|
||||||
if (!event.ctrlKey && !event.metaKey) {
|
if (!event.ctrlKey && !event.metaKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ export default {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.save()
|
this.save()
|
||||||
},
|
},
|
||||||
async save () {
|
async save() {
|
||||||
const button = 'save'
|
const button = 'save'
|
||||||
buttons.loading('save')
|
buttons.loading('save')
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="image-ex-container"
|
|
||||||
ref="container"
|
ref="container"
|
||||||
|
class="image-ex-container"
|
||||||
@touchstart="touchStart"
|
@touchstart="touchStart"
|
||||||
@touchmove="touchMove"
|
@touchmove="touchMove"
|
||||||
@dblclick="zoomAuto"
|
@dblclick="zoomAuto"
|
||||||
@ -10,7 +10,7 @@
|
|||||||
@mouseup="mouseUp"
|
@mouseup="mouseUp"
|
||||||
@wheel="wheelMove"
|
@wheel="wheelMove"
|
||||||
>
|
>
|
||||||
<img :src="src" class="image-ex-img" ref="imgex" @load="setCenter">
|
<img ref="imgex" :src="src" class="image-ex-img" @load="setCenter">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -54,20 +54,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
let container = this.$refs.container
|
const container = this.$refs.container
|
||||||
this.classList.forEach(className => container.classList.add(className))
|
this.classList.forEach(className => container.classList.add(className))
|
||||||
// set width and height if they are zero
|
// set width and height if they are zero
|
||||||
if (getComputedStyle(container).width === "0px") {
|
if (getComputedStyle(container).width === '0px') {
|
||||||
container.style.width = "100%"
|
container.style.width = '100%'
|
||||||
}
|
}
|
||||||
if (getComputedStyle(container).height === "0px") {
|
if (getComputedStyle(container).height === '0px') {
|
||||||
container.style.height = "100%"
|
container.style.height = '100%'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setCenter() {
|
setCenter() {
|
||||||
let container = this.$refs.container
|
const container = this.$refs.container
|
||||||
let img = this.$refs.imgex
|
const img = this.$refs.imgex
|
||||||
|
|
||||||
let rate = Math.min(
|
let rate = Math.min(
|
||||||
container.clientWidth / img.clientWidth,
|
container.clientWidth / img.clientWidth,
|
||||||
@ -80,7 +80,7 @@ export default {
|
|||||||
img.width = Math.floor(img.clientWidth * rate)
|
img.width = Math.floor(img.clientWidth * rate)
|
||||||
img.style.top = `${Math.floor((container.clientHeight - img.clientHeight) / 2)}px`
|
img.style.top = `${Math.floor((container.clientHeight - img.clientHeight) / 2)}px`
|
||||||
img.style.left = `${Math.floor((container.clientWidth - img.clientWidth) / 2)}px`
|
img.style.left = `${Math.floor((container.clientWidth - img.clientWidth) / 2)}px`
|
||||||
document.addEventListener('mouseup', () => this.inDrag = false )
|
document.addEventListener('mouseup', () => { this.inDrag = false })
|
||||||
},
|
},
|
||||||
mousedownStart(event) {
|
mousedownStart(event) {
|
||||||
this.lastX = null
|
this.lastX = null
|
||||||
@ -126,7 +126,7 @@ export default {
|
|||||||
this.lastY = event.targetTouches[0].pageY
|
this.lastY = event.targetTouches[0].pageY
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let step = this.$refs.imgex.width / 5
|
const step = this.$refs.imgex.width / 5
|
||||||
if (event.targetTouches.length === 2) {
|
if (event.targetTouches.length === 2) {
|
||||||
this.moveDisabled = true
|
this.moveDisabled = true
|
||||||
clearTimeout(this.disabledTimer)
|
clearTimeout(this.disabledTimer)
|
||||||
@ -135,9 +135,9 @@ export default {
|
|||||||
this.moveDisabledTime
|
this.moveDisabledTime
|
||||||
)
|
)
|
||||||
|
|
||||||
let p1 = event.targetTouches[0]
|
const p1 = event.targetTouches[0]
|
||||||
let p2 = event.targetTouches[1]
|
const p2 = event.targetTouches[1]
|
||||||
let touchDistance = Math.sqrt(
|
const touchDistance = Math.sqrt(
|
||||||
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
|
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
|
||||||
)
|
)
|
||||||
if (!this.lastTouchDistance) {
|
if (!this.lastTouchDistance) {
|
||||||
@ -149,8 +149,8 @@ export default {
|
|||||||
this.setZoom()
|
this.setZoom()
|
||||||
} else if (event.targetTouches.length === 1) {
|
} else if (event.targetTouches.length === 1) {
|
||||||
if (this.moveDisabled) return
|
if (this.moveDisabled) return
|
||||||
let x = event.targetTouches[0].pageX - this.lastX
|
const x = event.targetTouches[0].pageX - this.lastX
|
||||||
let y = event.targetTouches[0].pageY - this.lastY
|
const y = event.targetTouches[0].pageY - this.lastY
|
||||||
if (Math.abs(x) >= step && Math.abs(y) >= step) return
|
if (Math.abs(x) >= step && Math.abs(y) >= step) return
|
||||||
this.lastX = event.targetTouches[0].pageX
|
this.lastX = event.targetTouches[0].pageX
|
||||||
this.lastY = event.targetTouches[0].pageY
|
this.lastY = event.targetTouches[0].pageY
|
||||||
@ -158,7 +158,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
doMove(x, y) {
|
doMove(x, y) {
|
||||||
let style = this.$refs.imgex.style
|
const style = this.$refs.imgex.style
|
||||||
style.left = `${this.pxStringToNumber(style.left) + x}px`
|
style.left = `${this.pxStringToNumber(style.left) + x}px`
|
||||||
style.top = `${this.pxStringToNumber(style.top) + y}px`
|
style.top = `${this.pxStringToNumber(style.top) + y}px`
|
||||||
},
|
},
|
||||||
@ -172,7 +172,7 @@ export default {
|
|||||||
this.$refs.imgex.style.transform = `scale(${this.scale})`
|
this.$refs.imgex.style.transform = `scale(${this.scale})`
|
||||||
},
|
},
|
||||||
pxStringToNumber(style) {
|
pxStringToNumber(style) {
|
||||||
return +style.replace("px", "")
|
return +style.replace('px', '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,41 +4,53 @@
|
|||||||
<i class="material-icons">sentiment_dissatisfied</i>
|
<i class="material-icons">sentiment_dissatisfied</i>
|
||||||
<span>{{ $t('files.lonely') }}</span>
|
<span>{{ $t('files.lonely') }}</span>
|
||||||
</h2>
|
</h2>
|
||||||
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple>
|
<input id="upload-input" style="display:none" type="file" multiple @change="uploadInput($event)">
|
||||||
</div>
|
</div>
|
||||||
<div v-else id="listing"
|
<div
|
||||||
|
v-else
|
||||||
|
id="listing"
|
||||||
:class="user.viewMode"
|
:class="user.viewMode"
|
||||||
@dragenter="dragEnter"
|
@dragenter="dragEnter"
|
||||||
@dragend="dragEnd">
|
@dragend="dragEnd"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<div class="item header">
|
<div class="item header">
|
||||||
<div></div>
|
<div />
|
||||||
<div>
|
<div>
|
||||||
<p :class="{ active: nameSorted }" class="name"
|
<p
|
||||||
|
:class="{ active: nameSorted }"
|
||||||
|
class="name"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="sort('name')"
|
|
||||||
:title="$t('files.sortByName')"
|
:title="$t('files.sortByName')"
|
||||||
:aria-label="$t('files.sortByName')">
|
:aria-label="$t('files.sortByName')"
|
||||||
|
@click="sort('name')"
|
||||||
|
>
|
||||||
<span>{{ $t('files.name') }}</span>
|
<span>{{ $t('files.name') }}</span>
|
||||||
<i class="material-icons">{{ nameIcon }}</i>
|
<i class="material-icons">{{ nameIcon }}</i>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p :class="{ active: sizeSorted }" class="size"
|
<p
|
||||||
|
:class="{ active: sizeSorted }"
|
||||||
|
class="size"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="sort('size')"
|
|
||||||
:title="$t('files.sortBySize')"
|
:title="$t('files.sortBySize')"
|
||||||
:aria-label="$t('files.sortBySize')">
|
:aria-label="$t('files.sortBySize')"
|
||||||
|
@click="sort('size')"
|
||||||
|
>
|
||||||
<span>{{ $t('files.size') }}</span>
|
<span>{{ $t('files.size') }}</span>
|
||||||
<i class="material-icons">{{ sizeIcon }}</i>
|
<i class="material-icons">{{ sizeIcon }}</i>
|
||||||
</p>
|
</p>
|
||||||
<p :class="{ active: modifiedSorted }" class="modified"
|
<p
|
||||||
|
:class="{ active: modifiedSorted }"
|
||||||
|
class="modified"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="sort('modified')"
|
|
||||||
:title="$t('files.sortByLastModified')"
|
:title="$t('files.sortByLastModified')"
|
||||||
:aria-label="$t('files.sortByLastModified')">
|
:aria-label="$t('files.sortByLastModified')"
|
||||||
|
@click="sort('modified')"
|
||||||
|
>
|
||||||
<span>{{ $t('files.lastModified') }}</span>
|
<span>{{ $t('files.lastModified') }}</span>
|
||||||
<i class="material-icons">{{ modifiedIcon }}</i>
|
<i class="material-icons">{{ modifiedIcon }}</i>
|
||||||
</p>
|
</p>
|
||||||
@ -48,37 +60,39 @@
|
|||||||
|
|
||||||
<h2 v-if="req.numDirs > 0">{{ $t('files.folders') }}</h2>
|
<h2 v-if="req.numDirs > 0">{{ $t('files.folders') }}</h2>
|
||||||
<div v-if="req.numDirs > 0">
|
<div v-if="req.numDirs > 0">
|
||||||
<item v-for="(item) in dirs"
|
<item
|
||||||
|
v-for="(item) in dirs"
|
||||||
:key="base64(item.name)"
|
:key="base64(item.name)"
|
||||||
v-bind:index="item.index"
|
:index="item.index"
|
||||||
v-bind:name="item.name"
|
:name="item.name"
|
||||||
v-bind:isDir="item.isDir"
|
:is-dir="item.isDir"
|
||||||
v-bind:url="item.url"
|
:url="item.url"
|
||||||
v-bind:modified="item.modified"
|
:modified="item.modified"
|
||||||
v-bind:type="item.type"
|
:type="item.type"
|
||||||
v-bind:size="item.size">
|
:size="item.size"
|
||||||
</item>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 v-if="req.numFiles > 0">{{ $t('files.files') }}</h2>
|
<h2 v-if="req.numFiles > 0">{{ $t('files.files') }}</h2>
|
||||||
<div v-if="req.numFiles > 0">
|
<div v-if="req.numFiles > 0">
|
||||||
<item v-for="(item) in files"
|
<item
|
||||||
|
v-for="(item) in files"
|
||||||
:key="base64(item.name)"
|
:key="base64(item.name)"
|
||||||
v-bind:index="item.index"
|
:index="item.index"
|
||||||
v-bind:name="item.name"
|
:name="item.name"
|
||||||
v-bind:isDir="item.isDir"
|
:is-dir="item.isDir"
|
||||||
v-bind:url="item.url"
|
:url="item.url"
|
||||||
v-bind:modified="item.modified"
|
:modified="item.modified"
|
||||||
v-bind:type="item.type"
|
:type="item.type"
|
||||||
v-bind:size="item.size">
|
:size="item.size"
|
||||||
</item>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple>
|
<input id="upload-input" style="display:none" type="file" multiple @change="uploadInput($event)">
|
||||||
|
|
||||||
<div :class="{ active: $store.state.multiple }" id="multiple-selection">
|
<div id="multiple-selection" :class="{ active: $store.state.multiple }">
|
||||||
<p>{{ $t('files.multipleSelectionEnabled') }}</p>
|
<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 tabindex="0" role="button" :title="$t('files.clear')" :aria-label="$t('files.clear')" class="action" @click="$store.commit('multiple', false)">
|
||||||
<i class="material-icons">clear</i>
|
<i class="material-icons">clear</i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -94,28 +108,28 @@ import buttons from '@/utils/buttons'
|
|||||||
import url from '@/utils/url'
|
import url from '@/utils/url'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'listing',
|
name: 'Listing',
|
||||||
components: { Item },
|
components: { Item },
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
show: 50
|
show: 50
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'selected', 'user']),
|
...mapState(['req', 'selected', 'user']),
|
||||||
nameSorted () {
|
nameSorted() {
|
||||||
return (this.req.sorting.by === 'name')
|
return (this.req.sorting.by === 'name')
|
||||||
},
|
},
|
||||||
sizeSorted () {
|
sizeSorted() {
|
||||||
return (this.req.sorting.by === 'size')
|
return (this.req.sorting.by === 'size')
|
||||||
},
|
},
|
||||||
modifiedSorted () {
|
modifiedSorted() {
|
||||||
return (this.req.sorting.by === 'modified')
|
return (this.req.sorting.by === 'modified')
|
||||||
},
|
},
|
||||||
ascOrdered () {
|
ascOrdered() {
|
||||||
return this.req.sorting.asc
|
return this.req.sorting.asc
|
||||||
},
|
},
|
||||||
items () {
|
items() {
|
||||||
const dirs = []
|
const dirs = []
|
||||||
const files = []
|
const files = []
|
||||||
|
|
||||||
@ -129,31 +143,31 @@ export default {
|
|||||||
|
|
||||||
return { dirs, files }
|
return { dirs, files }
|
||||||
},
|
},
|
||||||
dirs () {
|
dirs() {
|
||||||
return this.items.dirs.slice(0, this.show)
|
return this.items.dirs.slice(0, this.show)
|
||||||
},
|
},
|
||||||
files () {
|
files() {
|
||||||
let show = this.show - this.items.dirs.length
|
let show = this.show - this.items.dirs.length
|
||||||
|
|
||||||
if (show < 0) show = 0
|
if (show < 0) show = 0
|
||||||
|
|
||||||
return this.items.files.slice(0, show)
|
return this.items.files.slice(0, show)
|
||||||
},
|
},
|
||||||
nameIcon () {
|
nameIcon() {
|
||||||
if (this.nameSorted && !this.ascOrdered) {
|
if (this.nameSorted && !this.ascOrdered) {
|
||||||
return 'arrow_upward'
|
return 'arrow_upward'
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'arrow_downward'
|
return 'arrow_downward'
|
||||||
},
|
},
|
||||||
sizeIcon () {
|
sizeIcon() {
|
||||||
if (this.sizeSorted && this.ascOrdered) {
|
if (this.sizeSorted && this.ascOrdered) {
|
||||||
return 'arrow_downward'
|
return 'arrow_downward'
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'arrow_upward'
|
return 'arrow_upward'
|
||||||
},
|
},
|
||||||
modifiedIcon () {
|
modifiedIcon() {
|
||||||
if (this.modifiedSorted && this.ascOrdered) {
|
if (this.modifiedSorted && this.ascOrdered) {
|
||||||
return 'arrow_downward'
|
return 'arrow_downward'
|
||||||
}
|
}
|
||||||
@ -161,7 +175,7 @@ export default {
|
|||||||
return 'arrow_upward'
|
return 'arrow_upward'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function() {
|
||||||
// Check the columns size for the first time.
|
// Check the columns size for the first time.
|
||||||
this.resizeEvent()
|
this.resizeEvent()
|
||||||
|
|
||||||
@ -172,7 +186,7 @@ export default {
|
|||||||
document.addEventListener('dragover', this.preventDefault)
|
document.addEventListener('dragover', this.preventDefault)
|
||||||
document.addEventListener('drop', this.drop)
|
document.addEventListener('drop', this.drop)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
// Remove event listeners before destroying this page.
|
// Remove event listeners before destroying this page.
|
||||||
window.removeEventListener('keydown', this.keyEvent)
|
window.removeEventListener('keydown', this.keyEvent)
|
||||||
window.removeEventListener('resize', this.resizeEvent)
|
window.removeEventListener('resize', this.resizeEvent)
|
||||||
@ -181,16 +195,16 @@ export default {
|
|||||||
document.removeEventListener('drop', this.drop)
|
document.removeEventListener('drop', this.drop)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'updateUser' ]),
|
...mapMutations(['updateUser']),
|
||||||
base64: function (name) {
|
base64: function(name) {
|
||||||
return window.btoa(unescape(encodeURIComponent(name)))
|
return window.btoa(unescape(encodeURIComponent(name)))
|
||||||
},
|
},
|
||||||
keyEvent (event) {
|
keyEvent(event) {
|
||||||
if (!event.ctrlKey && !event.metaKey) {
|
if (!event.ctrlKey && !event.metaKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = String.fromCharCode(event.which).toLowerCase()
|
const key = String.fromCharCode(event.which).toLowerCase()
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'f':
|
case 'f':
|
||||||
@ -206,25 +220,25 @@ export default {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
preventDefault (event) {
|
preventDefault(event) {
|
||||||
// Wrapper around prevent default.
|
// Wrapper around prevent default.
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
},
|
},
|
||||||
copyCut (event, key) {
|
copyCut(event, key) {
|
||||||
if (event.target.tagName.toLowerCase() === 'input') {
|
if (event.target.tagName.toLowerCase() === 'input') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let items = []
|
const items = []
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (const i of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[i].url,
|
from: this.req.items[i].url,
|
||||||
name: encodeURIComponent(this.req.items[i].name)
|
name: encodeURIComponent(this.req.items[i].name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.length == 0) {
|
if (items.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,14 +247,14 @@ export default {
|
|||||||
items: items
|
items: items
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
paste (event) {
|
paste(event) {
|
||||||
if (event.target.tagName.toLowerCase() === 'input') {
|
if (event.target.tagName.toLowerCase() === 'input') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let items = []
|
const items = []
|
||||||
|
|
||||||
for (let item of this.$store.state.clipboard.items) {
|
for (const item of this.$store.state.clipboard.items) {
|
||||||
const from = item.from.endsWith('/') ? item.from.slice(0, -1) : item.from
|
const from = item.from.endsWith('/') ? item.from.slice(0, -1) : item.from
|
||||||
const to = this.$route.path + item.name
|
const to = this.$route.path + item.name
|
||||||
items.push({ from, to })
|
items.push({ from, to })
|
||||||
@ -261,36 +275,36 @@ export default {
|
|||||||
this.$store.commit('setReload', true)
|
this.$store.commit('setReload', true)
|
||||||
}).catch(this.$showError)
|
}).catch(this.$showError)
|
||||||
},
|
},
|
||||||
resizeEvent () {
|
resizeEvent() {
|
||||||
// Update the columns size based on the window width.
|
// Update the columns size based on the window width.
|
||||||
let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
|
let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
|
||||||
let items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
|
const items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
|
||||||
if (columns === 0) columns = 1
|
if (columns === 0) columns = 1
|
||||||
items.style.width = `calc(${100 / columns}% - 1em)`
|
items.style.width = `calc(${100 / columns}% - 1em)`
|
||||||
},
|
},
|
||||||
scrollEvent () {
|
scrollEvent() {
|
||||||
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
|
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
|
||||||
this.show += 50
|
this.show += 50
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dragEnter () {
|
dragEnter() {
|
||||||
// When the user starts dragging an item, put every
|
// When the user starts dragging an item, put every
|
||||||
// file on the listing with 50% opacity.
|
// file on the listing with 50% opacity.
|
||||||
let items = document.getElementsByClassName('item')
|
const items = document.getElementsByClassName('item')
|
||||||
|
|
||||||
Array.from(items).forEach(file => {
|
Array.from(items).forEach(file => {
|
||||||
file.style.opacity = 0.5
|
file.style.opacity = 0.5
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dragEnd () {
|
dragEnd() {
|
||||||
this.resetOpacity()
|
this.resetOpacity()
|
||||||
},
|
},
|
||||||
drop: function (event) {
|
drop: function(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.resetOpacity()
|
this.resetOpacity()
|
||||||
|
|
||||||
let dt = event.dataTransfer
|
const dt = event.dataTransfer
|
||||||
let files = dt.files
|
const files = dt.files
|
||||||
let el = event.target
|
let el = event.target
|
||||||
|
|
||||||
if (files.length <= 0) return
|
if (files.length <= 0) return
|
||||||
@ -318,14 +332,14 @@ export default {
|
|||||||
|
|
||||||
this.checkConflict(files, this.req.items, base)
|
this.checkConflict(files, this.req.items, base)
|
||||||
},
|
},
|
||||||
checkConflict (files, items, base) {
|
checkConflict(files, items, base) {
|
||||||
if (typeof items === 'undefined' || items === null) {
|
if (typeof items === 'undefined' || items === null) {
|
||||||
items = []
|
items = []
|
||||||
}
|
}
|
||||||
|
|
||||||
let conflict = false
|
let conflict = false
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
let res = items.findIndex(function hasConflict (element) {
|
const res = items.findIndex(function hasConflict(element) {
|
||||||
return (element.name === this)
|
return (element.name === this)
|
||||||
}, files[i].name)
|
}, files[i].name)
|
||||||
|
|
||||||
@ -349,22 +363,22 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
uploadInput (event) {
|
uploadInput(event) {
|
||||||
this.checkConflict(event.currentTarget.files, this.req.items, '')
|
this.checkConflict(event.currentTarget.files, this.req.items, '')
|
||||||
},
|
},
|
||||||
resetOpacity () {
|
resetOpacity() {
|
||||||
let items = document.getElementsByClassName('item')
|
const items = document.getElementsByClassName('item')
|
||||||
|
|
||||||
Array.from(items).forEach(file => {
|
Array.from(items).forEach(file => {
|
||||||
file.style.opacity = 1
|
file.style.opacity = 1
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleFiles (files, base, overwrite = false) {
|
handleFiles(files, base, overwrite = false) {
|
||||||
buttons.loading('upload')
|
buttons.loading('upload')
|
||||||
let promises = []
|
const promises = []
|
||||||
let progress = new Array(files.length).fill(0)
|
const progress = new Array(files.length).fill(0)
|
||||||
|
|
||||||
let onupload = (id) => (event) => {
|
const onupload = (id) => (event) => {
|
||||||
progress[id] = (event.loaded / event.total) * 100
|
progress[id] = (event.loaded / event.total) * 100
|
||||||
|
|
||||||
let sum = 0
|
let sum = 0
|
||||||
@ -376,12 +390,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
let file = files[i]
|
const file = files[i]
|
||||||
let filenameEncoded = url.encodeRFC5987ValueChars(file.name)
|
const filenameEncoded = url.encodeRFC5987ValueChars(file.name)
|
||||||
promises.push(api.post(this.$route.path + base + filenameEncoded, file, overwrite, onupload(i)))
|
promises.push(api.post(this.$route.path + base + filenameEncoded, file, overwrite, onupload(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
let finish = () => {
|
const finish = () => {
|
||||||
buttons.success('upload')
|
buttons.success('upload')
|
||||||
this.$store.commit('setProgress', 0)
|
this.$store.commit('setProgress', 0)
|
||||||
}
|
}
|
||||||
@ -398,7 +412,7 @@ export default {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
async sort (by) {
|
async sort(by) {
|
||||||
let asc = false
|
let asc = false
|
||||||
|
|
||||||
if (by === 'name') {
|
if (by === 'name') {
|
||||||
@ -416,7 +430,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await users.update({ id: this.user.id, sorting: { by, asc } }, ['sorting'])
|
await users.update({ id: this.user.id, sorting: { by, asc }}, ['sorting'])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,8 @@
|
|||||||
:aria-label="name"
|
:aria-label="name"
|
||||||
:aria-selected="isSelected">
|
:aria-selected="isSelected">
|
||||||
<div>
|
<div>
|
||||||
<i class="material-icons">{{ icon }}</i>
|
<img v-if="type==='image'" :src="thumbnailUrl">
|
||||||
|
<i v-else class="material-icons">{{ icon }}</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -31,25 +32,26 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
import { mapMutations, mapGetters, mapState } from 'vuex'
|
||||||
|
import { baseURL } from '@/utils/constants'
|
||||||
import filesize from 'filesize'
|
import filesize from 'filesize'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'item',
|
name: 'Item',
|
||||||
data: function () {
|
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
||||||
|
data: function() {
|
||||||
return {
|
return {
|
||||||
touches: 0
|
touches: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['selected', 'req', 'user']),
|
...mapState(['selected', 'req', 'jwt']),
|
||||||
...mapGetters(['selectedCount']),
|
...mapGetters(['selectedCount']),
|
||||||
isSelected () {
|
isSelected() {
|
||||||
return (this.selected.indexOf(this.index) !== -1)
|
return this.selected.indexOf(this.index) !== -1
|
||||||
},
|
},
|
||||||
icon () {
|
icon() {
|
||||||
if (this.isDir) return 'folder'
|
if (this.isDir) return 'folder'
|
||||||
if (this.type === 'image') return 'insert_photo'
|
if (this.type === 'image') return 'insert_photo'
|
||||||
if (this.type === 'audio') return 'volume_up'
|
if (this.type === 'audio') return 'volume_up'
|
||||||
@ -59,27 +61,31 @@ export default {
|
|||||||
isDraggable () {
|
isDraggable () {
|
||||||
return this.user.perm.rename
|
return this.user.perm.rename
|
||||||
},
|
},
|
||||||
canDrop () {
|
canDrop() {
|
||||||
if (!this.isDir) return false
|
if (!this.isDir) return false
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (const i of this.selected) {
|
||||||
if (this.req.items[i].url === this.url) {
|
if (this.req.items[i].url === this.url) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
},
|
||||||
|
thumbnailUrl() {
|
||||||
|
const path = this.url.replace(/^\/files\//, '')
|
||||||
|
return `${baseURL}/api/thumbnail/${path}?auth=${this.jwt}&inline=true`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['addSelected', 'removeSelected', 'resetSelected']),
|
...mapMutations(['addSelected', 'removeSelected', 'resetSelected']),
|
||||||
humanSize: function () {
|
humanSize: function() {
|
||||||
return filesize(this.size)
|
return filesize(this.size)
|
||||||
},
|
},
|
||||||
humanTime: function () {
|
humanTime: function() {
|
||||||
return moment(this.modified).fromNow()
|
return moment(this.modified).fromNow()
|
||||||
},
|
},
|
||||||
dragStart: function () {
|
dragStart: function() {
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index)
|
||||||
return
|
return
|
||||||
@ -90,7 +96,7 @@ export default {
|
|||||||
this.addSelected(this.index)
|
this.addSelected(this.index)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dragOver: function (event) {
|
dragOver: function(event) {
|
||||||
if (!this.canDrop) return
|
if (!this.canDrop) return
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@ -104,28 +110,29 @@ export default {
|
|||||||
|
|
||||||
el.style.opacity = 1
|
el.style.opacity = 1
|
||||||
},
|
},
|
||||||
drop: function (event) {
|
drop: function(event) {
|
||||||
if (!this.canDrop) return
|
if (!this.canDrop) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (this.selectedCount === 0) return
|
if (this.selectedCount === 0) return
|
||||||
|
|
||||||
let items = []
|
const items = []
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (const i of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[i].url,
|
from: this.req.items[i].url,
|
||||||
to: this.url + this.req.items[i].name
|
to: this.url + this.req.items[i].name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
api.move(items)
|
api
|
||||||
|
.move(items)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$store.commit('setReload', true)
|
this.$store.commit('setReload', true)
|
||||||
})
|
})
|
||||||
.catch(this.$showError)
|
.catch(this.$showError)
|
||||||
},
|
},
|
||||||
click: function (event) {
|
click: function(event) {
|
||||||
if (this.selectedCount !== 0) event.preventDefault()
|
if (this.selectedCount !== 0) event.preventDefault()
|
||||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||||
this.removeSelected(this.index)
|
this.removeSelected(this.index)
|
||||||
@ -154,7 +161,7 @@ export default {
|
|||||||
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
|
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index)
|
||||||
},
|
},
|
||||||
touchstart () {
|
touchstart() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.touches = 0
|
this.touches = 0
|
||||||
}, 300)
|
}, 300)
|
||||||
@ -164,8 +171,8 @@ export default {
|
|||||||
this.open()
|
this.open()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
open: function () {
|
open: function() {
|
||||||
this.$router.push({path: this.url})
|
this.$router.push({ path: this.url })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="previewer">
|
<div id="previewer">
|
||||||
<div class="bar">
|
<div class="bar">
|
||||||
<button @click="back" class="action" :title="$t('files.closePreview')" :aria-label="$t('files.closePreview')" id="close">
|
<button
|
||||||
|
id="close"
|
||||||
|
class="action"
|
||||||
|
:title="$t('files.closePreview')"
|
||||||
|
:aria-label="$t('files.closePreview')"
|
||||||
|
@click="back"
|
||||||
|
>
|
||||||
<i class="material-icons">close</i>
|
<i class="material-icons">close</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<rename-button v-if="user.perm.rename"></rename-button>
|
<rename-button v-if="user.perm.rename" />
|
||||||
<delete-button v-if="user.perm.delete"></delete-button>
|
<delete-button v-if="user.perm.delete" />
|
||||||
<download-button v-if="user.perm.download"></download-button>
|
<download-button v-if="user.perm.download" />
|
||||||
<info-button></info-button>
|
<info-button />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="action" @click="prev" v-show="hasPrevious" :aria-label="$t('buttons.previous')" :title="$t('buttons.previous')">
|
<button
|
||||||
|
v-show="hasPrevious"
|
||||||
|
class="action"
|
||||||
|
:aria-label="$t('buttons.previous')"
|
||||||
|
:title="$t('buttons.previous')"
|
||||||
|
@click="prev"
|
||||||
|
>
|
||||||
<i class="material-icons">chevron_left</i>
|
<i class="material-icons">chevron_left</i>
|
||||||
</button>
|
</button>
|
||||||
<button class="action" @click="next" v-show="hasNext" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
|
<button
|
||||||
|
v-show="hasNext"
|
||||||
|
class="action"
|
||||||
|
:aria-label="$t('buttons.next')"
|
||||||
|
:title="$t('buttons.next')"
|
||||||
|
@click="next"
|
||||||
|
>
|
||||||
<i class="material-icons">chevron_right</i>
|
<i class="material-icons">chevron_right</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="preview">
|
<div class="preview">
|
||||||
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage>
|
<ExtendedImage v-if="req.type == 'image'" :src="raw" />
|
||||||
<audio v-else-if="req.type == 'audio'" :src="raw" autoplay controls></audio>
|
<audio v-else-if="req.type == 'audio'" :src="raw" autoplay controls />
|
||||||
<video v-else-if="req.type == 'video'" :src="raw" autoplay controls>
|
<video v-else-if="req.type == 'video'" :src="raw" autoplay controls>
|
||||||
<track
|
<track
|
||||||
kind="captions"
|
|
||||||
v-for="(sub, index) in subtitles"
|
v-for="(sub, index) in subtitles"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
kind="captions"
|
||||||
:src="sub"
|
:src="sub"
|
||||||
:label="'Subtitle ' + index" :default="index === 0">
|
:label="'Subtitle ' + index"
|
||||||
Sorry, your browser doesn't support embedded videos,
|
:default="index === 0"
|
||||||
but don't worry, you can <a :href="download">download it</a>
|
>Sorry, your browser doesn't support embedded videos,
|
||||||
|
but don't worry, you can
|
||||||
|
<a :href="download">download it</a>
|
||||||
and watch it with your favorite video player!
|
and watch it with your favorite video player!
|
||||||
</video>
|
</video>
|
||||||
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw"></object>
|
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw" />
|
||||||
<a v-else-if="req.type == 'blob'" :href="download">
|
<a v-else-if="req.type == 'blob'" :href="download">
|
||||||
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,15 +74,10 @@ import RenameButton from '@/components/buttons/Rename'
|
|||||||
import DownloadButton from '@/components/buttons/Download'
|
import DownloadButton from '@/components/buttons/Download'
|
||||||
import ExtendedImage from './ExtendedImage'
|
import ExtendedImage from './ExtendedImage'
|
||||||
|
|
||||||
const mediaTypes = [
|
const mediaTypes = ['image', 'video', 'audio', 'blob']
|
||||||
"image",
|
|
||||||
"video",
|
|
||||||
"audio",
|
|
||||||
"blob"
|
|
||||||
]
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'preview',
|
name: 'Preview',
|
||||||
components: {
|
components: {
|
||||||
InfoButton,
|
InfoButton,
|
||||||
DeleteButton,
|
DeleteButton,
|
||||||
@ -67,7 +85,7 @@ export default {
|
|||||||
DownloadButton,
|
DownloadButton,
|
||||||
ExtendedImage
|
ExtendedImage
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
previousLink: '',
|
previousLink: '',
|
||||||
nextLink: '',
|
nextLink: '',
|
||||||
@ -77,24 +95,33 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'user', 'oldReq', 'jwt']),
|
...mapState(['req', 'user', 'oldReq', 'jwt']),
|
||||||
hasPrevious () {
|
hasPrevious() {
|
||||||
return (this.previousLink !== '')
|
return this.previousLink !== ''
|
||||||
},
|
},
|
||||||
hasNext () {
|
hasNext() {
|
||||||
return (this.nextLink !== '')
|
return this.nextLink !== ''
|
||||||
},
|
},
|
||||||
download () {
|
download() {
|
||||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
||||||
},
|
},
|
||||||
raw () {
|
thumbnail() {
|
||||||
return `${this.download}&inline=true`
|
if (this.req.type === 'image') {
|
||||||
|
return `${baseURL}/api/thumbnail${this.req.path}?auth=${this.jwt}`
|
||||||
|
} else {
|
||||||
|
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted () {
|
raw() {
|
||||||
|
return `${this.thumbnail}&inline=true`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
window.addEventListener('keyup', this.key)
|
window.addEventListener('keyup', this.key)
|
||||||
|
|
||||||
if (this.req.subtitles) {
|
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`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -109,30 +136,32 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
window.removeEventListener('keyup', this.key)
|
window.removeEventListener('keyup', this.key)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
back () {
|
back() {
|
||||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
const uri = url.removeLastDir(this.$route.path) + '/'
|
||||||
this.$router.push({ path: uri })
|
this.$router.push({ path: uri })
|
||||||
},
|
},
|
||||||
prev () {
|
prev() {
|
||||||
this.$router.push({ path: this.previousLink })
|
this.$router.push({ path: this.previousLink })
|
||||||
},
|
},
|
||||||
next () {
|
next() {
|
||||||
this.$router.push({ path: this.nextLink })
|
this.$router.push({ path: this.nextLink })
|
||||||
},
|
},
|
||||||
key (event) {
|
key(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (event.which === 13 || event.which === 39) { // right arrow
|
if (event.which === 13 || event.which === 39) {
|
||||||
|
// right arrow
|
||||||
if (this.hasNext) this.next()
|
if (this.hasNext) this.next()
|
||||||
} else if (event.which === 37) { // left arrow
|
} else if (event.which === 37) {
|
||||||
|
// left arrow
|
||||||
if (this.hasPrevious) this.prev()
|
if (this.hasPrevious) this.prev()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateLinks (items) {
|
updateLinks(items) {
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
if (items[i].name !== this.req.name) {
|
if (items[i].name !== this.req.name) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -6,19 +6,23 @@
|
|||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.copyMessage') }}</p>
|
<p>{{ $t('prompts.copyMessage') }}</p>
|
||||||
<file-list @update:selected="val => dest = val"></file-list>
|
<file-list @update:selected="val => dest = val" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat"
|
@click="$store.commit('closeHovers')"
|
||||||
@click="copy"
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
:disabled="$route.path === dest"
|
:disabled="$route.path === dest"
|
||||||
:aria-label="$t('buttons.copy')"
|
:aria-label="$t('buttons.copy')"
|
||||||
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
|
:title="$t('buttons.copy')"
|
||||||
|
@click="copy"
|
||||||
|
>{{ $t('buttons.copy') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -30,9 +34,9 @@ import { files as api } from '@/api'
|
|||||||
import buttons from '@/utils/buttons'
|
import buttons from '@/utils/buttons'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'copy',
|
name: 'Copy',
|
||||||
components: { FileList },
|
components: { FileList },
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
current: window.location.pathname,
|
current: window.location.pathname,
|
||||||
dest: null
|
dest: null
|
||||||
@ -40,13 +44,13 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: mapState(['req', 'selected']),
|
computed: mapState(['req', 'selected']),
|
||||||
methods: {
|
methods: {
|
||||||
copy: async function (event) {
|
copy: async function(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
buttons.loading('copy')
|
buttons.loading('copy')
|
||||||
let items = []
|
const items = []
|
||||||
|
|
||||||
// Create a new promise for each file.
|
// Create a new promise for each file.
|
||||||
for (let item of this.selected) {
|
for (const item of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[item].url,
|
from: this.req.items[item].url,
|
||||||
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
||||||
|
|||||||
@ -5,33 +5,37 @@
|
|||||||
<p v-else>{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
|
<p v-else>{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button @click="$store.commit('closeHovers')"
|
<button
|
||||||
class="button button--flat button--grey"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button @click="submit"
|
@click="$store.commit('closeHovers')"
|
||||||
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
|
<button
|
||||||
class="button button--flat button--red"
|
class="button button--flat button--red"
|
||||||
:aria-label="$t('buttons.delete')"
|
:aria-label="$t('buttons.delete')"
|
||||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
:title="$t('buttons.delete')"
|
||||||
|
@click="submit"
|
||||||
|
>{{ $t('buttons.delete') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters, mapMutations, mapState} from 'vuex'
|
import { mapGetters, mapMutations, mapState } from 'vuex'
|
||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
import url from '@/utils/url'
|
import url from '@/utils/url'
|
||||||
import buttons from '@/utils/buttons'
|
import buttons from '@/utils/buttons'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'delete',
|
name: 'Delete',
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['isListing', 'selectedCount']),
|
...mapGetters(['isListing', 'selectedCount']),
|
||||||
...mapState(['req', 'selected'])
|
...mapState(['req', 'selected'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['closeHovers']),
|
...mapMutations(['closeHovers']),
|
||||||
submit: async function () {
|
submit: async function() {
|
||||||
this.closeHovers()
|
this.closeHovers()
|
||||||
buttons.loading('delete')
|
buttons.loading('delete')
|
||||||
|
|
||||||
@ -47,8 +51,8 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let promises = []
|
const promises = []
|
||||||
for (let index of this.selected) {
|
for (const index of this.selected) {
|
||||||
promises.push(api.remove(this.req.items[index].url))
|
promises.push(api.remove(this.req.items[index].url))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating" id="download">
|
<div id="download" class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.download') }}</h2>
|
<h2>{{ $t('prompts.download') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -7,35 +7,35 @@
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.downloadMessage') }}</p>
|
<p>{{ $t('prompts.downloadMessage') }}</p>
|
||||||
|
|
||||||
<button class="button button--block" @click="download('zip')" v-focus>zip</button>
|
<button v-focus class="button button--block" @click="download('zip')">zip</button>
|
||||||
<button class="button button--block" @click="download('tar')" v-focus>tar</button>
|
<button v-focus class="button button--block" @click="download('tar')">tar</button>
|
||||||
<button class="button button--block" @click="download('targz')" v-focus>tar.gz</button>
|
<button v-focus class="button button--block" @click="download('targz')">tar.gz</button>
|
||||||
<button class="button button--block" @click="download('tarbz2')" v-focus>tar.bz2</button>
|
<button v-focus class="button button--block" @click="download('tarbz2')">tar.bz2</button>
|
||||||
<button class="button button--block" @click="download('tarxz')" v-focus>tar.xz</button>
|
<button v-focus class="button button--block" @click="download('tarxz')">tar.xz</button>
|
||||||
<button class="button button--block" @click="download('tarlz4')" v-focus>tar.lz4</button>
|
<button v-focus class="button button--block" @click="download('tarlz4')">tar.lz4</button>
|
||||||
<button class="button button--block" @click="download('tarsz')" v-focus>tar.sz</button>
|
<button v-focus class="button button--block" @click="download('tarsz')">tar.sz</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters, mapState} from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'download',
|
name: 'Download',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['selected', 'req']),
|
...mapState(['selected', 'req']),
|
||||||
...mapGetters(['selectedCount'])
|
...mapGetters(['selectedCount'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
download: function (format) {
|
download: function(format) {
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
api.download(format, this.$route.path)
|
api.download(format, this.$route.path)
|
||||||
} else {
|
} else {
|
||||||
let files = []
|
const files = []
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (const i of this.selected) {
|
||||||
files.push(this.req.items[i].url)
|
files.push(this.req.items[i].url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ul class="file-list">
|
<ul class="file-list">
|
||||||
<li @click="select"
|
<li
|
||||||
@touchstart="touchstart"
|
v-for="item in items"
|
||||||
@dblclick="next"
|
:key="item.name"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
:aria-label="item.name"
|
:aria-label="item.name"
|
||||||
:aria-selected="selected == item.url"
|
:aria-selected="selected == item.url"
|
||||||
:key="item.name" v-for="item in items"
|
:data-url="item.url"
|
||||||
:data-url="item.url">{{ item.name }}</li>
|
@click="select"
|
||||||
|
@touchstart="touchstart"
|
||||||
|
@dblclick="next"
|
||||||
|
>{{ item.name }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>{{ $t('prompts.currentlyNavigating') }} <code>{{ nav }}</code>.</p>
|
<p>{{ $t('prompts.currentlyNavigating') }} <code>{{ nav }}</code>.</p>
|
||||||
@ -22,8 +25,8 @@ import url from '@/utils/url'
|
|||||||
import { files } from '@/api'
|
import { files } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'file-list',
|
name: 'FileList',
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
touches: {
|
touches: {
|
||||||
@ -35,12 +38,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'req' ]),
|
...mapState(['req']),
|
||||||
nav () {
|
nav() {
|
||||||
return decodeURIComponent(this.current)
|
return decodeURIComponent(this.current)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
// If we're showing this on a listing,
|
// If we're showing this on a listing,
|
||||||
// we can use the current request object
|
// we can use the current request object
|
||||||
// to fill the move options.
|
// to fill the move options.
|
||||||
@ -56,7 +59,7 @@ export default {
|
|||||||
.catch(this.$showError)
|
.catch(this.$showError)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fillOptions (req) {
|
fillOptions(req) {
|
||||||
// Sets the current path and resets
|
// Sets the current path and resets
|
||||||
// the current items.
|
// the current items.
|
||||||
this.current = req.url
|
this.current = req.url
|
||||||
@ -79,7 +82,7 @@ export default {
|
|||||||
|
|
||||||
// Otherwise we add every directory to the
|
// Otherwise we add every directory to the
|
||||||
// move options.
|
// move options.
|
||||||
for (let item of req.items) {
|
for (const item of req.items) {
|
||||||
if (!item.isDir) continue
|
if (!item.isDir) continue
|
||||||
|
|
||||||
this.items.push({
|
this.items.push({
|
||||||
@ -88,18 +91,18 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
next: function (event) {
|
next: function(event) {
|
||||||
// Retrieves the URL of the directory the user
|
// Retrieves the URL of the directory the user
|
||||||
// just clicked in and fill the options with its
|
// just clicked in and fill the options with its
|
||||||
// content.
|
// content.
|
||||||
let uri = event.currentTarget.dataset.url
|
const uri = event.currentTarget.dataset.url
|
||||||
|
|
||||||
files.fetch(uri)
|
files.fetch(uri)
|
||||||
.then(this.fillOptions)
|
.then(this.fillOptions)
|
||||||
.catch(this.$showError)
|
.catch(this.$showError)
|
||||||
},
|
},
|
||||||
touchstart (event) {
|
touchstart(event) {
|
||||||
let url = event.currentTarget.dataset.url
|
const url = event.currentTarget.dataset.url
|
||||||
|
|
||||||
// In 300 milliseconds, we shall reset the count.
|
// In 300 milliseconds, we shall reset the count.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -123,7 +126,7 @@ export default {
|
|||||||
this.next(event)
|
this.next(event)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
select: function (event) {
|
select: function(event) {
|
||||||
// If the element is already selected, unselect it.
|
// If the element is already selected, unselect it.
|
||||||
if (this.selected === event.currentTarget.dataset.url) {
|
if (this.selected === event.currentTarget.dataset.url) {
|
||||||
this.selected = null
|
this.selected = null
|
||||||
|
|||||||
@ -19,16 +19,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button type="submit"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
type="submit"
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
:aria-label="$t('buttons.ok')"
|
:aria-label="$t('buttons.ok')"
|
||||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
:title="$t('buttons.ok')"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
>{{ $t('buttons.ok') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default { name: 'help' }
|
export default { name: 'Help' }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p v-if="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
|
<p v-if="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
|
||||||
|
|
||||||
<p class="break-word" v-if="selected.length < 2"><strong>{{ $t('prompts.displayName') }}</strong> {{ name }}</p>
|
<p v-if="selected.length < 2" class="break-word"><strong>{{ $t('prompts.displayName') }}</strong> {{ name }}</p>
|
||||||
<p v-if="!dir || selected.length > 1"><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length"></span> {{ humanSize }}</p>
|
<p v-if="!dir || selected.length > 1"><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length" /> {{ humanSize }}</p>
|
||||||
<p v-if="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime }}</p>
|
<p v-if="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime }}</p>
|
||||||
|
|
||||||
<template v-if="dir && selected.length === 0">
|
<template v-if="dir && selected.length === 0">
|
||||||
@ -25,57 +25,59 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button type="submit"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
type="submit"
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
:aria-label="$t('buttons.ok')"
|
:aria-label="$t('buttons.ok')"
|
||||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
:title="$t('buttons.ok')"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
>{{ $t('buttons.ok') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState, mapGetters} from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import filesize from 'filesize'
|
import filesize from 'filesize'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'info',
|
name: 'Info',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'selected']),
|
...mapState(['req', 'selected']),
|
||||||
...mapGetters(['selectedCount', 'isListing']),
|
...mapGetters(['selectedCount', 'isListing']),
|
||||||
humanSize: function () {
|
humanSize: function() {
|
||||||
if (this.selectedCount === 0 || !this.isListing) {
|
if (this.selectedCount === 0 || !this.isListing) {
|
||||||
return filesize(this.req.size)
|
return filesize(this.req.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
let sum = 0
|
let sum = 0
|
||||||
|
|
||||||
for (let selected of this.selected) {
|
for (const selected of this.selected) {
|
||||||
sum += this.req.items[selected].size
|
sum += this.req.items[selected].size
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesize(sum)
|
return filesize(sum)
|
||||||
},
|
},
|
||||||
humanTime: function () {
|
humanTime: function() {
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
return moment(this.req.modified).fromNow()
|
return moment(this.req.modified).fromNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
return moment(this.req.items[this.selected[0]]).fromNow()
|
return moment(this.req.items[this.selected[0]]).fromNow()
|
||||||
},
|
},
|
||||||
name: function () {
|
name: function() {
|
||||||
return this.selectedCount === 0 ? this.req.name : this.req.items[this.selected[0]].name
|
return this.selectedCount === 0 ? this.req.name : this.req.items[this.selected[0]].name
|
||||||
},
|
},
|
||||||
dir: function () {
|
dir: function() {
|
||||||
return this.selectedCount > 1 || (this.selectedCount === 0
|
return this.selectedCount > 1 || (this.selectedCount === 0
|
||||||
? this.req.isDir
|
? this.req.isDir
|
||||||
: this.req.items[this.selected[0]].isDir)
|
: this.req.items[this.selected[0]].isDir)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checksum: async function (event, algo) {
|
checksum: async function(event, algo) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
let link
|
let link
|
||||||
|
|||||||
@ -5,19 +5,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<file-list @update:selected="val => dest = val"></file-list>
|
<file-list @update:selected="val => dest = val" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat"
|
@click="$store.commit('closeHovers')"
|
||||||
@click="move"
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
:disabled="$route.path === dest"
|
:disabled="$route.path === dest"
|
||||||
:aria-label="$t('buttons.move')"
|
:aria-label="$t('buttons.move')"
|
||||||
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
|
:title="$t('buttons.move')"
|
||||||
|
@click="move"
|
||||||
|
>{{ $t('buttons.move') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -29,9 +33,9 @@ import { files as api } from '@/api'
|
|||||||
import buttons from '@/utils/buttons'
|
import buttons from '@/utils/buttons'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'move',
|
name: 'Move',
|
||||||
components: { FileList },
|
components: { FileList },
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
current: window.location.pathname,
|
current: window.location.pathname,
|
||||||
dest: null
|
dest: null
|
||||||
@ -39,12 +43,12 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: mapState(['req', 'selected']),
|
computed: mapState(['req', 'selected']),
|
||||||
methods: {
|
methods: {
|
||||||
move: async function (event) {
|
move: async function(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
buttons.loading('move')
|
buttons.loading('move')
|
||||||
let items = []
|
const items = []
|
||||||
|
|
||||||
for (let item of this.selected) {
|
for (const item of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[item].url,
|
from: this.req.items[item].url,
|
||||||
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
||||||
|
|||||||
@ -6,15 +6,15 @@
|
|||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.newDirMessage') }}</p>
|
<p>{{ $t('prompts.newDirMessage') }}</p>
|
||||||
<input class="input input--block" type="text" @keyup.enter="submit" v-model.trim="name" v-focus>
|
<input v-model.trim="name" v-focus class="input input--block" type="text" @keyup.enter="submit">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button
|
<button
|
||||||
class="button button--flat button--grey"
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')"
|
:title="$t('buttons.cancel')"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
>{{ $t('buttons.cancel') }}</button>
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
@ -32,14 +32,14 @@ import { files as api } from '@/api'
|
|||||||
import url from '@/utils/url'
|
import url from '@/utils/url'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'new-dir',
|
name: 'NewDir',
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
name: ''
|
name: ''
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([ 'isFiles', 'isListing' ])
|
...mapGetters(['isFiles', 'isListing'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: async function(event) {
|
submit: async function(event) {
|
||||||
@ -66,6 +66,6 @@ export default {
|
|||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -6,21 +6,21 @@
|
|||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.newFileMessage') }}</p>
|
<p>{{ $t('prompts.newFileMessage') }}</p>
|
||||||
<input class="input input--block" v-focus type="text" @keyup.enter="submit" v-model.trim="name">
|
<input v-model.trim="name" v-focus class="input input--block" type="text" @keyup.enter="submit">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button
|
<button
|
||||||
class="button button--flat button--grey"
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')"
|
:title="$t('buttons.cancel')"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
>{{ $t('buttons.cancel') }}</button>
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
@click="submit"
|
|
||||||
:aria-label="$t('buttons.create')"
|
:aria-label="$t('buttons.create')"
|
||||||
:title="$t('buttons.create')"
|
:title="$t('buttons.create')"
|
||||||
|
@click="submit"
|
||||||
>{{ $t('buttons.create') }}</button>
|
>{{ $t('buttons.create') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -32,14 +32,14 @@ import { files as api } from '@/api'
|
|||||||
import url from '@/utils/url'
|
import url from '@/utils/url'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'new-file',
|
name: 'NewFile',
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
name: ''
|
name: ''
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([ 'isFiles', 'isListing' ])
|
...mapGetters(['isFiles', 'isListing'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: async function(event) {
|
submit: async function(event) {
|
||||||
@ -66,6 +66,6 @@ export default {
|
|||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<component :is="currentComponent"></component>
|
<component :is="currentComponent" />
|
||||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
<div v-show="showOverlay" class="overlay" @click="resetPrompts" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ import { mapState } from 'vuex'
|
|||||||
import buttons from '@/utils/buttons'
|
import buttons from '@/utils/buttons'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'prompts',
|
name: 'Prompts',
|
||||||
components: {
|
components: {
|
||||||
Info,
|
Info,
|
||||||
Delete,
|
Delete,
|
||||||
@ -35,7 +35,7 @@ export default {
|
|||||||
Help,
|
Help,
|
||||||
Replace
|
Replace
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
pluginData: {
|
pluginData: {
|
||||||
buttons,
|
buttons,
|
||||||
@ -46,7 +46,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['show', 'plugins']),
|
...mapState(['show', 'plugins']),
|
||||||
currentComponent: function () {
|
currentComponent: function() {
|
||||||
const matched = [
|
const matched = [
|
||||||
'info',
|
'info',
|
||||||
'help',
|
'help',
|
||||||
@ -59,16 +59,16 @@ export default {
|
|||||||
'download',
|
'download',
|
||||||
'replace',
|
'replace',
|
||||||
'share'
|
'share'
|
||||||
].indexOf(this.show) >= 0;
|
].indexOf(this.show) >= 0
|
||||||
|
|
||||||
return matched && this.show || null;
|
return matched && this.show || null
|
||||||
},
|
},
|
||||||
showOverlay: function () {
|
showOverlay: function() {
|
||||||
return (this.show !== null && this.show !== 'search' && this.show !== 'more')
|
return (this.show !== null && this.show !== 'search' && this.show !== 'more')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetPrompts () {
|
resetPrompts() {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,19 +6,23 @@
|
|||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
|
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
|
||||||
<input class="input input--block" v-focus type="text" @keyup.enter="submit" v-model.trim="name">
|
<input v-model.trim="name" v-focus class="input input--block" type="text" @keyup.enter="submit">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button @click="submit"
|
@click="$store.commit('closeHovers')"
|
||||||
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
|
<button
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
type="submit"
|
type="submit"
|
||||||
:aria-label="$t('buttons.rename')"
|
:aria-label="$t('buttons.rename')"
|
||||||
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
|
:title="$t('buttons.rename')"
|
||||||
|
@click="submit"
|
||||||
|
>{{ $t('buttons.rename') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -29,13 +33,13 @@ import url from '@/utils/url'
|
|||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'rename',
|
name: 'Rename',
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
name: ''
|
name: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created() {
|
||||||
this.name = this.oldName()
|
this.name = this.oldName()
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -43,10 +47,10 @@ export default {
|
|||||||
...mapGetters(['isListing'])
|
...mapGetters(['isListing'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cancel: function () {
|
cancel: function() {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
},
|
},
|
||||||
oldName: function () {
|
oldName: function() {
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
return this.req.name
|
return this.req.name
|
||||||
}
|
}
|
||||||
@ -58,7 +62,7 @@ export default {
|
|||||||
|
|
||||||
return this.req.items[this.selected[0]].name
|
return this.req.items[this.selected[0]].name
|
||||||
},
|
},
|
||||||
submit: async function () {
|
submit: async function() {
|
||||||
let oldLink = ''
|
let oldLink = ''
|
||||||
let newLink = ''
|
let newLink = ''
|
||||||
|
|
||||||
|
|||||||
@ -9,14 +9,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat button--red"
|
@click="$store.commit('closeHovers')"
|
||||||
@click="showConfirm"
|
>{{ $t('buttons.cancel') }}</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat button--red"
|
||||||
:aria-label="$t('buttons.replace')"
|
:aria-label="$t('buttons.replace')"
|
||||||
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
|
:title="$t('buttons.replace')"
|
||||||
|
@click="showConfirm"
|
||||||
|
>{{ $t('buttons.replace') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -25,7 +29,7 @@
|
|||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'replace',
|
name: 'Replace',
|
||||||
computed: mapState(['showConfirm'])
|
computed: mapState(['showConfirm'])
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating" id="share">
|
<div id="share" class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('buttons.share') }}</h2>
|
<h2>{{ $t('buttons.share') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -7,7 +7,7 @@
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-if="!hasPermanent">
|
<li v-if="!hasPermanent">
|
||||||
<a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
|
<a :aria-label="$t('buttons.permalink')" @click="getPermalink">{{ $t('buttons.permalink') }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li v-for="link in links" :key="link.hash">
|
<li v-for="link in links" :key="link.hash">
|
||||||
@ -16,43 +16,53 @@
|
|||||||
<template v-else>{{ $t('permanent') }}</template>
|
<template v-else>{{ $t('permanent') }}</template>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button class="action"
|
<button
|
||||||
@click="deleteLink($event, link)"
|
class="action"
|
||||||
:aria-label="$t('buttons.delete')"
|
:aria-label="$t('buttons.delete')"
|
||||||
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
|
:title="$t('buttons.delete')"
|
||||||
|
@click="deleteLink($event, link)"
|
||||||
|
><i class="material-icons">delete</i></button>
|
||||||
|
|
||||||
<button class="action copy-clipboard"
|
<button
|
||||||
|
class="action copy-clipboard"
|
||||||
:data-clipboard-text="buildLink(link.hash)"
|
:data-clipboard-text="buildLink(link.hash)"
|
||||||
:aria-label="$t('buttons.copyToClipboard')"
|
:aria-label="$t('buttons.copyToClipboard')"
|
||||||
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
|
:title="$t('buttons.copyToClipboard')"
|
||||||
|
><i class="material-icons">content_paste</i></button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<input v-focus
|
<input
|
||||||
|
v-model.trim="time"
|
||||||
|
v-focus
|
||||||
type="number"
|
type="number"
|
||||||
max="2147483647"
|
max="2147483647"
|
||||||
min="0"
|
min="0"
|
||||||
@keyup.enter="submit"
|
@keyup.enter="submit"
|
||||||
v-model.trim="time">
|
>
|
||||||
<select v-model="unit" :aria-label="$t('time.unit')">
|
<select v-model="unit" :aria-label="$t('time.unit')">
|
||||||
<option value="seconds">{{ $t('time.seconds') }}</option>
|
<option value="seconds">{{ $t('time.seconds') }}</option>
|
||||||
<option value="minutes">{{ $t('time.minutes') }}</option>
|
<option value="minutes">{{ $t('time.minutes') }}</option>
|
||||||
<option value="hours">{{ $t('time.hours') }}</option>
|
<option value="hours">{{ $t('time.hours') }}</option>
|
||||||
<option value="days">{{ $t('time.days') }}</option>
|
<option value="days">{{ $t('time.days') }}</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="action"
|
<button
|
||||||
@click="submit"
|
class="action"
|
||||||
:aria-label="$t('buttons.create')"
|
:aria-label="$t('buttons.create')"
|
||||||
:title="$t('buttons.create')"><i class="material-icons">add</i></button>
|
:title="$t('buttons.create')"
|
||||||
|
@click="submit"
|
||||||
|
><i class="material-icons">add</i></button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat"
|
<button
|
||||||
@click="$store.commit('closeHovers')"
|
class="button button--flat"
|
||||||
:aria-label="$t('buttons.close')"
|
:aria-label="$t('buttons.close')"
|
||||||
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
|
:title="$t('buttons.close')"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
>{{ $t('buttons.close') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -65,8 +75,8 @@ import moment from 'moment'
|
|||||||
import Clipboard from 'clipboard'
|
import Clipboard from 'clipboard'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'share',
|
name: 'Share',
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
time: '',
|
time: '',
|
||||||
unit: 'hours',
|
unit: 'hours',
|
||||||
@ -76,9 +86,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'req', 'selected', 'selectedCount' ]),
|
...mapState(['req', 'selected', 'selectedCount']),
|
||||||
...mapGetters([ 'isListing' ]),
|
...mapGetters(['isListing']),
|
||||||
url () {
|
url() {
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
return this.$route.path
|
return this.$route.path
|
||||||
}
|
}
|
||||||
@ -91,13 +101,13 @@ export default {
|
|||||||
return this.req.items[this.selected[0]].url
|
return this.req.items[this.selected[0]].url
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async beforeMount () {
|
async beforeMount() {
|
||||||
try {
|
try {
|
||||||
const links = await api.get(this.url)
|
const links = await api.get(this.url)
|
||||||
this.links = links
|
this.links = links
|
||||||
this.sort()
|
this.sort()
|
||||||
|
|
||||||
for (let link of this.links) {
|
for (const link of this.links) {
|
||||||
if (link.expire === 0) {
|
if (link.expire === 0) {
|
||||||
this.hasPermanent = true
|
this.hasPermanent = true
|
||||||
break
|
break
|
||||||
@ -107,17 +117,17 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
this.clip = new Clipboard('.copy-clipboard')
|
this.clip = new Clipboard('.copy-clipboard')
|
||||||
this.clip.on('success', () => {
|
this.clip.on('success', () => {
|
||||||
this.$showSuccess(this.$t('success.linkCopied'))
|
this.$showSuccess(this.$t('success.linkCopied'))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
this.clip.destroy()
|
this.clip.destroy()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: async function () {
|
submit: async function() {
|
||||||
if (!this.time) return
|
if (!this.time) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -128,7 +138,7 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPermalink: async function () {
|
getPermalink: async function() {
|
||||||
try {
|
try {
|
||||||
const res = await api.create(this.url)
|
const res = await api.create(this.url)
|
||||||
this.links.push(res)
|
this.links.push(res)
|
||||||
@ -138,7 +148,7 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteLink: async function (event, link) {
|
deleteLink: async function(event, link) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
try {
|
try {
|
||||||
await api.remove(link.hash)
|
await api.remove(link.hash)
|
||||||
@ -148,13 +158,13 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
humanTime (time) {
|
humanTime(time) {
|
||||||
return moment(time * 1000).fromNow()
|
return moment(time * 1000).fromNow()
|
||||||
},
|
},
|
||||||
buildLink (hash) {
|
buildLink(hash) {
|
||||||
return `${window.location.origin}${baseURL}/share/${hash}`
|
return `${window.location.origin}${baseURL}/share/${hash}`
|
||||||
},
|
},
|
||||||
sort () {
|
sort() {
|
||||||
this.links = this.links.sort((a, b) => {
|
this.links = this.links.sort((a, b) => {
|
||||||
if (a.expire === 0) return -1
|
if (a.expire === 0) return -1
|
||||||
if (b.expire === 0) return 1
|
if (b.expire === 0) return 1
|
||||||
|
|||||||
@ -2,20 +2,20 @@
|
|||||||
<div>
|
<div>
|
||||||
<h3>{{ $t('settings.userCommands') }}</h3>
|
<h3>{{ $t('settings.userCommands') }}</h3>
|
||||||
<p class="small">{{ $t('settings.userCommandsHelp') }} <i>git svn hg</i>.</p>
|
<p class="small">{{ $t('settings.userCommandsHelp') }} <i>git svn hg</i>.</p>
|
||||||
<input class="input input--block" type="text" v-model.trim="raw">
|
<input v-model.trim="raw" class="input input--block" type="text">
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'permissions',
|
name: 'Permissions',
|
||||||
props: ['commands'],
|
props: ['commands'],
|
||||||
computed: {
|
computed: {
|
||||||
raw: {
|
raw: {
|
||||||
get () {
|
get() {
|
||||||
return this.commands.join(' ')
|
return this.commands.join(' ')
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
this.$emit('update:commands', value.split(' '))
|
this.$emit('update:commands', value.split(' '))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-on:change="change" :value="locale">
|
<select :value="locale" @change="change">
|
||||||
<option v-for="(language, value) in locales" :key="value" :value="value">{{ $t('languages.' + language) }}</option>
|
<option v-for="(language, value) in locales" :key="value" :value="value">{{ $t('languages.' + language) }}</option>
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
@ -7,10 +7,10 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'languages',
|
name: 'Languages',
|
||||||
props: [ 'locale' ],
|
props: ['locale'],
|
||||||
data() {
|
data() {
|
||||||
let dataObj = {
|
const dataObj = {
|
||||||
locales: {
|
locales: {
|
||||||
ar: 'ar',
|
ar: 'ar',
|
||||||
de: 'de',
|
de: 'de',
|
||||||
@ -31,14 +31,14 @@ export default {
|
|||||||
'zh-cn': 'zhCN',
|
'zh-cn': 'zhCN',
|
||||||
'zh-tw': 'zhTW'
|
'zh-tw': 'zhTW'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Object.defineProperty(dataObj, "locales", { configurable: false, writable: false });
|
Object.defineProperty(dataObj, 'locales', { configurable: false, writable: false })
|
||||||
|
|
||||||
return dataObj;
|
return dataObj
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change (event) {
|
change(event) {
|
||||||
this.$emit('update:locale', event.target.value)
|
this.$emit('update:locale', event.target.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,28 +3,28 @@
|
|||||||
<h3>{{ $t('settings.permissions') }}</h3>
|
<h3>{{ $t('settings.permissions') }}</h3>
|
||||||
<p class="small">{{ $t('settings.permissionsHelp') }}</p>
|
<p class="small">{{ $t('settings.permissionsHelp') }}</p>
|
||||||
|
|
||||||
<p><input type="checkbox" v-model="admin"> {{ $t('settings.administrator') }}</p>
|
<p><input v-model="admin" type="checkbox"> {{ $t('settings.administrator') }}</p>
|
||||||
|
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.create"> {{ $t('settings.perm.create') }}</p>
|
<p><input v-model="perm.create" type="checkbox" :disabled="admin"> {{ $t('settings.perm.create') }}</p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.delete"> {{ $t('settings.perm.delete') }}</p>
|
<p><input v-model="perm.delete" type="checkbox" :disabled="admin"> {{ $t('settings.perm.delete') }}</p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.download"> {{ $t('settings.perm.download') }}</p>
|
<p><input v-model="perm.download" type="checkbox" :disabled="admin"> {{ $t('settings.perm.download') }}</p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.modify"> {{ $t('settings.perm.modify') }}</p>
|
<p><input v-model="perm.modify" type="checkbox" :disabled="admin"> {{ $t('settings.perm.modify') }}</p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.execute"> {{ $t('settings.perm.execute') }}</p>
|
<p><input v-model="perm.execute" type="checkbox" :disabled="admin"> {{ $t('settings.perm.execute') }}</p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.rename"> {{ $t('settings.perm.rename') }}</p>
|
<p><input v-model="perm.rename" type="checkbox" :disabled="admin"> {{ $t('settings.perm.rename') }}</p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.share"> {{ $t('settings.perm.share') }}</p>
|
<p><input v-model="perm.share" type="checkbox" :disabled="admin"> {{ $t('settings.perm.share') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'permissions',
|
name: 'Permissions',
|
||||||
props: ['perm'],
|
props: ['perm'],
|
||||||
computed: {
|
computed: {
|
||||||
admin: {
|
admin: {
|
||||||
get () {
|
get() {
|
||||||
return this.perm.admin
|
return this.perm.admin
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
for (const key in this.perm) {
|
for (const key in this.perm) {
|
||||||
this.perm[key] = true
|
this.perm[key] = true
|
||||||
|
|||||||
@ -1,43 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<form class="rules small">
|
<form class="rules small">
|
||||||
<div v-for="(rule, index) in rules" :key="index">
|
<div v-for="(rule, index) in rules" :key="index">
|
||||||
<input type="checkbox" v-model="rule.regex"><label>Regex</label>
|
<input v-model="rule.regex" type="checkbox"><label>Regex</label>
|
||||||
<input type="checkbox" v-model="rule.allow"><label>Allow</label>
|
<input v-model="rule.allow" type="checkbox"><label>Allow</label>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@keypress.enter.prevent
|
|
||||||
type="text"
|
|
||||||
v-if="rule.regex"
|
v-if="rule.regex"
|
||||||
v-model="rule.regexp.raw"
|
v-model="rule.regexp.raw"
|
||||||
:placeholder="$t('settings.insertRegex')" />
|
|
||||||
<input
|
|
||||||
@keypress.enter.prevent
|
|
||||||
type="text"
|
type="text"
|
||||||
|
:placeholder="$t('settings.insertRegex')"
|
||||||
|
@keypress.enter.prevent
|
||||||
|
>
|
||||||
|
<input
|
||||||
v-else
|
v-else
|
||||||
v-model="rule.path"
|
v-model="rule.path"
|
||||||
:placeholder="$t('settings.insertPath')" />
|
type="text"
|
||||||
|
:placeholder="$t('settings.insertPath')"
|
||||||
|
@keypress.enter.prevent
|
||||||
|
>
|
||||||
|
|
||||||
<button class="button button--red" @click="remove($event, index)">-</button>
|
<button class="button button--red" @click="remove($event, index)">-</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="button" @click="create" default="false">{{ $t('buttons.new') }}</button>
|
<button class="button" default="false" @click="create">{{ $t('buttons.new') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'rules-textarea',
|
name: 'RulesTextarea',
|
||||||
props: ['rules'],
|
props: ['rules'],
|
||||||
methods: {
|
methods: {
|
||||||
remove (event, index) {
|
remove(event, index) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
let rules = [ ...this.rules ]
|
const rules = [...this.rules]
|
||||||
rules.splice(index, 1)
|
rules.splice(index, 1)
|
||||||
this.$emit('update:rules', [ ...rules ])
|
this.$emit('update:rules', [...rules])
|
||||||
},
|
},
|
||||||
create (event) {
|
create(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
this.$emit('update:rules', [
|
this.$emit('update:rules', [
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-on:change="change" :value="theme">
|
<select :value="theme" @change="change">
|
||||||
<option value="">{{ $t('settings.themes.light') }}</option>
|
<option value="">{{ $t('settings.themes.light') }}</option>
|
||||||
<option value="dark">{{ $t('settings.themes.dark') }}</option>
|
<option value="dark">{{ $t('settings.themes.dark') }}</option>
|
||||||
</select>
|
</select>
|
||||||
@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'themes',
|
name: 'Themes',
|
||||||
props: [ 'theme' ],
|
props: ['theme'],
|
||||||
methods: {
|
methods: {
|
||||||
change (event) {
|
change(event) {
|
||||||
this.$emit('update:theme', event.target.value)
|
this.$emit('update:theme', event.target.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,26 +2,26 @@
|
|||||||
<div>
|
<div>
|
||||||
<p v-if="!isDefault">
|
<p v-if="!isDefault">
|
||||||
<label for="username">{{ $t('settings.username') }}</label>
|
<label for="username">{{ $t('settings.username') }}</label>
|
||||||
<input class="input input--block" type="text" v-model="user.username" id="username">
|
<input id="username" v-model="user.username" class="input input--block" type="text">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p v-if="!isDefault">
|
<p v-if="!isDefault">
|
||||||
<label for="password">{{ $t('settings.password') }}</label>
|
<label for="password">{{ $t('settings.password') }}</label>
|
||||||
<input class="input input--block" type="password" :placeholder="passwordPlaceholder" v-model="user.password" id="password">
|
<input id="password" v-model="user.password" class="input input--block" type="password" :placeholder="passwordPlaceholder">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="scope">{{ $t('settings.scope') }}</label>
|
<label for="scope">{{ $t('settings.scope') }}</label>
|
||||||
<input class="input input--block" type="text" v-model="user.scope" id="scope">
|
<input id="scope" v-model="user.scope" class="input input--block" type="text">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="locale">{{ $t('settings.language') }}</label>
|
<label for="locale">{{ $t('settings.language') }}</label>
|
||||||
<languages class="input input--block" id="locale" :locale.sync="user.locale"></languages>
|
<languages id="locale" class="input input--block" :locale.sync="user.locale" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p v-if="!isDefault">
|
<p v-if="!isDefault">
|
||||||
<input type="checkbox" :disabled="user.perm.admin" v-model="user.lockPassword"> {{ $t('settings.lockPassword') }}
|
<input v-model="user.lockPassword" type="checkbox" :disabled="user.perm.admin"> {{ $t('settings.lockPassword') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<permissions :perm.sync="user.perm" />
|
<permissions :perm.sync="user.perm" />
|
||||||
@ -42,21 +42,21 @@ import Permissions from './Permissions'
|
|||||||
import Commands from './Commands'
|
import Commands from './Commands'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'user',
|
name: 'User',
|
||||||
components: {
|
components: {
|
||||||
Permissions,
|
Permissions,
|
||||||
Languages,
|
Languages,
|
||||||
Rules,
|
Rules,
|
||||||
Commands
|
Commands
|
||||||
},
|
},
|
||||||
props: [ 'user', 'isNew', 'isDefault' ],
|
props: ['user', 'isNew', 'isDefault'],
|
||||||
computed: {
|
computed: {
|
||||||
passwordPlaceholder () {
|
passwordPlaceholder() {
|
||||||
return this.isNew ? '' : this.$t('settings.avoidChanges')
|
return this.isNew ? '' : this.$t('settings.avoidChanges')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'user.perm.admin': function () {
|
'user.perm.admin': function() {
|
||||||
if (!this.user.perm.admin) return
|
if (!this.user.perm.admin) return
|
||||||
this.user.lockPassword = false
|
this.user.lockPassword = false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#listing h2 {
|
#listing h2 {
|
||||||
margin: 0 0 0 0.5em;
|
margin: 0 0 0 0.5em;
|
||||||
font-size: .9em;
|
font-size: 0.9em;
|
||||||
color: rgba(0, 0, 0, 0.38);
|
color: rgba(0, 0, 0, 0.38);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@ -10,7 +10,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing>div {
|
#listing > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@ -22,7 +22,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
color: #6f6f6f;
|
color: #6f6f6f;
|
||||||
transition: .1s ease background, .1s ease opacity;
|
transition: 0.1s ease background, 0.1s ease opacity;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -52,6 +52,13 @@
|
|||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#listing .item img {
|
||||||
|
width: 4em;
|
||||||
|
height: 4em;
|
||||||
|
margin-right: 0.1em;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
@ -64,7 +71,7 @@
|
|||||||
|
|
||||||
.message i {
|
.message i {
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
margin-bottom: .2em;
|
margin-bottom: 0.2em;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,14 +82,14 @@
|
|||||||
|
|
||||||
#listing.mosaic .item {
|
#listing.mosaic .item {
|
||||||
width: calc(33% - 1em);
|
width: calc(33% - 1em);
|
||||||
margin: .5em;
|
margin: 0.5em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.mosaic .item:hover {
|
#listing.mosaic .item:hover {
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !important;
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.mosaic .header {
|
#listing.mosaic .header {
|
||||||
@ -116,7 +123,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing .item[aria-selected=true] {
|
#listing .item[aria-selected="true"] {
|
||||||
background: var(--blue) !important;
|
background: var(--blue) !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
@ -129,6 +136,11 @@
|
|||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#listing.list .item div:first-of-type img {
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
#listing.list .item div:last-of-type {
|
#listing.list .item div:last-of-type {
|
||||||
width: calc(100% - 3em);
|
width: calc(100% - 3em);
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -151,19 +163,19 @@
|
|||||||
#listing.list .header i {
|
#listing.list .header i {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-left: .2em;
|
margin-left: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .item.header {
|
#listing.list .item.header {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
padding: .85em;
|
padding: 0.85em;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .item.header>div:first-child {
|
#listing.list .item.header > div:first-child {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +187,7 @@
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .item.header>div:first-child {
|
#listing.list .item.header > div:first-child {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +205,7 @@
|
|||||||
|
|
||||||
#listing.list .header i {
|
#listing.list .header i {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: .1s ease all;
|
transition: 0.1s ease all;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .header p:hover i,
|
#listing.list .header p:hover i,
|
||||||
@ -215,7 +227,7 @@
|
|||||||
height: 4em;
|
height: 4em;
|
||||||
padding: 0.5em 0.5em 0.5em 1em;
|
padding: 0.5em 0.5em 0.5em 1em;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
transition: .2s ease bottom;
|
transition: 0.2s ease bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing #multiple-selection.active {
|
#listing #multiple-selection.active {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import zhTW from './zh-tw.json'
|
|||||||
|
|
||||||
Vue.use(VueI18n)
|
Vue.use(VueI18n)
|
||||||
|
|
||||||
export function detectLocale () {
|
export function detectLocale() {
|
||||||
let locale = (navigator.language || navigator.browserLangugae).toLowerCase()
|
let locale = (navigator.language || navigator.browserLangugae).toLowerCase()
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case /^ar.*/i.test(locale):
|
case /^ar.*/i.test(locale):
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import App from '@/App'
|
|||||||
|
|
||||||
sync(store, router)
|
sync(store, router)
|
||||||
|
|
||||||
async function start () {
|
async function start() {
|
||||||
if (loginPage) {
|
if (loginPage) {
|
||||||
await validateLogin()
|
await validateLogin()
|
||||||
} else {
|
} else {
|
||||||
@ -17,7 +17,7 @@ async function start () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (recaptcha) {
|
if (recaptcha) {
|
||||||
await new Promise (resolve => {
|
await new Promise(resolve => {
|
||||||
const check = () => {
|
const check = () => {
|
||||||
if (typeof window.grecaptcha === 'undefined') {
|
if (typeof window.grecaptcha === 'undefined') {
|
||||||
setTimeout(check, 100)
|
setTimeout(check, 100)
|
||||||
@ -35,8 +35,8 @@ async function start () {
|
|||||||
store,
|
store,
|
||||||
router,
|
router,
|
||||||
i18n,
|
i18n,
|
||||||
template: '<App/>',
|
components: { App },
|
||||||
components: { App }
|
template: '<App/>'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,7 @@ const mutations = {
|
|||||||
state.plugins.push(value)
|
state.plugins.push(value)
|
||||||
},
|
},
|
||||||
removeSelected: (state, value) => {
|
removeSelected: (state, value) => {
|
||||||
let i = state.selected.indexOf(value)
|
const i = state.selected.indexOf(value)
|
||||||
if (i === -1) return
|
if (i === -1) return
|
||||||
state.selected.splice(i, 1)
|
state.selected.splice(i, 1)
|
||||||
},
|
},
|
||||||
@ -62,7 +62,7 @@ const mutations = {
|
|||||||
updateUser: (state, value) => {
|
updateUser: (state, value) => {
|
||||||
if (typeof value !== 'object') return
|
if (typeof value !== 'object') return
|
||||||
|
|
||||||
for (let field in value) {
|
for (const field in value) {
|
||||||
if (field === 'locale') {
|
if (field === 'locale') {
|
||||||
moment.locale(value[field])
|
moment.locale(value[field])
|
||||||
i18n.default.locale = value[field]
|
i18n.default.locale = value[field]
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import router from '@/router'
|
|||||||
import { Base64 } from 'js-base64'
|
import { Base64 } from 'js-base64'
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from '@/utils/constants'
|
||||||
|
|
||||||
export function parseToken (token) {
|
export function parseToken(token) {
|
||||||
const parts = token.split('.')
|
const parts = token.split('.')
|
||||||
|
|
||||||
if (parts.length !== 3) {
|
if (parts.length !== 3) {
|
||||||
@ -21,7 +21,7 @@ export function parseToken (token) {
|
|||||||
store.commit('setUser', data.user)
|
store.commit('setUser', data.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function validateLogin () {
|
export async function validateLogin() {
|
||||||
try {
|
try {
|
||||||
if (localStorage.getItem('jwt')) {
|
if (localStorage.getItem('jwt')) {
|
||||||
await renew(localStorage.getItem('jwt'))
|
await renew(localStorage.getItem('jwt'))
|
||||||
@ -31,7 +31,7 @@ export async function validateLogin () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login (username, password, recaptcha) {
|
export async function login(username, password, recaptcha) {
|
||||||
const data = { username, password, recaptcha }
|
const data = { username, password, recaptcha }
|
||||||
|
|
||||||
const res = await fetch(`${baseURL}/api/login`, {
|
const res = await fetch(`${baseURL}/api/login`, {
|
||||||
@ -51,11 +51,11 @@ export async function login (username, password, recaptcha) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function renew (jwt) {
|
export async function renew(jwt) {
|
||||||
const res = await fetch(`${baseURL}/api/renew`, {
|
const res = await fetch(`${baseURL}/api/renew`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Auth': jwt,
|
'X-Auth': jwt
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export async function renew (jwt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function signup (username, password) {
|
export async function signup(username, password) {
|
||||||
const data = { username, password }
|
const data = { username, password }
|
||||||
|
|
||||||
const res = await fetch(`${baseURL}/api/signup`, {
|
const res = await fetch(`${baseURL}/api/signup`, {
|
||||||
@ -84,9 +84,9 @@ export async function signup (username, password) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout () {
|
export function logout() {
|
||||||
store.commit('setJWT', '')
|
store.commit('setJWT', '')
|
||||||
store.commit('setUser', null)
|
store.commit('setUser', null)
|
||||||
localStorage.setItem('jwt', null)
|
localStorage.setItem('jwt', null)
|
||||||
router.push({path: '/login'})
|
router.push({ path: '/login' })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
function loading (button) {
|
function loading(button) {
|
||||||
let el = document.querySelector(`#${button}-button > i`)
|
const el = document.querySelector(`#${button}-button > i`)
|
||||||
|
|
||||||
if (el === undefined || el === null) {
|
if (el === undefined || el === null) {
|
||||||
console.log('Error getting button ' + button) // eslint-disable-line
|
console.log('Error getting button ' + button) // eslint-disable-line
|
||||||
@ -16,8 +16,8 @@ function loading (button) {
|
|||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
function done (button) {
|
function done(button) {
|
||||||
let el = document.querySelector(`#${button}-button > i`)
|
const el = document.querySelector(`#${button}-button > i`)
|
||||||
|
|
||||||
if (el === undefined || el === null) {
|
if (el === undefined || el === null) {
|
||||||
console.log('Error getting button ' + button) // eslint-disable-line
|
console.log('Error getting button ' + button) // eslint-disable-line
|
||||||
@ -33,8 +33,8 @@ function done (button) {
|
|||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
function success (button) {
|
function success(button) {
|
||||||
let el = document.querySelector(`#${button}-button > i`)
|
const el = document.querySelector(`#${button}-button > i`)
|
||||||
|
|
||||||
if (el === undefined || el === null) {
|
if (el === undefined || el === null) {
|
||||||
console.log('Error getting button ' + button) // eslint-disable-line
|
console.log('Error getting button ' + button) // eslint-disable-line
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export default function (name) {
|
export default function(name) {
|
||||||
let re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$')
|
const re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$')
|
||||||
return document.cookie.replace(re, '$1')
|
return document.cookie.replace(re, '$1')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
export default function getRule (rules) {
|
export default function getRule(rules) {
|
||||||
for (let i = 0; i < rules.length; i++) {
|
for (let i = 0; i < rules.length; i++) {
|
||||||
rules[i] = rules[i].toLowerCase()
|
rules[i] = rules[i].toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = null
|
let result = null
|
||||||
let find = Array.prototype.find
|
const find = Array.prototype.find
|
||||||
|
|
||||||
find.call(document.styleSheets, styleSheet => {
|
find.call(document.styleSheets, styleSheet => {
|
||||||
result = find.call(styleSheet.cssRules, cssRule => {
|
result = find.call(styleSheet.cssRules, cssRule => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
function removeLastDir (url) {
|
function removeLastDir(url) {
|
||||||
var arr = url.split('/')
|
var arr = url.split('/')
|
||||||
if (arr.pop() === '') {
|
if (arr.pop() === '') {
|
||||||
arr.pop()
|
arr.pop()
|
||||||
@ -10,14 +10,14 @@ function removeLastDir (url) {
|
|||||||
// this code borrow from mozilla
|
// this code borrow from mozilla
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Examples
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Examples
|
||||||
function encodeRFC5987ValueChars(str) {
|
function encodeRFC5987ValueChars(str) {
|
||||||
return encodeURIComponent(str).
|
return encodeURIComponent(str)
|
||||||
// Note that although RFC3986 reserves "!", RFC5987 does not,
|
// Note that although RFC3986 reserves "!", RFC5987 does not,
|
||||||
// so we do not need to escape it
|
// so we do not need to escape it
|
||||||
replace(/['()]/g, escape). // i.e., %27 %28 %29
|
.replace(/['()]/g, escape) // i.e., %27 %28 %29
|
||||||
replace(/\*/g, '%2A').
|
.replace(/\*/g, '%2A')
|
||||||
// The following are not required for percent-encoding per RFC5987,
|
// The following are not required for percent-encoding per RFC5987,
|
||||||
// so we can allow for a little better readability over the wire: |`^
|
// so we can allow for a little better readability over the wire: |`^
|
||||||
replace(/%(?:7C|60|5E)/g, unescape);
|
.replace(/%(?:7C|60|5E)/g, unescape)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@ -24,19 +24,19 @@ Vue.prototype.$showSuccess = (message) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vue.prototype.$showError = (error) => {
|
Vue.prototype.$showError = (error) => {
|
||||||
let btns = [
|
const btns = [
|
||||||
Noty.button(i18n.t('buttons.close'), '', function () {
|
Noty.button(i18n.t('buttons.close'), '', function() {
|
||||||
n.close()
|
n.close()
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!disableExternal) {
|
if (!disableExternal) {
|
||||||
btns.unshift(Noty.button(i18n.t('buttons.reportIssue'), '', function () {
|
btns.unshift(Noty.button(i18n.t('buttons.reportIssue'), '', function() {
|
||||||
window.open('https://github.com/filebrowser/filebrowser/issues/new/choose')
|
window.open('https://github.com/filebrowser/filebrowser/issues/new/choose')
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = new Noty(Object.assign({}, notyDefault, {
|
const n = new Noty(Object.assign({}, notyDefault, {
|
||||||
text: error.message || error,
|
text: error.message || error,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
timeout: null,
|
timeout: null,
|
||||||
@ -47,7 +47,7 @@ Vue.prototype.$showError = (error) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vue.directive('focus', {
|
Vue.directive('focus', {
|
||||||
inserted: function (el) {
|
inserted: function(el) {
|
||||||
el.focus()
|
el.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,13 +11,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="error">
|
<div v-if="error">
|
||||||
<not-found v-if="error.message === '404'"></not-found>
|
<not-found v-if="error.message === '404'" />
|
||||||
<forbidden v-else-if="error.message === '403'"></forbidden>
|
<forbidden v-else-if="error.message === '403'" />
|
||||||
<internal-error v-else></internal-error>
|
<internal-error v-else />
|
||||||
</div>
|
</div>
|
||||||
<editor v-else-if="isEditor"></editor>
|
<editor v-else-if="isEditor" />
|
||||||
<listing :class="{ multiple }" v-else-if="isListing"></listing>
|
<listing v-else-if="isListing" :class="{ multiple }" />
|
||||||
<preview v-else-if="isPreview"></preview>
|
<preview v-else-if="isPreview" />
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h2 class="message">
|
<h2 class="message">
|
||||||
<span>{{ $t('files.loading') }}</span>
|
<span>{{ $t('files.loading') }}</span>
|
||||||
@ -35,12 +35,12 @@ import Listing from '@/components/files/Listing'
|
|||||||
import { files as api } from '@/api'
|
import { files as api } from '@/api'
|
||||||
import { mapGetters, mapState, mapMutations } from 'vuex'
|
import { mapGetters, mapState, mapMutations } from 'vuex'
|
||||||
|
|
||||||
function clean (path) {
|
function clean(path) {
|
||||||
return path.endsWith('/') ? path.slice(0, -1) : path
|
return path.endsWith('/') ? path.slice(0, -1) : path
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'files',
|
name: 'Files',
|
||||||
components: {
|
components: {
|
||||||
Forbidden,
|
Forbidden,
|
||||||
NotFound,
|
NotFound,
|
||||||
@ -63,11 +63,11 @@ export default {
|
|||||||
'multiple',
|
'multiple',
|
||||||
'loading'
|
'loading'
|
||||||
]),
|
]),
|
||||||
isPreview () {
|
isPreview() {
|
||||||
return !this.loading && !this.isListing && !this.isEditor
|
return !this.loading && !this.isListing && !this.isEditor
|
||||||
},
|
},
|
||||||
breadcrumbs () {
|
breadcrumbs() {
|
||||||
let parts = this.$route.path.split('/')
|
const parts = this.$route.path.split('/')
|
||||||
|
|
||||||
if (parts[0] === '') {
|
if (parts[0] === '') {
|
||||||
parts.shift()
|
parts.shift()
|
||||||
@ -77,7 +77,7 @@ export default {
|
|||||||
parts.pop()
|
parts.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
let breadcrumbs = []
|
const breadcrumbs = []
|
||||||
|
|
||||||
for (let i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
@ -100,34 +100,34 @@ export default {
|
|||||||
return breadcrumbs
|
return breadcrumbs
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
error: null
|
error: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
this.fetchData()
|
|
||||||
},
|
|
||||||
watch: {
|
watch: {
|
||||||
'$route': 'fetchData',
|
'$route': 'fetchData',
|
||||||
'reload': function () {
|
'reload': function() {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
window.addEventListener('keydown', this.keyEvent)
|
window.addEventListener('keydown', this.keyEvent)
|
||||||
window.addEventListener('scroll', this.scroll)
|
window.addEventListener('scroll', this.scroll)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
window.removeEventListener('keydown', this.keyEvent)
|
window.removeEventListener('keydown', this.keyEvent)
|
||||||
window.removeEventListener('scroll', this.scroll)
|
window.removeEventListener('scroll', this.scroll)
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed() {
|
||||||
this.$store.commit('updateRequest', {})
|
this.$store.commit('updateRequest', {})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'setLoading' ]),
|
...mapMutations(['setLoading']),
|
||||||
async fetchData () {
|
async fetchData() {
|
||||||
// Reset view information.
|
// Reset view information.
|
||||||
this.$store.commit('setReload', false)
|
this.$store.commit('setReload', false)
|
||||||
this.$store.commit('resetSelected')
|
this.$store.commit('resetSelected')
|
||||||
@ -157,7 +157,7 @@ export default {
|
|||||||
this.setLoading(false)
|
this.setLoading(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
keyEvent (event) {
|
keyEvent(event) {
|
||||||
// Esc!
|
// Esc!
|
||||||
if (event.keyCode === 27) {
|
if (event.keyCode === 27) {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
@ -212,7 +212,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scroll () {
|
scroll() {
|
||||||
if (this.req.kind !== 'listing' || this.$store.state.user.viewMode === 'mosaic') return
|
if (this.req.kind !== 'listing' || this.$store.state.user.viewMode === 'mosaic') return
|
||||||
|
|
||||||
let top = 112 - window.scrollY
|
let top = 112 - window.scrollY
|
||||||
@ -223,10 +223,10 @@ export default {
|
|||||||
|
|
||||||
document.querySelector('#listing.list .item.header').style.top = top + 'px'
|
document.querySelector('#listing.list .item.header').style.top = top + 'px'
|
||||||
},
|
},
|
||||||
openSidebar () {
|
openSidebar() {
|
||||||
this.$store.commit('showHover', 'sidebar')
|
this.$store.commit('showHover', 'sidebar')
|
||||||
},
|
},
|
||||||
openSearch () {
|
openSearch() {
|
||||||
this.$store.commit('showHover', 'search')
|
this.$store.commit('showHover', 'search')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div id="progress">
|
<div id="progress">
|
||||||
<div v-bind:style="{ width: $store.state.progress + '%' }"></div>
|
<div :style="{ width: $store.state.progress + '%' }" />
|
||||||
</div>
|
</div>
|
||||||
<site-header></site-header>
|
<site-header />
|
||||||
<sidebar></sidebar>
|
<sidebar />
|
||||||
<main>
|
<main>
|
||||||
<router-view></router-view>
|
<router-view />
|
||||||
<shell v-if="isLogged && user.perm.execute" />
|
<shell v-if="isLogged && user.perm.execute" />
|
||||||
</main>
|
</main>
|
||||||
<prompts></prompts>
|
<prompts />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ import SiteHeader from '@/components/Header'
|
|||||||
import Shell from '@/components/Shell'
|
import Shell from '@/components/Shell'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'layout',
|
name: 'Layout',
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SiteHeader,
|
SiteHeader,
|
||||||
@ -29,11 +29,11 @@ export default {
|
|||||||
Shell
|
Shell
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([ 'isLogged' ]),
|
...mapGetters(['isLogged']),
|
||||||
...mapState([ 'user' ])
|
...mapState(['user'])
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route': function () {
|
'$route': function() {
|
||||||
this.$store.commit('resetSelected')
|
this.$store.commit('resetSelected')
|
||||||
this.$store.commit('multiple', false)
|
this.$store.commit('multiple', false)
|
||||||
if (this.$store.state.show !== 'success') this.$store.commit('closeHovers')
|
if (this.$store.state.show !== 'success') this.$store.commit('closeHovers')
|
||||||
|
|||||||
@ -5,14 +5,14 @@
|
|||||||
<h1>{{ name }}</h1>
|
<h1>{{ name }}</h1>
|
||||||
<div v-if="error !== ''" class="wrong">{{ error }}</div>
|
<div v-if="error !== ''" class="wrong">{{ error }}</div>
|
||||||
|
|
||||||
<input class="input input--block" type="text" v-model="username" :placeholder="$t('login.username')">
|
<input v-model="username" class="input input--block" type="text" :placeholder="$t('login.username')">
|
||||||
<input class="input input--block" type="password" v-model="password" :placeholder="$t('login.password')">
|
<input v-model="password" class="input input--block" type="password" :placeholder="$t('login.password')">
|
||||||
<input class="input input--block" v-if="createMode" type="password" v-model="passwordConfirm" :placeholder="$t('login.passwordConfirm')" />
|
<input v-if="createMode" v-model="passwordConfirm" class="input input--block" type="password" :placeholder="$t('login.passwordConfirm')">
|
||||||
|
|
||||||
<div v-if="recaptcha" id="recaptcha"></div>
|
<div v-if="recaptcha" id="recaptcha" />
|
||||||
<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 v-if="signup" @click="toggleMode">{{ createMode ? $t('login.loginInstead') : $t('login.createAnAccount') }}</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -22,13 +22,8 @@ import * as auth from '@/utils/auth'
|
|||||||
import { name, logoURL, recaptcha, recaptchaKey, signup } from '@/utils/constants'
|
import { name, logoURL, recaptcha, recaptchaKey, signup } from '@/utils/constants'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'login',
|
name: 'Login',
|
||||||
computed: {
|
data: function() {
|
||||||
signup: () => signup,
|
|
||||||
name: () => name,
|
|
||||||
logoURL: () => logoURL
|
|
||||||
},
|
|
||||||
data: function () {
|
|
||||||
return {
|
return {
|
||||||
createMode: false,
|
createMode: false,
|
||||||
error: '',
|
error: '',
|
||||||
@ -38,7 +33,12 @@ export default {
|
|||||||
passwordConfirm: ''
|
passwordConfirm: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
computed: {
|
||||||
|
signup: () => signup,
|
||||||
|
name: () => name,
|
||||||
|
logoURL: () => logoURL
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
if (!recaptcha) return
|
if (!recaptcha) return
|
||||||
|
|
||||||
window.grecaptcha.render('recaptcha', {
|
window.grecaptcha.render('recaptcha', {
|
||||||
@ -46,10 +46,10 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleMode () {
|
toggleMode() {
|
||||||
this.createMode = !this.createMode
|
this.createMode = !this.createMode
|
||||||
},
|
},
|
||||||
async submit (event) {
|
async submit(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ export default {
|
|||||||
await auth.login(this.username, this.password, captcha)
|
await auth.login(this.username, this.password, captcha)
|
||||||
this.$router.push({ path: redirect })
|
this.$router.push({ path: redirect })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message == 409) {
|
if (e.message === 409) {
|
||||||
this.error = this.$t('login.usernameTaken')
|
this.error = this.$t('login.usernameTaken')
|
||||||
} else {
|
} else {
|
||||||
this.error = this.$t('login.wrongCredentials')
|
this.error = this.$t('login.wrongCredentials')
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dashboard">
|
<div class="dashboard">
|
||||||
<ul id="nav" v-if="user.perm.admin">
|
<ul v-if="user.perm.admin" id="nav">
|
||||||
<li :class="{ active: $route.path === '/settings/profile' }"><router-link to="/settings/profile">{{ $t('settings.profileSettings') }}</router-link></li>
|
<li :class="{ active: $route.path === '/settings/profile' }"><router-link to="/settings/profile">{{ $t('settings.profileSettings') }}</router-link></li>
|
||||||
<li :class="{ active: $route.path === '/settings/global' }"><router-link to="/settings/global">{{ $t('settings.globalSettings') }}</router-link></li>
|
<li :class="{ active: $route.path === '/settings/global' }"><router-link to="/settings/global">{{ $t('settings.globalSettings') }}</router-link></li>
|
||||||
<li :class="{ active: $route.path === '/settings/users' }"><router-link to="/settings/users">{{ $t('settings.userManagement') }}</router-link></li>
|
<li :class="{ active: $route.path === '/settings/users' }"><router-link to="/settings/users">{{ $t('settings.userManagement') }}</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<router-view></router-view>
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'settings',
|
name: 'Settings',
|
||||||
computed: mapState([ 'user' ])
|
computed: mapState(['user'])
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="share" v-if="loaded">
|
<div v-if="loaded" class="share">
|
||||||
<a target="_blank" :href="link">
|
<a target="_blank" :href="link">
|
||||||
<div class="share__box">
|
<div class="share__box">
|
||||||
<div class="share__box__download" v-if="file.isDir">{{ $t('download.downloadFolder') }}</div>
|
<div v-if="file.isDir" class="share__box__download">{{ $t('download.downloadFolder') }}</div>
|
||||||
<div class="share__box__download" v-else>{{ $t('download.downloadFile') }}</div>
|
<div v-else class="share__box__download">{{ $t('download.downloadFile') }}</div>
|
||||||
<div class="share__box__info">
|
<div class="share__box__info">
|
||||||
<svg v-if="file.isDir" fill="#40c4ff" height="150" viewBox="0 0 24 24" width="150" xmlns="http://www.w3.org/2000/svg">
|
<svg v-if="file.isDir" fill="#40c4ff" height="150" viewBox="0 0 24 24" width="150" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/>
|
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" />
|
||||||
<path d="M0 0h24v24H0z" fill="none"/>
|
<path d="M0 0h24v24H0z" fill="none" />
|
||||||
</svg>
|
</svg>
|
||||||
<svg v-else fill="#40c4ff" height="150" viewBox="0 0 24 24" width="150" xmlns="http://www.w3.org/2000/svg">
|
<svg v-else fill="#40c4ff" height="150" viewBox="0 0 24 24" width="150" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/>
|
<path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z" />
|
||||||
<path d="M0 0h24v24H0z" fill="none"/>
|
<path d="M0 0h24v24H0z" fill="none" />
|
||||||
</svg>
|
</svg>
|
||||||
<h1 class="share__box__title">{{ file.name }}</h1>
|
<h1 class="share__box__title">{{ file.name }}</h1>
|
||||||
<qrcode-vue :value="fullLink" size="200" level="M"></qrcode-vue>
|
<qrcode-vue :value="fullLink" size="200" level="M" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@ -27,7 +27,7 @@ import { baseURL } from '@/utils/constants'
|
|||||||
import QrcodeVue from 'qrcode.vue'
|
import QrcodeVue from 'qrcode.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'share',
|
name: 'Share',
|
||||||
components: {
|
components: {
|
||||||
QrcodeVue
|
QrcodeVue
|
||||||
},
|
},
|
||||||
@ -36,25 +36,25 @@ export default {
|
|||||||
notFound: false,
|
notFound: false,
|
||||||
file: null
|
file: null
|
||||||
}),
|
}),
|
||||||
|
computed: {
|
||||||
|
hash: function() {
|
||||||
|
return this.$route.params.pathMatch
|
||||||
|
},
|
||||||
|
link: function() {
|
||||||
|
return `${baseURL}/api/public/dl/${this.hash}/${encodeURI(this.file.name)}`
|
||||||
|
},
|
||||||
|
fullLink: function() {
|
||||||
|
return window.location.origin + this.link
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route': 'fetchData'
|
'$route': 'fetchData'
|
||||||
},
|
},
|
||||||
created: function () {
|
created: function() {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
hash: function () {
|
|
||||||
return this.$route.params.pathMatch
|
|
||||||
},
|
|
||||||
link: function () {
|
|
||||||
return `${baseURL}/api/public/dl/${this.hash}/${encodeURI(this.file.name)}`
|
|
||||||
},
|
|
||||||
fullLink: function () {
|
|
||||||
return window.location.origin + this.link
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
fetchData: async function () {
|
fetchData: async function() {
|
||||||
try {
|
try {
|
||||||
this.file = await api.getHash(this.hash)
|
this.file = await api.getHash(this.hash)
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
|
|||||||
@ -8,6 +8,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {name: 'forbidden'}
|
export default { name: 'Forbidden' }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {name: 'not-found'}
|
export default { name: 'NotFound' }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {name: 'internal-error'}
|
export default { name: 'InternalError' }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dashboard" v-if="settings !== null">
|
<div v-if="settings !== null" class="dashboard">
|
||||||
<form class="card" @submit.prevent="save">
|
<form class="card" @submit.prevent="save">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('settings.globalSettings') }}</h2>
|
<h2>{{ $t('settings.globalSettings') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p><input type="checkbox" v-model="settings.signup"> {{ $t('settings.allowSignup') }}</p>
|
<p><input v-model="settings.signup" type="checkbox"> {{ $t('settings.allowSignup') }}</p>
|
||||||
|
|
||||||
<p><input type="checkbox" v-model="settings.createUserDir"> {{ $t('settings.createUserDir') }}</p>
|
<p><input v-model="settings.createUserDir" type="checkbox"> {{ $t('settings.createUserDir') }}</p>
|
||||||
|
|
||||||
<h3>{{ $t('settings.rules') }}</h3>
|
<h3>{{ $t('settings.rules') }}</h3>
|
||||||
<p class="small">{{ $t('settings.globalRules') }}</p>
|
<p class="small">{{ $t('settings.globalRules') }}</p>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<h3>{{ $t('settings.executeOnShell') }}</h3>
|
<h3>{{ $t('settings.executeOnShell') }}</h3>
|
||||||
<p class="small">{{ $t('settings.executeOnShellDescription') }}</p>
|
<p class="small">{{ $t('settings.executeOnShellDescription') }}</p>
|
||||||
<input class="input input--block" type="text" placeholder="bash -c, cmd /c, ..." v-model="settings.shell" />
|
<input v-model="settings.shell" class="input input--block" type="text" placeholder="bash -c, cmd /c, ...">
|
||||||
|
|
||||||
<h3>{{ $t('settings.branding') }}</h3>
|
<h3>{{ $t('settings.branding') }}</h3>
|
||||||
|
|
||||||
@ -25,23 +25,23 @@
|
|||||||
</i18n>
|
</i18n>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<input type="checkbox" v-model="settings.branding.disableExternal" id="branding-links" />
|
<input id="branding-links" v-model="settings.branding.disableExternal" type="checkbox">
|
||||||
{{ $t('settings.disableExternalLinks') }}
|
{{ $t('settings.disableExternalLinks') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="theme">{{ $t('settings.themes.title') }}</label>
|
<label for="theme">{{ $t('settings.themes.title') }}</label>
|
||||||
<themes class="input input--block" :theme.sync="settings.branding.theme" id="theme"></themes>
|
<themes id="theme" class="input input--block" :theme.sync="settings.branding.theme" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="branding-name">{{ $t('settings.instanceName') }}</label>
|
<label for="branding-name">{{ $t('settings.instanceName') }}</label>
|
||||||
<input class="input input--block" type="text" v-model="settings.branding.name" id="branding-name" />
|
<input id="branding-name" v-model="settings.branding.name" class="input input--block" type="text">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="branding-files">{{ $t('settings.brandingDirectoryPath') }}</label>
|
<label for="branding-files">{{ $t('settings.brandingDirectoryPath') }}</label>
|
||||||
<input class="input input--block" type="text" v-model="settings.branding.files" id="branding-files" />
|
<input id="branding-files" v-model="settings.branding.files" class="input input--block" type="text">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<div class="card-content">
|
<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 :is-new="false" :is-default="true" :user.sync="settings.defaults" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
@ -86,7 +86,7 @@
|
|||||||
<i class="material-icons">arrow_drop_down</i>
|
<i class="material-icons">arrow_drop_down</i>
|
||||||
</label>
|
</label>
|
||||||
<div class="collapse">
|
<div class="collapse">
|
||||||
<textarea class="input input--block input--textarea" v-model.trim="command.value"></textarea>
|
<textarea v-model.trim="command.value" class="input input--block input--textarea" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -106,25 +106,25 @@ import Rules from '@/components/settings/Rules'
|
|||||||
import Themes from '@/components/settings/Themes'
|
import Themes from '@/components/settings/Themes'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'settings',
|
name: 'Settings',
|
||||||
components: {
|
components: {
|
||||||
Themes,
|
Themes,
|
||||||
UserForm,
|
UserForm,
|
||||||
Rules
|
Rules
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
originalSettings: null,
|
originalSettings: null,
|
||||||
settings: null
|
settings: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user' ])
|
...mapState(['user'])
|
||||||
},
|
},
|
||||||
async created () {
|
async created() {
|
||||||
try {
|
try {
|
||||||
const original = await api.get()
|
const original = await api.get()
|
||||||
let settings = { ...original, commands: [] }
|
const settings = { ...original, commands: [] }
|
||||||
|
|
||||||
for (const key in original.commands) {
|
for (const key in original.commands) {
|
||||||
settings.commands.push({
|
settings.commands.push({
|
||||||
@ -142,9 +142,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
capitalize (name, where = '_') {
|
capitalize(name, where = '_') {
|
||||||
if (where === 'caps') where = /(?=[A-Z])/
|
if (where === 'caps') where = /(?=[A-Z])/
|
||||||
let splitted = name.split(where)
|
const splitted = name.split(where)
|
||||||
name = ''
|
name = ''
|
||||||
|
|
||||||
for (let i = 0; i < splitted.length; i++) {
|
for (let i = 0; i < splitted.length; i++) {
|
||||||
@ -153,8 +153,8 @@ export default {
|
|||||||
|
|
||||||
return name.slice(0, -1)
|
return name.slice(0, -1)
|
||||||
},
|
},
|
||||||
async save () {
|
async save() {
|
||||||
let settings = {
|
const settings = {
|
||||||
...this.settings,
|
...this.settings,
|
||||||
shell: this.settings.shell.trim().split(' ').filter(s => s !== ''),
|
shell: this.settings.shell.trim().split(' ').filter(s => s !== ''),
|
||||||
commands: {}
|
commands: {}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h3>{{ $t('settings.language') }}</h3>
|
<h3>{{ $t('settings.language') }}</h3>
|
||||||
<languages class="input input--block" :locale.sync="locale"></languages>
|
<languages class="input input--block" :locale.sync="locale" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
@ -15,14 +15,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form class="card" v-if="!user.lockPassword" @submit="updatePassword">
|
<form v-if="!user.lockPassword" class="card" @submit="updatePassword">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('settings.changePassword') }}</h2>
|
<h2>{{ $t('settings.changePassword') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<input :class="passwordClass" type="password" :placeholder="$t('settings.newPassword')" v-model="password" name="password">
|
<input v-model="password" :class="passwordClass" type="password" :placeholder="$t('settings.newPassword')" name="password">
|
||||||
<input :class="passwordClass" type="password" :placeholder="$t('settings.newPasswordConfirm')" v-model="passwordConf" name="password">
|
<input v-model="passwordConf" :class="passwordClass" type="password" :placeholder="$t('settings.newPasswordConfirm')" name="password">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
@ -38,11 +38,11 @@ import { users as api } from '@/api'
|
|||||||
import Languages from '@/components/settings/Languages'
|
import Languages from '@/components/settings/Languages'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'settings',
|
name: 'Settings',
|
||||||
components: {
|
components: {
|
||||||
Languages
|
Languages
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
password: '',
|
password: '',
|
||||||
passwordConf: '',
|
passwordConf: '',
|
||||||
@ -50,8 +50,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user' ]),
|
...mapState(['user']),
|
||||||
passwordClass () {
|
passwordClass() {
|
||||||
const baseClass = 'input input--block'
|
const baseClass = 'input input--block'
|
||||||
|
|
||||||
if (this.password === '' && this.passwordConf === '') {
|
if (this.password === '' && this.passwordConf === '') {
|
||||||
@ -65,12 +65,12 @@ export default {
|
|||||||
return `${baseClass} input--red`
|
return `${baseClass} input--red`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created() {
|
||||||
this.locale = this.user.locale
|
this.locale = this.user.locale
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'updateUser' ]),
|
...mapMutations(['updateUser']),
|
||||||
async updatePassword (event) {
|
async updatePassword(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (this.password !== this.passwordConf || this.password === '') {
|
if (this.password !== this.passwordConf || this.password === '') {
|
||||||
@ -86,7 +86,7 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateSettings (event) {
|
async updateSettings(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,27 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<form v-if="loaded" @submit="save" class="card">
|
<form v-if="loaded" class="card" @submit="save">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2 v-if="user.id === 0">{{ $t('settings.newUser') }}</h2>
|
<h2 v-if="user.id === 0">{{ $t('settings.newUser') }}</h2>
|
||||||
<h2 v-else>{{ $t('settings.user') }} {{ user.username }}</h2>
|
<h2 v-else>{{ $t('settings.user') }} {{ user.username }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<user-form :user.sync="user" :isDefault="false" :isNew="isNew" />
|
<user-form :user.sync="user" :is-default="false" :is-new="isNew" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button
|
<button
|
||||||
v-if="!isNew"
|
v-if="!isNew"
|
||||||
@click.prevent="deletePrompt"
|
|
||||||
type="button"
|
type="button"
|
||||||
class="button button--flat button--red"
|
class="button button--flat button--red"
|
||||||
:aria-label="$t('buttons.delete')"
|
:aria-label="$t('buttons.delete')"
|
||||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
:title="$t('buttons.delete')"
|
||||||
|
@click.prevent="deletePrompt"
|
||||||
|
>{{ $t('buttons.delete') }}</button>
|
||||||
<input
|
<input
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
type="submit"
|
type="submit"
|
||||||
:value="$t('buttons.save')">
|
:value="$t('buttons.save')"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@ -31,15 +33,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
@click="closeHovers"
|
|
||||||
v-focus
|
v-focus
|
||||||
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">
|
:title="$t('buttons.cancel')"
|
||||||
|
@click="closeHovers"
|
||||||
|
>
|
||||||
{{ $t('buttons.cancel') }}
|
{{ $t('buttons.cancel') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="button button--flat"
|
<button
|
||||||
@click="deleteUser">
|
class="button button--flat"
|
||||||
|
@click="deleteUser"
|
||||||
|
>
|
||||||
{{ $t('buttons.delete') }}
|
{{ $t('buttons.delete') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -54,7 +60,7 @@ import UserForm from '@/components/settings/UserForm'
|
|||||||
import deepClone from 'lodash.clonedeep'
|
import deepClone from 'lodash.clonedeep'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'user',
|
name: 'User',
|
||||||
components: {
|
components: {
|
||||||
UserForm
|
UserForm
|
||||||
},
|
},
|
||||||
@ -65,27 +71,27 @@ export default {
|
|||||||
loaded: false
|
loaded: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
this.fetchData()
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
isNew () {
|
isNew() {
|
||||||
return this.$route.path === '/settings/users/new'
|
return this.$route.path === '/settings/users/new'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route': 'fetchData',
|
'$route': 'fetchData',
|
||||||
'user.perm.admin': function () {
|
'user.perm.admin': function() {
|
||||||
if (!this.user.perm.admin) return
|
if (!this.user.perm.admin) return
|
||||||
this.user.lockPassword = false
|
this.user.lockPassword = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'closeHovers', 'showHover', 'setUser' ]),
|
...mapMutations(['closeHovers', 'showHover', 'setUser']),
|
||||||
async fetchData () {
|
async fetchData() {
|
||||||
try {
|
try {
|
||||||
if (this.isNew) {
|
if (this.isNew) {
|
||||||
let { defaults } = await settings.get()
|
const { defaults } = await settings.get()
|
||||||
this.user = {
|
this.user = {
|
||||||
...defaults,
|
...defaults,
|
||||||
username: '',
|
username: '',
|
||||||
@ -104,10 +110,10 @@ export default {
|
|||||||
this.$router.push({ path: '/settings/users/new' })
|
this.$router.push({ path: '/settings/users/new' })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deletePrompt () {
|
deletePrompt() {
|
||||||
this.showHover('deleteUser')
|
this.showHover('deleteUser')
|
||||||
},
|
},
|
||||||
async deleteUser (event) {
|
async deleteUser(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -118,9 +124,9 @@ export default {
|
|||||||
this.$showError(e)
|
this.$showError(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async save (event) {
|
async save(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
let user = {
|
const user = {
|
||||||
...this.originalUser,
|
...this.originalUser,
|
||||||
...this.user
|
...this.user
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<th>{{ $t('settings.username') }}</th>
|
<th>{{ $t('settings.username') }}</th>
|
||||||
<th>{{ $t('settings.admin') }}</th>
|
<th>{{ $t('settings.admin') }}</th>
|
||||||
<th>{{ $t('settings.scope') }}</th>
|
<th>{{ $t('settings.scope') }}</th>
|
||||||
<th></th>
|
<th />
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr v-for="user in users" :key="user.id">
|
<tr v-for="user in users" :key="user.id">
|
||||||
@ -31,13 +31,13 @@
|
|||||||
import { users as api } from '@/api'
|
import { users as api } from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'users',
|
name: 'Users',
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
users: []
|
users: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created() {
|
||||||
try {
|
try {
|
||||||
this.users = await api.getAll()
|
this.users = await api.getAll()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/caddyserver/caddy v1.0.3
|
github.com/caddyserver/caddy v1.0.3
|
||||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/dsnet/compress v0.0.1 // indirect
|
github.com/dsnet/compress v0.0.1 // indirect
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
@ -29,6 +30,7 @@ require (
|
|||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
go.etcd.io/bbolt v1.3.3
|
go.etcd.io/bbolt v1.3.3
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
|
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
|
||||||
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect
|
||||||
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect
|
golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect
|
golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect
|
||||||
golang.org/x/text v0.3.2 // indirect
|
golang.org/x/text v0.3.2 // indirect
|
||||||
|
|||||||
5
go.sum
5
go.sum
@ -43,6 +43,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||||
@ -239,6 +241,9 @@ golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
||||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||||
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8=
|
||||||
|
|||||||
@ -59,6 +59,7 @@ func NewHandler(store *storage.Storage, server *settings.Server) (http.Handler,
|
|||||||
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
|
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
|
||||||
|
|
||||||
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
|
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
|
||||||
|
api.PathPrefix("/thumbnail").Handler(monkey(thumbnailHandler, "/api/thumbnail")).Methods("GET")
|
||||||
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
|
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
|
||||||
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
||||||
|
|
||||||
|
|||||||
92
http/thumbnail.go
Normal file
92
http/thumbnail.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/errors"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/files"
|
||||||
|
"image"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var thumbnailHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
if !d.user.Perm.Download {
|
||||||
|
return http.StatusAccepted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := files.NewFileInfo(files.FileOptions{
|
||||||
|
Fs: d.user.Fs,
|
||||||
|
Path: r.URL.Path,
|
||||||
|
Modify: d.user.Perm.Modify,
|
||||||
|
Expand: true,
|
||||||
|
Checker: d,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.IsDir || file.Type != "image" {
|
||||||
|
return http.StatusNotFound, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return thumbnailFileHandler(w, r, file)
|
||||||
|
})
|
||||||
|
|
||||||
|
func thumbnailFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo) (int, error) {
|
||||||
|
fd, err := file.Fs.Open(file.Path)
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
if r.URL.Query().Get("inline") == "true" {
|
||||||
|
w.Header().Set("Content-Disposition", "inline")
|
||||||
|
} else {
|
||||||
|
// As per RFC6266 section 4.3
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(file.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
srcImg, err := imaging.Decode(fd, imaging.AutoOrientation(true))
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
dstImg := fitResizeImage(srcImg)
|
||||||
|
w.Header().Add("Content-Type", mime.TypeByExtension(file.Extension))
|
||||||
|
err = func() error {
|
||||||
|
switch file.Extension {
|
||||||
|
case ".jpg", ".jpeg":
|
||||||
|
return jpeg.Encode(w, dstImg, nil)
|
||||||
|
case ".png":
|
||||||
|
return png.Encode(w, dstImg)
|
||||||
|
case ".gif":
|
||||||
|
return gif.Encode(w, dstImg, nil)
|
||||||
|
default:
|
||||||
|
return errors.ErrNotExist
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSize = 1080
|
||||||
|
|
||||||
|
func fitResizeImage(srcImage image.Image) image.Image {
|
||||||
|
width := srcImage.Bounds().Dx()
|
||||||
|
height := srcImage.Bounds().Dy()
|
||||||
|
if width > maxSize && width > height {
|
||||||
|
width = maxSize
|
||||||
|
height = 0
|
||||||
|
} else if height > maxSize && height > width {
|
||||||
|
width = 0
|
||||||
|
height = maxSize
|
||||||
|
} else {
|
||||||
|
return srcImage
|
||||||
|
}
|
||||||
|
return imaging.Resize(srcImage, width, height, imaging.NearestNeighbor)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user