Update On Fri Mar 6 20:05:19 CET 2026

This commit is contained in:
github-action[bot]
2026-03-06 20:05:19 +01:00
parent 485ff49495
commit a8d097aeb5
134 changed files with 3133 additions and 1332 deletions
+12
View File
@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
## [2.61.2](https://github.com/filebrowser/filebrowser/compare/v2.61.1...v2.61.2) (2026-03-06)
### Bug Fixes
* added dateFormat to getUserDefaults so this is respected in the … ([#5804](https://github.com/filebrowser/filebrowser/issues/5804)) ([8598db2](https://github.com/filebrowser/filebrowser/commit/8598db2accccf5b87353e5e718b2ad1c946e5c44))
* avoid sending the same name in the file/folder rename modal ([#5806](https://github.com/filebrowser/filebrowser/issues/5806)) ([d7b00ce](https://github.com/filebrowser/filebrowser/commit/d7b00ce5f672b7ce0b26ce31abdfc74f8b00b939))
* **csv-viewer:** add support for missing text encodings in dropdown list ([#5795](https://github.com/filebrowser/filebrowser/issues/5795)) ([4af3f85](https://github.com/filebrowser/filebrowser/commit/4af3f85e64e795e8ae1d87d4caee8185028294ac))
* **frontend:** do not delete original assets ([4d9e6b8](https://github.com/filebrowser/filebrowser/commit/4d9e6b821852203cef67233791a922013bd5b64d))
* **frontend:** input password type ([8ee5576](https://github.com/filebrowser/filebrowser/commit/8ee55761a1aa9bc091d8466c44f03c2043a8ca79))
* validate current password with a modal ([#5805](https://github.com/filebrowser/filebrowser/issues/5805)) ([177c7cf](https://github.com/filebrowser/filebrowser/commit/177c7cfcce36779e2c5ebaa4b59a055dd1e17648))
## [2.61.1](https://github.com/filebrowser/filebrowser/compare/v2.61.0...v2.61.1) (2026-03-04)
+2
View File
@@ -168,6 +168,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
Sorting: a.Settings.Defaults.Sorting,
Perm: a.Settings.Defaults.Perm,
Commands: a.Settings.Defaults.Commands,
DateFormat: a.Settings.Defaults.DateFormat,
HideDotfiles: a.Settings.Defaults.HideDotfiles,
}
u = a.GetUser(d)
@@ -233,6 +234,7 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
By: a.Fields.GetString("user.sorting.by", d.Sorting.By),
},
Commands: a.Fields.GetArray("user.commands", d.Commands),
DateFormat: a.Fields.GetBoolean("user.dateFormat", d.DateFormat),
HideDotfiles: a.Fields.GetBoolean("user.hideDotfiles", d.HideDotfiles),
Perm: perms,
LockPassword: true,
+2 -1
View File
@@ -99,7 +99,7 @@ func getProxyAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (a
return nil, err
}
if header == "" && defaultAuther != nil {
if header == "" && defaultAuther != nil {
header = defaultAuther["header"].(string)
}
@@ -236,6 +236,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintln(w, "\nDefaults:")
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
fmt.Fprintf(w, "\tDateFormat:\t%t\n", set.Defaults.DateFormat)
fmt.Fprintf(w, "\tHideDotfiles:\t%t\n", set.Defaults.HideDotfiles)
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
+2
View File
@@ -138,6 +138,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
defaults.Sorting.By, err = flags.GetString(flag.Name)
case "sorting.asc":
defaults.Sorting.Asc, err = flags.GetBool(flag.Name)
case "dateFormat":
defaults.DateFormat, err = flags.GetBool(flag.Name)
case "hideDotfiles":
defaults.HideDotfiles, err = flags.GetBool(flag.Name)
}
+1 -1
View File
@@ -58,7 +58,7 @@
"@vitejs/plugin-vue": "^6.0.1",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/tsconfig": "^0.8.1",
"@vue/tsconfig": "^0.9.0",
"autoprefixer": "^10.4.21",
"eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.5",
+44 -43
View File
@@ -28,7 +28,7 @@ importers:
version: 1.11.19
dompurify:
specifier: ^3.2.6
version: 3.3.1
version: 3.3.2
epubjs:
specifier: ^0.3.93
version: 0.3.93
@@ -46,10 +46,10 @@ importers:
version: 4.17.23
marked:
specifier: ^17.0.0
version: 17.0.3
version: 17.0.4
marked-katex-extension:
specifier: ^5.1.6
version: 5.1.7(katex@0.16.28)(marked@17.0.3)
version: 5.1.7(katex@0.16.28)(marked@17.0.4)
material-icons:
specifier: ^1.13.14
version: 1.13.14
@@ -110,16 +110,16 @@ importers:
version: 4.17.12
'@types/node':
specifier: ^24.10.1
version: 24.11.0
version: 24.12.0
'@typescript-eslint/eslint-plugin':
specifier: ^8.37.0
version: 8.56.1(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(typescript@5.9.3)
'@vitejs/plugin-legacy':
specifier: ^7.2.1
version: 7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2))
version: 7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2))
'@vitejs/plugin-vue':
specifier: ^6.0.1
version: 6.0.4(vite@7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))
version: 6.0.4(vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))
'@vue/eslint-config-prettier':
specifier: ^10.2.0
version: 10.2.0(eslint@10.0.2)(prettier@3.8.1)
@@ -127,11 +127,11 @@ importers:
specifier: ^14.6.0
version: 14.7.0(eslint-plugin-vue@10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2)))(eslint@10.0.2)(typescript@5.9.3)
'@vue/tsconfig':
specifier: ^0.8.1
version: 0.8.1(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3))
specifier: ^0.9.0
version: 0.9.0(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3))
autoprefixer:
specifier: ^10.4.21
version: 10.4.27(postcss@8.5.6)
version: 10.4.27(postcss@8.5.8)
eslint:
specifier: ^10.0.0
version: 10.0.2
@@ -146,7 +146,7 @@ importers:
version: 10.8.0(@typescript-eslint/parser@8.56.0(eslint@10.0.2)(typescript@5.9.3))(eslint@10.0.2)(vue-eslint-parser@10.4.0(eslint@10.0.2))
postcss:
specifier: ^8.5.6
version: 8.5.6
version: 8.5.8
prettier:
specifier: ^3.6.2
version: 3.8.1
@@ -158,10 +158,10 @@ importers:
version: 5.9.3
vite:
specifier: ^7.2.2
version: 7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2)
version: 7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2)
vite-plugin-compression2:
specifier: ^2.3.1
version: 2.4.0(rollup@4.57.1)
version: 2.5.0(rollup@4.57.1)
vue-tsc:
specifier: ^3.1.3
version: 3.2.5(typescript@5.9.3)
@@ -1288,8 +1288,8 @@ packages:
'@types/lodash@4.17.23':
resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==}
'@types/node@24.11.0':
resolution: {integrity: sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==}
'@types/node@24.12.0':
resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -1538,8 +1538,8 @@ packages:
'@vue/shared@3.5.29':
resolution: {integrity: sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==}
'@vue/tsconfig@0.8.1':
resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==}
'@vue/tsconfig@0.9.0':
resolution: {integrity: sha512-RP+v9Cpbsk1ZVXltCHHkYBr7+624x6gcijJXVjIcsYk7JXqvIpRtMwU2ARLvWDhmy9ffdFYxhsfJnPztADBohQ==}
peerDependencies:
typescript: 5.x
vue: ^3.4.0
@@ -1800,8 +1800,9 @@ packages:
dom-walk@0.1.2:
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
dompurify@3.3.1:
resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==}
dompurify@3.3.2:
resolution: {integrity: sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==}
engines: {node: '>=20'}
electron-to-chromium@1.5.286:
resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==}
@@ -2205,8 +2206,8 @@ packages:
katex: '>=0.16 <0.17'
marked: '>=4 <18'
marked@17.0.3:
resolution: {integrity: sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A==}
marked@17.0.4:
resolution: {integrity: sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==}
engines: {node: '>= 20'}
hasBin: true
@@ -2358,8 +2359,8 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
postcss@8.5.6:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1:
@@ -2648,8 +2649,8 @@ packages:
videojs-vtt.js@0.15.5:
resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==}
vite-plugin-compression2@2.4.0:
resolution: {integrity: sha512-8J4CBF1+dM1I06azba/eXJuJHinLF0Am7lUvRH8AZpu0otJoBaDEnxrIEr5iPZJSwH0AEglJGYCveh7pN52jCg==}
vite-plugin-compression2@2.5.0:
resolution: {integrity: sha512-bHxtBibPxxSn5eZSe0IAzvYucP/hg8Bz8ppjbH7lndU5kIHT+92qTkB4z9xWYfnyV0YHuir1SjOuyO0fzU4Vgg==}
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
@@ -3835,7 +3836,7 @@ snapshots:
'@types/lodash@4.17.23': {}
'@types/node@24.11.0':
'@types/node@24.12.0':
dependencies:
undici-types: 7.16.0
@@ -4036,7 +4037,7 @@ snapshots:
global: 4.4.0
is-function: 1.0.2
'@vitejs/plugin-legacy@7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2))':
'@vitejs/plugin-legacy@7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0)
@@ -4051,14 +4052,14 @@ snapshots:
regenerator-runtime: 0.14.1
systemjs: 6.15.1
terser: 5.46.0
vite: 7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2)
vite: 7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))':
'@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
vite: 7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2)
vite: 7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2)
vue: 3.5.29(typescript@5.9.3)
'@volar/language-core@2.4.28':
@@ -4118,7 +4119,7 @@ snapshots:
'@vue/shared': 3.5.28
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.6
postcss: 8.5.8
source-map-js: 1.2.1
'@vue/compiler-sfc@3.5.29':
@@ -4130,7 +4131,7 @@ snapshots:
'@vue/shared': 3.5.29
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.6
postcss: 8.5.8
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.28':
@@ -4239,7 +4240,7 @@ snapshots:
'@vue/shared@3.5.29': {}
'@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3))':
'@vue/tsconfig@0.9.0(typescript@5.9.3)(vue@3.5.29(typescript@5.9.3))':
optionalDependencies:
typescript: 5.9.3
vue: 3.5.29(typescript@5.9.3)
@@ -4306,13 +4307,13 @@ snapshots:
'@babel/parser': 7.29.0
ast-kit: 2.2.0
autoprefixer@10.4.27(postcss@8.5.6):
autoprefixer@10.4.27(postcss@8.5.8):
dependencies:
browserslist: 4.28.1
caniuse-lite: 1.0.30001774
fraction.js: 5.3.4
picocolors: 1.1.1
postcss: 8.5.6
postcss: 8.5.8
postcss-value-parser: 4.2.0
babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.29.0):
@@ -4448,7 +4449,7 @@ snapshots:
dom-walk@0.1.2: {}
dompurify@3.3.1:
dompurify@3.3.2:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -4902,12 +4903,12 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
marked-katex-extension@5.1.7(katex@0.16.28)(marked@17.0.3):
marked-katex-extension@5.1.7(katex@0.16.28)(marked@17.0.4):
dependencies:
katex: 0.16.28
marked: 17.0.3
marked: 17.0.4
marked@17.0.3: {}
marked@17.0.4: {}
marks-pane@1.0.9: {}
@@ -5044,7 +5045,7 @@ snapshots:
postcss-value-parser@4.2.0: {}
postcss@8.5.6:
postcss@8.5.8:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
@@ -5352,23 +5353,23 @@ snapshots:
dependencies:
global: 4.4.0
vite-plugin-compression2@2.4.0(rollup@4.57.1):
vite-plugin-compression2@2.5.0(rollup@4.57.1):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.57.1)
tar-mini: 0.2.0
transitivePeerDependencies:
- rollup
vite@7.3.1(@types/node@24.11.0)(terser@5.46.0)(yaml@2.8.2):
vite@7.3.1(@types/node@24.12.0)(terser@5.46.0)(yaml@2.8.2):
dependencies:
esbuild: 0.27.3
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
postcss: 8.5.8
rollup: 4.57.1
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.11.0
'@types/node': 24.12.0
fsevents: 2.3.3
terser: 5.46.0
yaml: 2.8.2
@@ -0,0 +1,142 @@
<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { vClickOutside } from "@/utils/index";
const props = withDefaults(
defineProps<{
position?: "top-left" | "bottom-left" | "bottom-right" | "top-right";
closeOnClick?: boolean;
}>(),
{
position: "bottom-right",
closeOnClick: true,
}
);
const isOpen = defineModel<boolean>();
const triggerRef = ref<HTMLElement | null>(null);
const listRef = ref<HTMLElement | null>(null);
const dropdownStyle = ref<Record<string, string>>({});
const updatePosition = () => {
if (!isOpen.value || !triggerRef.value) return;
const rect = triggerRef.value.getBoundingClientRect();
dropdownStyle.value = {
position: "fixed",
top: props.position.includes("bottom")
? `${rect.bottom + 2}px`
: `${rect.top}px`,
left: props.position.includes("left") ? `${rect.left}px` : "auto",
right: props.position.includes("right")
? `${window.innerWidth - rect.right}px`
: "auto",
zIndex: "11000",
};
};
watch(isOpen, (open) => {
if (open) {
updatePosition();
}
});
const onWindowChange = () => {
updatePosition();
};
const closeDropdown = (e: Event) => {
if (
e.target instanceof HTMLElement &&
listRef.value?.contains(e.target) &&
!props.closeOnClick
) {
return;
}
isOpen.value = false;
};
onMounted(() => {
window.addEventListener("resize", onWindowChange);
window.addEventListener("scroll", onWindowChange, true);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", onWindowChange);
window.removeEventListener("scroll", onWindowChange, true);
});
</script>
<script lang="ts">
export default {
directives: {
clickOutside: vClickOutside,
},
};
</script>
<template>
<div
class="dropdown-modal-container"
v-click-outside="closeDropdown"
ref="triggerRef"
>
<button @click="isOpen = !isOpen" class="dropdown-modal-trigger">
<slot></slot>
<i class="material-icons">chevron_right</i>
</button>
<teleport to="body">
<div
ref="listRef"
class="dropdown-modal-list"
:class="{ 'dropdown-modal-open': isOpen }"
:style="dropdownStyle"
>
<div>
<slot name="list"></slot>
</div>
</div>
</teleport>
</div>
</template>
<style scoped>
.dropdown-modal-trigger {
background: var(--surfacePrimary);
color: var(--textSecondary);
border: 1px solid var(--borderPrimary);
border-radius: 0.1em;
padding: 0.5em 1em;
transition: 0.2s ease all;
margin: 0;
display: flex;
justify-content: space-between;
gap: 1rem;
align-items: center;
}
.dropdown-modal-trigger > i {
transform: rotate(90deg);
}
.dropdown-modal-list {
padding: 0.25rem;
background-color: var(--surfacePrimary);
color: var(--textSecondary);
display: none;
border: 1px solid var(--borderPrimary);
border-radius: 0.1em;
}
.dropdown-modal-list > div {
max-height: 450px;
padding: 0.25rem;
}
.dropdown-modal-open {
display: block;
}
</style>
@@ -21,19 +21,37 @@
</div>
<div class="header-select" v-if="isEncodedContent">
<label for="fileEncoding">{{ $t("files.fileEncoding") }}</label>
<select
id="fileEncoding"
class="input input--block"
v-model="selectedEncoding"
<DropdownModal
v-model="isEncondingDropdownOpen"
:close-on-click="false"
>
<option
v-for="encoding in availableEncodings"
:value="encoding"
:key="encoding"
>
{{ encoding }}
</option>
</select>
<div>
<span class="selected-encoding">{{ selectedEncoding }}</span>
</div>
<template v-slot:list>
<input
v-model="encodingSearch"
:placeholder="$t('search.search')"
class="input input--block"
name="encoding"
/>
<div class="encoding-list">
<div v-if="encodingList.length == 0" class="message">
<i class="material-icons">sentiment_dissatisfied</i>
<span>{{ $t("files.lonely") }}</span>
</div>
<button
v-for="encoding in encodingList"
:value="encoding"
:key="encoding"
class="encoding-button"
@click="selectedEncoding = encoding"
>
{{ encoding }}
</button>
</div>
</template>
</DropdownModal>
</div>
</div>
<div v-if="displayError" class="csv-error">
@@ -74,10 +92,11 @@
</template>
<script setup lang="ts">
import { computed, ref, watchEffect } from "vue";
import { computed, ref, watch, watchEffect } from "vue";
import { parse } from "csv-parse/browser/esm";
import { useI18n } from "vue-i18n";
import { availableEncodings, decode } from "@/utils/encodings";
import DropdownModal from "../DropdownModal.vue";
const { t } = useI18n({});
@@ -90,6 +109,16 @@ const props = withDefaults(defineProps<Props>(), {
error: "",
});
const isEncondingDropdownOpen = ref(false);
const encodingSearch = ref<string>("");
const encodingList = computed(() => {
return availableEncodings.filter((e) =>
e.toLowerCase().includes(encodingSearch.value.toLowerCase())
);
});
const columnSeparator = ref([",", ";"]);
const selectedEncoding = ref("utf-8");
@@ -128,6 +157,11 @@ watchEffect(() => {
);
}
});
watch(selectedEncoding, () => {
isEncondingDropdownOpen.value = false;
encodingSearch.value = "";
});
</script>
<style scoped>
@@ -271,14 +305,21 @@ watchEffect(() => {
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
flex-direction: column;
@media (width >= 640px) {
flex-direction: row;
}
}
.header-select > label {
font-size: small;
max-width: 80px;
@media (width >= 640px) {
max-width: 70px;
}
}
.header-select > select {
.header-select > select,
.header-select > div {
margin-bottom: 0;
}
@@ -286,4 +327,40 @@ watchEffect(() => {
font-size: 1.2rem;
color: var(--blue);
}
.encoding-list {
max-height: 300px;
min-width: 120px;
overflow: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
touch-action: pan-y;
}
.encoding-button {
background-color: transparent;
border: none;
outline: none;
padding: 0.25rem 0.5rem;
color: var(--textPrimary);
text-align: left;
cursor: pointer;
border-radius: 0.2rem;
white-space: nowrap;
display: block;
width: 100%;
}
.encoding-button:hover {
background-color: var(--surfaceSecondary);
}
.selected-encoding {
white-space: nowrap;
text-overflow: ellipsis;
}
.message {
font-size: 1.25em;
}
</style>
@@ -0,0 +1,58 @@
<template>
<div class="card floating">
<div class="card-title">
<h2>{{ $t("prompts.currentPassword") }}</h2>
</div>
<div class="card-content">
<p>
{{ $t("prompts.currentPasswordMessage") }}
</p>
<input
id="focus-prompt"
class="input input--block"
type="password"
@keyup.enter="submit"
v-model="password"
/>
</div>
<div class="card-action">
<button
class="button button--flat button--grey"
@click="cancel"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')"
>
{{ $t("buttons.cancel") }}
</button>
<button
@click="submit"
class="button button--flat"
type="submit"
:aria-label="$t('buttons.ok')"
:title="$t('buttons.ok')"
>
{{ $t("buttons.ok") }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useLayoutStore } from "@/stores/layout";
const layoutStore = useLayoutStore();
const { currentPrompt } = layoutStore;
const password = ref("");
const submit = (event: Event) => {
currentPrompt?.confirm(event, password.value);
};
const cancel = () => {
layoutStore.closeHovers();
};
</script>
@@ -28,6 +28,7 @@ import ShareDelete from "./ShareDelete.vue";
import Upload from "./Upload.vue";
import DiscardEditorChanges from "./DiscardEditorChanges.vue";
import ResolveConflict from "./ResolveConflict.vue";
import CurrentPassword from "./CurrentPassword.vue";
const layoutStore = useLayoutStore();
@@ -50,6 +51,7 @@ const components = new Map<string, any>([
["deleteUser", DeleteUser],
["discardEditorChanges", DiscardEditorChanges],
["resolve-conflict", ResolveConflict],
["current-password", CurrentPassword],
]);
const modal = computed(() => {
@@ -6,7 +6,7 @@
<div class="card-content">
<p>
{{ $t("prompts.renameMessage") }} <code>{{ oldName() }}</code
{{ $t("prompts.renameMessage") }} <code>{{ oldName }}</code
>:
</p>
<input
@@ -33,6 +33,7 @@
type="submit"
:aria-label="$t('buttons.rename')"
:title="$t('buttons.rename')"
:disabled="name === '' || name === oldName"
>
{{ $t("buttons.rename") }}
</button>
@@ -56,7 +57,7 @@ export default {
};
},
created() {
this.name = this.oldName();
this.name = this.oldName;
},
inject: ["$showError"],
computed: {
@@ -67,25 +68,28 @@ export default {
"isListing",
]),
...mapWritableState(useFileStore, ["reload", "preselect"]),
},
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
cancel: function () {
this.closeHovers();
},
oldName: function () {
oldName() {
if (!this.isListing) {
return this.req.name;
}
if (this.selectedCount === 0 || this.selectedCount > 1) {
// This shouldn't happen.
return;
return "";
}
return this.req.items[this.selected[0]].name;
},
},
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
cancel: function () {
this.closeHovers();
},
submit: async function () {
if (this.name === "" || this.name === this.oldName) {
return;
}
let oldLink = "";
let newLink = "";
+3 -1
View File
@@ -175,7 +175,9 @@
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
"forbiddenError": "Forbidden Error",
"currentPassword": "Your password",
"currentPasswordMessage": "Enter your password to validate this action."
},
"search": {
"images": "Images",
+14 -14
View File
@@ -49,10 +49,10 @@
"editAsText": "텍스트로 편집",
"increaseFontSize": "글꼴 크기 증가",
"decreaseFontSize": "글꼴 크기 감소",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
"overrideAll": "대상 폴더의 모든 파일을 대체",
"skipAll": "충돌하는 모든 파일을 건너뛰기",
"renameAll": "모든 파일의 이름 변경 (복사본 생성)",
"singleDecision": "충돌하는 각 파일을 확인"
},
"download": {
"downloadFile": "파일 다운로드",
@@ -166,16 +166,16 @@
"optionalPassword": "비밀번호 (선택)",
"resolution": "해상도",
"discardEditorChanges": "변경 사항을 취소하시겠습니까?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
"replaceOrSkip": "파일 대체 또는 건너뛰기",
"resolveConflict": "어떤 파일을 유지하시겠습니까?",
"singleConflictResolve": "모든 버전을 선택하면 복사한 파일의 이름에 번호를 추가합니다.",
"fastConflictResolve": "대상 폴더에 같은 이름을 가진 파일이 {count}개 있습니다.",
"uploadingFiles": "파일 업로드 중",
"filesInOrigin": "원본 파일",
"filesInDest": "대상 경로의 파일",
"override": "덮어쓰기",
"skip": "건너뛰기",
"forbiddenError": "권한 오류"
},
"search": {
"images": "이미지",
+4
View File
@@ -10,4 +10,8 @@ declare global {
// TODO: no idea what the exact type is
__vue__: any;
}
interface HTMLElement {
clickOutsideEvent?: (event: Event) => void;
}
}
+173
View File
@@ -1,4 +1,5 @@
export const availableEncodings = [
// encodings
"utf-8",
"ibm866",
"iso-8859-2",
@@ -37,6 +38,178 @@ export const availableEncodings = [
"euc-kr",
"utf-16be",
"utf-16le",
// label encodings
"unicode-1-1-utf-8",
"utf8",
"866",
"cp866",
"csibm866",
"csisolatin2",
"iso-ir-101",
"iso8859-2",
"iso88592",
"iso_8859-2",
"iso_8859-2:1987",
"l2",
"latin2",
"csisolatin3",
"iso-ir-109",
"iso8859-3",
"iso88593",
"iso_8859-3",
"iso_8859-3:1988",
"l3",
"latin3",
"csisolatin4",
"iso-ir-110",
"iso8859-4",
"iso88594",
"iso_8859-4",
"iso_8859-4:1988",
"l4",
"latin4",
"csisolatincyrillic",
"cyrillic",
"iso-ir-144",
"iso88595",
"iso_8859-5",
"iso_8859-5:1988",
"arabic",
"asmo-708",
"csiso88596e",
"csiso88596i",
"csisolatinarabic",
"ecma-114",
"iso-8859-6-e",
"iso-8859-6-i",
"iso-ir-127",
"iso8859-6",
"iso88596",
"iso_8859-6",
"iso_8859-6:1987",
"csisolatingreek",
"ecma-118",
"elot_928",
"greek",
"greek8",
"iso-ir-126",
"iso8859-7",
"iso88597",
"iso_8859-7",
"iso_8859-7:1987",
"sun_eu_greek",
"csiso88598e",
"csisolatinhebrew",
"hebrew",
"iso-8859-8-e",
"iso-ir-138",
"iso8859-8",
"iso88598",
"iso_8859-8",
"iso_8859-8:1988",
"visual",
"csiso88598i",
"logical",
"csisolatin6",
"iso-ir-157",
"iso8859-10",
"iso885910",
"l6",
"latin6",
"iso8859-13",
"iso885913",
"iso8859-14",
"iso885914",
"csisolatin9",
"iso8859-15",
"iso885915",
"l9",
"latin9",
"cskoi8r",
"koi",
"koi8",
"koi8_r",
"csmacintosh",
"mac",
"x-mac-roman",
"dos-874",
"iso-8859-11",
"iso8859-11",
"iso885911",
"tis-620",
"cp1250",
"x-cp1250",
"cp1251",
"x-cp1251",
"ansi_x3.4-1968",
"ascii",
"cp1252",
"cp819",
"csisolatin1",
"ibm819",
"iso-8859-1",
"iso-ir-100",
"iso8859-1",
"iso88591",
"iso_8859-1",
"iso_8859-1:1987",
"l1",
"latin1",
"us-ascii",
"x-cp1252",
"cp1253",
"x-cp1253",
"cp1254",
"csisolatin5",
"iso-8859-9",
"iso-ir-148",
"iso8859-9",
"iso88599",
"iso_8859-9",
"iso_8859-9:1989",
"l5",
"latin5",
"x-cp1254",
"cp1255",
"x-cp1255",
"cp1256",
"x-cp1256",
"cp1257",
"x-cp1257",
"cp1258",
"x-cp1258",
"x-mac-ukrainian",
"chinese",
"csgb2312",
"csiso58gb231280",
"gb2312",
"gb_2312",
"gb_2312-80",
"iso-ir-58",
"x-gbk",
"big5-hkscs",
"cn-big5",
"csbig5",
"x-x-big5",
"cseucpkdfmtjapanese",
"x-euc-jp",
"csiso2022jp",
"csshiftjis",
"ms_kanji",
"shift-jis",
"sjis",
"windows-31j",
"x-sjis",
"cseuckr",
"csksc56011987",
"iso-ir-149",
"korean",
"ks_c_5601-1987",
"ks_c_5601-1989",
"ksc5601",
"ksc_5601",
"windows-949",
"utf-16",
];
export function decode(content: ArrayBuffer, encoding: string): string {
+22
View File
@@ -4,3 +4,25 @@ import { partial } from "filesize";
* Formats filesize as KiB/MiB/...
*/
export const filesize = partial({ base: 2 });
export const vClickOutside = {
created(el: HTMLElement, binding: any) {
el.clickOutsideEvent = (event: Event) => {
const target = event.target;
if (target instanceof Node) {
if (!el.contains(target)) {
binding.value(event);
}
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted(el: HTMLElement) {
if (el.clickOutsideEvent) {
document.removeEventListener("click", el.clickOutsideEvent);
}
},
};
@@ -311,7 +311,11 @@ const isPdf = computed(() => fileStore.req?.extension.toLowerCase() == ".pdf");
const isEpub = computed(
() => fileStore.req?.extension.toLowerCase() == ".epub"
);
const isCsv = computed(() => fileStore.req?.extension.toLowerCase() == ".csv");
const isCsv = computed(
() =>
fileStore.req?.extension.toLowerCase() == ".csv" &&
fileStore.req.size <= CSV_MAX_SIZE
);
const isResizeEnabled = computed(() => resizePreview);
@@ -15,19 +15,6 @@
:isDefault="false"
:isNew="isNew"
/>
<p v-if="isCurrentPasswordRequired">
<label for="currentPassword">{{
t("settings.currentPassword")
}}</label>
<input
class="input input--block"
type="password"
v-model="currentPassword"
id="currentPassword"
autocomplete="current-password"
/>
</p>
</div>
<div class="card-action">
@@ -77,7 +64,6 @@ const error = ref<StatusError>();
const originalUser = ref<IUser>();
const user = ref<IUser>();
const createUserDir = ref<boolean>(false);
const currentPassword = ref<string>("");
const isCurrentPasswordRequired = ref<boolean>(false);
const $showError = inject<IToastError>("$showError")!;
@@ -134,16 +120,30 @@ const fetchData = async () => {
}
};
const deletePrompt = () =>
layoutStore.showHover({ prompt: "deleteUser", confirm: deleteUser });
const deletePrompt = () => {
if (isCurrentPasswordRequired.value) {
layoutStore.showHover({
prompt: "current-password",
confirm: (event: Event, currentPassword: string) => {
event.preventDefault();
layoutStore.closeHovers();
deleteUser(currentPassword);
},
});
} else {
layoutStore.showHover({
prompt: "deleteUser",
confirm: () => deleteUser(""),
});
}
};
const deleteUser = async (e: Event) => {
e.preventDefault();
const deleteUser = async (currentPassword: string) => {
if (!user.value) {
return false;
}
try {
await api.remove(user.value.id, currentPassword.value);
await api.remove(user.value.id, currentPassword);
router.push({ path: "/settings/users" });
$showSuccess(t("settings.userDeleted"));
} catch (err) {
@@ -157,8 +157,25 @@ const deleteUser = async (e: Event) => {
return true;
};
const save = async (event: Event) => {
const save = (event: Event) => {
event.preventDefault();
if (isCurrentPasswordRequired.value) {
layoutStore.showHover({
prompt: "current-password",
confirm: (event: Event, currentPassword: string) => {
event.preventDefault();
layoutStore.closeHovers();
send(currentPassword);
},
});
} else {
send("");
}
return true;
};
const send = async (currentPassword: string) => {
if (!user.value) {
return false;
}
@@ -170,11 +187,11 @@ const save = async (event: Event) => {
...user.value,
};
const loc = await api.create(newUser, currentPassword.value);
const loc = await api.create(newUser, currentPassword);
router.push({ path: loc || "/settings/users" });
$showSuccess(t("settings.userCreated"));
} else {
await api.update(user.value, ["all"], currentPassword.value);
await api.update(user.value, ["all"], currentPassword);
if (user.value.id === authStore.user?.id) {
authStore.updateUser(user.value);
@@ -185,7 +202,5 @@ const save = async (event: Event) => {
} catch (e: any) {
$showError(e);
}
return true;
};
</script>
+1 -1
View File
@@ -14,7 +14,7 @@ const plugins = [
// defaults already drop IE support
targets: ["defaults"],
}),
compression({ include: /\.js$/i, deleteOriginalAssets: true }),
compression({ include: /\.js$/, deleteOriginalAssets: false }),
];
const resolve = {
+3 -3
View File
@@ -17,8 +17,8 @@ require (
github.com/mholt/archives v0.1.5
github.com/mitchellh/go-homedir v1.1.0
github.com/redis/go-redis/v9 v9.18.0
github.com/samber/lo v1.52.0
github.com/shirou/gopsutil/v4 v4.26.1
github.com/samber/lo v1.53.0
github.com/shirou/gopsutil/v4 v4.26.2
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
@@ -47,7 +47,7 @@ require (
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
+6 -6
View File
@@ -83,8 +83,8 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV
github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU=
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw=
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
@@ -215,10 +215,10 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo=
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
+1 -1
View File
@@ -43,7 +43,7 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
return renderJSON(w, r, file)
} else if encoding == "true" {
if file.Type != "text" {
return http.StatusUnsupportedMediaType, fmt.Errorf("file is not a text file")
return renderJSON(w, r, file)
}
f, err := d.user.Fs.Open(r.URL.Path)