Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5e0c4e2e1 | ||
|
|
ca019ae7d9 | ||
|
|
4488f5b131 | ||
|
|
166583db63 | ||
|
|
85b7d2762d | ||
|
|
4edabb9ccc | ||
|
|
103683069e | ||
|
|
5328e80d2e | ||
|
|
847d08bdd1 | ||
|
|
0231b7ebdf | ||
|
|
e07c59df0b | ||
|
|
0d3eb9bea9 | ||
|
|
34ae34e764 | ||
|
|
ca0108f070 | ||
|
|
a1e442ef9e | ||
|
|
6ad8160aa3 | ||
|
|
22b848f26e | ||
|
|
a418dd6bb3 | ||
|
|
d978d1ecca | ||
|
|
9cc18a81e3 | ||
|
|
e38c28273a | ||
|
|
da2dff0933 | ||
|
|
261bca67bc | ||
|
|
ca5e249e3c | ||
|
|
f4e148523e | ||
|
|
1f22fe65ec | ||
|
|
e2bdf6f747 | ||
|
|
d236f1c563 | ||
|
|
4edf425a9f | ||
|
|
a1a7ac4f04 | ||
|
|
74917c8037 | ||
|
|
7bd27f5e82 | ||
|
|
9f4288b1b4 | ||
|
|
41b801d30c | ||
|
|
da6b7ac26b | ||
|
|
7f8b920aa4 | ||
|
|
dd53644acb | ||
|
|
9b80a9aa6c | ||
|
|
0321415a15 | ||
|
|
23e84c9974 | ||
|
|
0fadf28b18 | ||
|
|
871f337892 | ||
|
|
29c73eaca6 | ||
|
|
f13c7c8cff | ||
|
|
1e03feadb5 | ||
|
|
8adf127c7d | ||
|
|
7dbf7a3528 | ||
|
|
65a837de49 | ||
|
|
876cdb3426 | ||
|
|
2f805de527 | ||
|
|
7a16129bfc | ||
|
|
0f39bd055e | ||
|
|
860c19ddf5 | ||
|
|
a8fc1657b7 | ||
|
|
c406bda0c7 | ||
|
|
d9f9460c1e | ||
|
|
126227bb27 | ||
|
|
b6a4fb1f27 | ||
|
|
2368e468b9 | ||
|
|
8d8cd26d7f | ||
|
|
8f81b77cf2 | ||
|
|
0616f6811f | ||
|
|
432f3e60ff | ||
|
|
ae72f93667 | ||
|
|
4812536555 | ||
|
|
6d44b3ae1d | ||
|
|
811cf2dfe5 | ||
|
|
b5f970731b | ||
|
|
fc80f4f44c | ||
|
|
6aea227617 | ||
|
|
09a26166b4 | ||
|
|
a63573b67e | ||
|
|
4bd7d69c82 | ||
|
|
c21af0791a | ||
|
|
858eb42651 | ||
|
|
21709428b1 | ||
|
|
846fb3333a | ||
|
|
6f772f2b10 | ||
|
|
3c5d36673f | ||
|
|
27afbb8813 | ||
|
|
79875bac7f | ||
|
|
be8ba18937 | ||
|
|
ef2e9992dc | ||
|
|
0542fc0ba4 | ||
|
|
f04af0cac6 | ||
|
|
f5f8b60b33 | ||
|
|
6dcef07f40 | ||
|
|
6700a981a5 | ||
|
|
490e5bbf99 | ||
|
|
df63cb595c | ||
|
|
3cb21c727d | ||
|
|
c01b6a840c | ||
|
|
cbcf708d94 | ||
|
|
4d9e6b8218 | ||
|
|
8ee55761a1 | ||
|
|
8598db2acc | ||
|
|
a7dc7bf88a | ||
|
|
9f56826fb2 | ||
|
|
d7b00ce5f6 | ||
|
|
177c7cfcce | ||
|
|
4af3f85e64 | ||
|
|
c950a57df8 | ||
|
|
7ed1425115 | ||
|
|
148b3c5942 | ||
|
|
a40f80672d | ||
|
|
8dc618a24b | ||
|
|
31194fb57a | ||
|
|
aa809096eb | ||
|
|
e3d00d591b | ||
|
|
7b16e2de90 | ||
|
|
2470b9eb69 | ||
|
|
9940bdd663 | ||
|
|
200d501547 | ||
|
|
3169a14a4d | ||
|
|
7e78ad97ad | ||
|
|
0467326d5c | ||
|
|
63a76ef18c | ||
|
|
3cad9a2b0e | ||
|
|
804b14b698 | ||
|
|
95e6ed75a7 | ||
|
|
b09960e538 | ||
|
|
5e8f5be245 | ||
|
|
e5bc0d3cce | ||
|
|
f67bccf8c5 | ||
|
|
88b97def9e | ||
|
|
1f7904dad2 | ||
|
|
5feaf66575 | ||
|
|
c94870fcfe | ||
|
|
6a76dfeba9 |
@@ -10,7 +10,7 @@
|
||||
|
||||
Before submitting your PR, please indicate which issues are either fixed or closed by this PR. See [GitHub Help: Closing issues using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/).
|
||||
|
||||
- [ ] I am aware the project is currently in maintenance-only mode. See [README](https://github.com/filebrowser/community/blob/master/README.md)
|
||||
- [ ] I am aware the project is currently in **maintenance-only** mode and new feature requests won't be accepted. See [README](https://github.com/filebrowser/community/blob/master/README.md)
|
||||
- [ ] I am aware that translations MUST be made through [Transifex](https://app.transifex.com/file-browser/file-browser/) and that this PR is NOT a translation update
|
||||
- [ ] I am making a PR against the `master` branch.
|
||||
- [ ] I am sure File Browser can be successfully built. See [builds](https://github.com/filebrowser/community/blob/master/builds.md) and [development](https://github.com/filebrowser/community/blob/master/development.md).
|
||||
|
||||
+31
-13
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v6
|
||||
@@ -27,6 +27,24 @@ jobs:
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm run lint
|
||||
|
||||
test-frontend:
|
||||
name: Test Frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24.x"
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
- working-directory: frontend
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm run test
|
||||
|
||||
lint-backend:
|
||||
name: Lint Backend
|
||||
runs-on: ubuntu-latest
|
||||
@@ -34,7 +52,7 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.x"
|
||||
go-version: "1.26.x"
|
||||
- uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: "latest"
|
||||
@@ -46,7 +64,7 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.x"
|
||||
go-version: "1.26.x"
|
||||
- run: go test --race ./...
|
||||
|
||||
build:
|
||||
@@ -58,8 +76,8 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25'
|
||||
- uses: pnpm/action-setup@v4
|
||||
go-version: '1.26'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v6
|
||||
@@ -68,7 +86,7 @@ jobs:
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
uses: go-task/setup-task@v2
|
||||
- run: task build
|
||||
|
||||
release:
|
||||
@@ -82,8 +100,8 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25'
|
||||
- uses: pnpm/action-setup@v4
|
||||
go-version: '1.26'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v6
|
||||
@@ -92,19 +110,19 @@ jobs:
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
uses: go-task/setup-task@v2
|
||||
- run: task build:frontend
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
uses: goreleaser/goreleaser-action@v7
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean
|
||||
|
||||
@@ -18,9 +18,9 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
uses: go-task/setup-task@v2
|
||||
- name: Build site
|
||||
run: task docs
|
||||
|
||||
@@ -39,14 +39,14 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
uses: go-task/setup-task@v2
|
||||
- name: Build site
|
||||
run: task docs
|
||||
- name: Upload static files as artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
path: www/public
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v5
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: marocchino/sticky-pull-request-comment@v2
|
||||
- uses: marocchino/sticky-pull-request-comment@v3
|
||||
# When the previous steps fails, the workflow would stop. By adding this
|
||||
# condition you can continue the execution with the populated error message.
|
||||
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
# Delete a previous comment when the issue has been resolved
|
||||
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
uses: marocchino/sticky-pull-request-comment@v3
|
||||
with:
|
||||
header: pr-title-lint-error
|
||||
delete: true
|
||||
|
||||
+193
@@ -2,6 +2,199 @@
|
||||
|
||||
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.63.8](https://github.com/filebrowser/filebrowser/compare/v2.63.7...v2.63.8) (2026-06-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check if share is within scope when creating ([ca019ae](https://github.com/filebrowser/filebrowser/commit/ca019ae7d966a7c28de2b2341271cd13e3458ae6))
|
||||
|
||||
## [2.63.7](https://github.com/filebrowser/filebrowser/compare/v2.63.6...v2.63.7) (2026-06-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disallow shares for non-existent paths ([166583d](https://github.com/filebrowser/filebrowser/commit/166583db632e088e9f0adce30aec43bb9d9019f4))
|
||||
|
||||
## [2.63.6](https://github.com/filebrowser/filebrowser/compare/v2.63.5...v2.63.6) (2026-06-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* address three security disclosures (archive traversal, login DoS, symlink escape) ([847d08b](https://github.com/filebrowser/filebrowser/commit/847d08bdd135e5c3659f2e6dea2f0cd36617af9b))
|
||||
* cross-user unauthorized share-link deletion ([0231b7e](https://github.com/filebrowser/filebrowser/commit/0231b7ebdfbe77a6c54027d30c4856c3fd81ee4d))
|
||||
* incorrect access control in public directory shares via rule path rebasing ([e07c59d](https://github.com/filebrowser/filebrowser/commit/e07c59df0b850f5924d5b1683e8609661ddcf534))
|
||||
* parse csv files with uneven columns in their rows ([#5965](https://github.com/filebrowser/filebrowser/issues/5965)) ([5328e80](https://github.com/filebrowser/filebrowser/commit/5328e80d2e88d1c279a1250a7dfee4fc96f703ec))
|
||||
* remove undocumented hook auth with shell replacement ([34ae34e](https://github.com/filebrowser/filebrowser/commit/34ae34e764d72540c039f1f5ea2ec4c974168c1f))
|
||||
|
||||
## [2.63.5](https://github.com/filebrowser/filebrowser/compare/v2.63.4...v2.63.5) (2026-05-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **router:** handle undefined catchAll param on root redirect ([#5955](https://github.com/filebrowser/filebrowser/issues/5955)) ([6ad8160](https://github.com/filebrowser/filebrowser/commit/6ad8160aa3309314c1b471c5090b67c824464396))
|
||||
|
||||
## [2.63.4](https://github.com/filebrowser/filebrowser/compare/v2.63.3...v2.63.4) (2026-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* show item shares from all users to admins ([#5941](https://github.com/filebrowser/filebrowser/issues/5941)) ([9cc18a8](https://github.com/filebrowser/filebrowser/commit/9cc18a81e3e1b8bf96795bfbe3d83ced294ecfd7))
|
||||
|
||||
## [2.63.3](https://github.com/filebrowser/filebrowser/compare/v2.63.2...v2.63.3) (2026-05-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correct environment variable in compose.yaml ([#5910](https://github.com/filebrowser/filebrowser/issues/5910)) ([41b801d](https://github.com/filebrowser/filebrowser/commit/41b801d30c736c8ca863e2be6aece7d99e92129e))
|
||||
* Fix conflict modal and add a resume transfert option ([#5884](https://github.com/filebrowser/filebrowser/issues/5884)) ([f4e1485](https://github.com/filebrowser/filebrowser/commit/f4e148523e0dc9242081831b53544396f995c611))
|
||||
|
||||
## [2.63.2](https://github.com/filebrowser/filebrowser/compare/v2.63.1...v2.63.2) (2026-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **preview:** let arrow keys seek video instead of switching files ([#5895](https://github.com/filebrowser/filebrowser/issues/5895)) ([0fadf28](https://github.com/filebrowser/filebrowser/commit/0fadf28b18e506ddca0027e83ebe567ac57932bf))
|
||||
|
||||
## [2.63.1](https://github.com/filebrowser/filebrowser/compare/v2.63.0...v2.63.1) (2026-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check download permission in resource handler ([#5891](https://github.com/filebrowser/filebrowser/issues/5891)) ([1e03fea](https://github.com/filebrowser/filebrowser/commit/1e03feadb550e4414b5589a6a8df57f538efba15))
|
||||
* check share owner permissions on public share access ([#5888](https://github.com/filebrowser/filebrowser/issues/5888)) ([7dbf7a3](https://github.com/filebrowser/filebrowser/commit/7dbf7a3528234b2a9ee9c4115e8ecf58d258ca51))
|
||||
* enforce directory boundary in rule path matching ([#5889](https://github.com/filebrowser/filebrowser/issues/5889)) ([8adf127](https://github.com/filebrowser/filebrowser/commit/8adf127c7d33585333b8030869f6f318e6517179))
|
||||
* restrict default permissions for proxy-auth auto-provisioned users ([#5890](https://github.com/filebrowser/filebrowser/issues/5890)) ([f13c7c8](https://github.com/filebrowser/filebrowser/commit/f13c7c8cffd6d58ff29c4a6763ced1385f69961e))
|
||||
|
||||
## [2.63.0](https://github.com/filebrowser/filebrowser/compare/v2.62.2...v2.63.0) (2026-04-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* enable copy operation on drag‑and‑drop with ctrl key ([#5882](https://github.com/filebrowser/filebrowser/issues/5882)) ([876cdb3](https://github.com/filebrowser/filebrowser/commit/876cdb34265b090c2a74a69509f4106f2c5e8726))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check download permission when sharing permission is enabled ([#5875](https://github.com/filebrowser/filebrowser/issues/5875)) ([0f39bd0](https://github.com/filebrowser/filebrowser/commit/0f39bd055efdadc15abd2f8146cf5da3793f8318))
|
||||
* **tus:** reject negative upload-length to prevent inconsistent cache entry ([#5876](https://github.com/filebrowser/filebrowser/issues/5876)) ([7a16129](https://github.com/filebrowser/filebrowser/commit/7a16129bfc07dbdc2fa52b99d2985c1bc0ea12e2))
|
||||
|
||||
## [2.62.2](https://github.com/filebrowser/filebrowser/compare/v2.62.1...v2.62.2) (2026-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable scripted content in epub ([126227b](https://github.com/filebrowser/filebrowser/commit/126227bb2754eee15cd7c722916c3bb8821084a2))
|
||||
* double slash in TUS upload path when readEntries returns multiple batches ([#5848](https://github.com/filebrowser/filebrowser/issues/5848)) ([432f3e6](https://github.com/filebrowser/filebrowser/commit/432f3e60ffdf92af6f8f56119a1bac8084f52a60))
|
||||
* include filename in Content-Disposition header for inline downloads ([#5860](https://github.com/filebrowser/filebrowser/issues/5860)) ([8f81b77](https://github.com/filebrowser/filebrowser/commit/8f81b77cf2a3da0a445f3700fbf4a0091ea46c07))
|
||||
* json escaping ([c406bda](https://github.com/filebrowser/filebrowser/commit/c406bda0c73ac8b187e23a97c05521edc77efa84))
|
||||
* self-registered users don't get execute perms ([b6a4fb1](https://github.com/filebrowser/filebrowser/commit/b6a4fb1f27f4d894b384c0f3acacda276d1338a5))
|
||||
* shares listing ([a8fc165](https://github.com/filebrowser/filebrowser/commit/a8fc1657b796c5da7190466beff13e680721b6d3))
|
||||
* touch Redis upload cache key on GetLength to prevent TTL expiry ([#5850](https://github.com/filebrowser/filebrowser/issues/5850)) ([4812536](https://github.com/filebrowser/filebrowser/commit/48125365551ce2b27790aaafd7594cf5ce52f1ba))
|
||||
* use html/template ([d9f9460](https://github.com/filebrowser/filebrowser/commit/d9f9460c1e51d10a25065e10358c12d5ced66ad9))
|
||||
|
||||
## [2.62.1](https://github.com/filebrowser/filebrowser/compare/v2.62.0...v2.62.1) (2026-03-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* base url/reverse proxy redirect ([fc80f4f](https://github.com/filebrowser/filebrowser/commit/fc80f4f44c856ddc19df3024c245990fffd55630))
|
||||
|
||||
## [2.62.0](https://github.com/filebrowser/filebrowser/compare/v2.61.2...v2.62.0) (2026-03-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Updates for project File Browser ([#5807](https://github.com/filebrowser/filebrowser/issues/5807)) ([858eb42](https://github.com/filebrowser/filebrowser/commit/858eb426515ec55172e9cca47bdf1e25a0d0d81d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow deleting the user's own account ([#5820](https://github.com/filebrowser/filebrowser/issues/5820)) ([f04af0c](https://github.com/filebrowser/filebrowser/commit/f04af0cac6c808b8e7c9a9651380c252c4de9132))
|
||||
* around languages ([c21af07](https://github.com/filebrowser/filebrowser/commit/c21af0791a5df458c2ddb81ce9ae44b772b6d82d))
|
||||
* clean path in patch handler ([4bd7d69](https://github.com/filebrowser/filebrowser/commit/4bd7d69c82163b201a987e99c0c50d7ecc6ee5f1))
|
||||
* make perm.share depend on share.download ([09a2616](https://github.com/filebrowser/filebrowser/commit/09a26166b4f79446e7174c017380f6db45444e32))
|
||||
* properly surface config parse errors ([#5822](https://github.com/filebrowser/filebrowser/issues/5822)) ([ef2e999](https://github.com/filebrowser/filebrowser/commit/ef2e9992dc3098f6c4722c2a98966cd8abf8bab5))
|
||||
* signup handler shouldn't create admins ([a63573b](https://github.com/filebrowser/filebrowser/commit/a63573b67eb302167b4c4f218361a2d0c138deab))
|
||||
* **tus:** preserve percent-encoded upload paths in Location header ([#5817](https://github.com/filebrowser/filebrowser/issues/5817)) ([0542fc0](https://github.com/filebrowser/filebrowser/commit/0542fc0ba43740c967414eebd156bac86ad80376))
|
||||
* **upload:** avoid skipping whole folder upload on conflict modal ([#5814](https://github.com/filebrowser/filebrowser/issues/5814)) ([f5f8b60](https://github.com/filebrowser/filebrowser/commit/f5f8b60b331a07729a1fed1ed065cb6fc20930ea))
|
||||
* **upload:** don't mark every folder-upload file as conflicting ([#5813](https://github.com/filebrowser/filebrowser/issues/5813)) ([6dcef07](https://github.com/filebrowser/filebrowser/commit/6dcef07f40d550acee63dd01e0a3bcf78532f690))
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* check for correct permission in TUS Delete ([7ed1425](https://github.com/filebrowser/filebrowser/commit/7ed1425115be602c2b23236c410098ea2d74b42f))
|
||||
|
||||
## [2.61.0](https://github.com/filebrowser/filebrowser/compare/v2.60.0...v2.61.0) (2026-02-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* improved conflict resolution when uploading/copying/moving files ([#5765](https://github.com/filebrowser/filebrowser/issues/5765)) ([aa80909](https://github.com/filebrowser/filebrowser/commit/aa809096eb35fdfbdeb6784b1ebfe2ca1e42f52b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correctly clean path ([31194fb](https://github.com/filebrowser/filebrowser/commit/31194fb57a5b92e7155219d7ec7273028fcb2e83))
|
||||
|
||||
## [2.60.0](https://github.com/filebrowser/filebrowser/compare/v2.59.0...v2.60.0) (2026-02-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Updates for project File Browser ([#5764](https://github.com/filebrowser/filebrowser/issues/5764)) ([9940bdd](https://github.com/filebrowser/filebrowser/commit/9940bdd663ff5141110778524b8a22c957036e78))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* always show separators and encoding list in the CSV viewer ([#5774](https://github.com/filebrowser/filebrowser/issues/5774)) ([3169a14](https://github.com/filebrowser/filebrowser/commit/3169a14a4d63a0a11a5288f4f3a674c0a0edb972))
|
||||
* modal lifecycle issues, multiple modals, new directory creation and discard changes behavior ([#5773](https://github.com/filebrowser/filebrowser/issues/5773)) ([200d501](https://github.com/filebrowser/filebrowser/commit/200d5015472c79d5caa683ea291ebf500356a39f))
|
||||
|
||||
## [2.59.0](https://github.com/filebrowser/filebrowser/compare/v2.58.0...v2.59.0) (2026-02-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add 'Open direct' button to images ([#5678](https://github.com/filebrowser/filebrowser/issues/5678)) ([804b14b](https://github.com/filebrowser/filebrowser/commit/804b14b698aa218fa5c2aaba687e72c5f7617f0f))
|
||||
* Updates for project File Browser ([#5760](https://github.com/filebrowser/filebrowser/issues/5760)) ([63a76ef](https://github.com/filebrowser/filebrowser/commit/63a76ef18c51121e08634810a894c1e22a870428))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* render equations in markdown preview ([#5745](https://github.com/filebrowser/filebrowser/issues/5745)) ([0467326](https://github.com/filebrowser/filebrowser/commit/0467326d5c082c42c0ede88ee2d3472f5fb65600))
|
||||
|
||||
## [2.58.0](https://github.com/filebrowser/filebrowser/compare/v2.57.1...v2.58.0) (2026-02-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* nederlands ([88b97de](https://github.com/filebrowser/filebrowser/commit/88b97def9ee72fe6e8094209aebb71830b7305be))
|
||||
* support for multiple encodings in CSV files ([#5756](https://github.com/filebrowser/filebrowser/issues/5756)) ([f67bccf](https://github.com/filebrowser/filebrowser/commit/f67bccf8c5470cb280fe854d92aa2666c270bcf5))
|
||||
* Updates for project File Browser ([#5749](https://github.com/filebrowser/filebrowser/issues/5749)) ([c94870f](https://github.com/filebrowser/filebrowser/commit/c94870fcfe1b4acb2db9ab897b9f7d35e3b75770))
|
||||
* Updates for project File Browser ([#5759](https://github.com/filebrowser/filebrowser/issues/5759)) ([5e8f5be](https://github.com/filebrowser/filebrowser/commit/5e8f5be245fd0126545ef5ca61c2d428ac128ad5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** pnpm lock ([b09960e](https://github.com/filebrowser/filebrowser/commit/b09960e538387ff29371c80be1584720f65181e7))
|
||||
* ignore version.go ([1f7904d](https://github.com/filebrowser/filebrowser/commit/1f7904dad21a87f04e1543ee10b60ce79e5eebe9))
|
||||
* respect Accept-Encoding for pre-compressed JS ([#5750](https://github.com/filebrowser/filebrowser/issues/5750)) ([6a76dfe](https://github.com/filebrowser/filebrowser/commit/6a76dfeba9254a938e320928c67d110f73f83715))
|
||||
* wrap response text in Error before reject ([#5753](https://github.com/filebrowser/filebrowser/issues/5753)) ([e5bc0d3](https://github.com/filebrowser/filebrowser/commit/e5bc0d3cce18fa7b069688b176b99efbb67382d2))
|
||||
|
||||
## [2.57.1](https://github.com/filebrowser/filebrowser/compare/v2.57.0...v2.57.1) (2026-02-08)
|
||||
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@ This project is a finished product which fulfills its goal: be a single binary w
|
||||
|
||||
- It can take a while until someone gets back to you. Please be patient.
|
||||
- [Issues](https://github.com/filebrowser/filebrowser/issues) are meant to track bugs. Unrelated issues will be converted into [discussions](https://github.com/filebrowser/filebrowser/discussions).
|
||||
- No new features will be implemented by maintainers. Pull requests for new features will be reviewed on a case by case basis.
|
||||
- The priority is triaging issues, addressing security issues and reviewing pull requests meant to solve bugs.
|
||||
- No new features are planned. Pull requests for new features are not guaranteed to be reviewed.
|
||||
|
||||
Please read [@hacdias' personal reflection](https://hacdias.com/2026/03/11/filebrowser/) on the project status.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
+2
-16
@@ -86,22 +86,6 @@ func (a *HookAuth) LoginPage() bool {
|
||||
// RunCommand starts the hook command and returns the action
|
||||
func (a *HookAuth) RunCommand() (string, error) {
|
||||
command := strings.Split(a.Command, " ")
|
||||
envMapping := func(key string) string {
|
||||
switch key {
|
||||
case "USERNAME":
|
||||
return a.Cred.Username
|
||||
case "PASSWORD":
|
||||
return a.Cred.Password
|
||||
default:
|
||||
return os.Getenv(key)
|
||||
}
|
||||
}
|
||||
for i, arg := range command {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
command[i] = os.Expand(arg, envMapping)
|
||||
}
|
||||
|
||||
cmd := exec.Command(command[0], command[1:]...)
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("USERNAME=%s", a.Cred.Username))
|
||||
@@ -168,6 +152,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 +218,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,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// writeHookScript writes a POSIX shell script to a temp file and returns its
|
||||
// path, marking it executable.
|
||||
func writeHookScript(t *testing.T, body string) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), "hook.sh")
|
||||
if err := os.WriteFile(path, []byte("#!/bin/sh\n"+body), 0o700); err != nil {
|
||||
t.Fatalf("failed to write hook script: %v", err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// TestRunCommandNoCredentialInjection ensures that attacker-controlled
|
||||
// credentials submitted at the unauthenticated login endpoint cannot be
|
||||
// injected into the hook command string. Credentials must only ever reach the
|
||||
// hook through the USERNAME/PASSWORD environment variables, never via string
|
||||
// substitution into the command itself (CWE-78/CWE-88).
|
||||
func TestRunCommandNoCredentialInjection(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("uses POSIX shell")
|
||||
}
|
||||
|
||||
marker := filepath.Join(t.TempDir(), "pwned")
|
||||
|
||||
// The hook simply blocks. If the credential were ever interpolated into the
|
||||
// command string and evaluated by a shell, the embedded `touch` would
|
||||
// create the marker file.
|
||||
script := writeHookScript(t, "echo hook.action=block\n")
|
||||
|
||||
a := &HookAuth{
|
||||
Command: script,
|
||||
Cred: hookCred{
|
||||
Username: `"; touch ` + marker + `; #`,
|
||||
Password: `$(touch ` + marker + `)`,
|
||||
},
|
||||
}
|
||||
|
||||
action, err := a.RunCommand()
|
||||
if err != nil {
|
||||
t.Fatalf("RunCommand returned error: %v", err)
|
||||
}
|
||||
if action != "block" {
|
||||
t.Fatalf("expected action %q, got %q", "block", action)
|
||||
}
|
||||
if _, err := os.Stat(marker); err == nil {
|
||||
t.Fatalf("credential injection executed: marker file %q was created", marker)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRunCommandReceivesCredentialsViaEnv verifies the supported contract: the
|
||||
// hook receives credentials through the USERNAME and PASSWORD environment
|
||||
// variables.
|
||||
func TestRunCommandReceivesCredentialsViaEnv(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("uses POSIX shell")
|
||||
}
|
||||
|
||||
script := writeHookScript(t, `if [ "$USERNAME" = alice ] && [ "$PASSWORD" = secret ]; then
|
||||
echo hook.action=auth
|
||||
else
|
||||
echo hook.action=block
|
||||
fi
|
||||
`)
|
||||
|
||||
a := &HookAuth{
|
||||
Command: script,
|
||||
Cred: hookCred{
|
||||
Username: "alice",
|
||||
Password: "secret",
|
||||
},
|
||||
}
|
||||
|
||||
action, err := a.RunCommand()
|
||||
if err != nil {
|
||||
t.Fatalf("RunCommand returned error: %v", err)
|
||||
}
|
||||
if action != "auth" {
|
||||
t.Fatalf("expected action %q, got %q", "auth", action)
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,9 @@ func (a ProxyAuth) createUser(usr users.Store, setting *settings.Settings, srv *
|
||||
LockPassword: true,
|
||||
}
|
||||
setting.Defaults.Apply(user)
|
||||
user.Perm.Admin = false
|
||||
user.Perm.Execute = false
|
||||
user.Commands = []string{}
|
||||
|
||||
var userHome string
|
||||
userHome, err = setting.MakeUserDir(user.Username, user.Scope, srv.Root)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/settings"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
)
|
||||
|
||||
type mockUserStore struct {
|
||||
users map[string]*users.User
|
||||
}
|
||||
|
||||
func (m *mockUserStore) Get(_ string, id interface{}) (*users.User, error) {
|
||||
if v, ok := id.(string); ok {
|
||||
if u, ok := m.users[v]; ok {
|
||||
return u, nil
|
||||
}
|
||||
}
|
||||
return nil, fberrors.ErrNotExist
|
||||
}
|
||||
|
||||
func (m *mockUserStore) Gets(_ string) ([]*users.User, error) { return nil, nil }
|
||||
func (m *mockUserStore) Update(_ *users.User, _ ...string) error { return nil }
|
||||
func (m *mockUserStore) Save(user *users.User) error {
|
||||
m.users[user.Username] = user
|
||||
return nil
|
||||
}
|
||||
func (m *mockUserStore) Delete(_ interface{}) error { return nil }
|
||||
func (m *mockUserStore) LastUpdate(_ uint) int64 { return 0 }
|
||||
|
||||
func TestProxyAuthCreateUserRestrictsDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
store := &mockUserStore{users: make(map[string]*users.User)}
|
||||
srv := &settings.Server{Root: t.TempDir()}
|
||||
|
||||
s := &settings.Settings{
|
||||
Key: []byte("key"),
|
||||
AuthMethod: MethodProxyAuth,
|
||||
Defaults: settings.UserDefaults{
|
||||
Perm: users.Permissions{
|
||||
Admin: true,
|
||||
Execute: true,
|
||||
Create: true,
|
||||
Rename: true,
|
||||
Modify: true,
|
||||
Delete: true,
|
||||
Share: true,
|
||||
Download: true,
|
||||
},
|
||||
Commands: []string{"git", "ls", "cat", "id"},
|
||||
},
|
||||
}
|
||||
|
||||
auth := ProxyAuth{Header: "X-Remote-User"}
|
||||
req, _ := http.NewRequest(http.MethodGet, "/", http.NoBody)
|
||||
req.Header.Set("X-Remote-User", "newproxyuser")
|
||||
|
||||
user, err := auth.Auth(req, store, s, srv)
|
||||
if err != nil {
|
||||
t.Fatalf("Auth() error: %v", err)
|
||||
}
|
||||
|
||||
if user.Perm.Admin {
|
||||
t.Error("auto-provisioned proxy user should not have Admin permission")
|
||||
}
|
||||
if user.Perm.Execute {
|
||||
t.Error("auto-provisioned proxy user should not have Execute permission")
|
||||
}
|
||||
if len(user.Commands) != 0 {
|
||||
t.Errorf("auto-provisioned proxy user should have empty Commands, got %v", user.Commands)
|
||||
}
|
||||
if !user.Perm.Create {
|
||||
t.Error("auto-provisioned proxy user should retain Create permission from defaults")
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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)
|
||||
|
||||
+3
-1
@@ -80,7 +80,7 @@ func addUserFlags(flags *pflag.FlagSet) {
|
||||
flags.Bool("singleClick", false, "use single clicks only")
|
||||
flags.Bool("redirectAfterCopyMove", false, "redirect to destination after copy/move")
|
||||
flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)")
|
||||
flags.Bool("hideDotfiles", false, "hide dotfiles")
|
||||
flags.Bool("hideDotfiles", false, "hide dotfiles in file listings")
|
||||
flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+2
-1
@@ -118,7 +118,8 @@ func initViper(cmd *cobra.Command) (*viper.Viper, error) {
|
||||
|
||||
// Read in configuration
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
if errors.Is(err, viper.ConfigParseError{}) {
|
||||
|
||||
if errors.As(err, &viper.ConfigParseError{}) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Run using:
|
||||
# docker compose -f compose.yaml -f compose.redis.yaml up --build
|
||||
|
||||
services:
|
||||
redis:
|
||||
container_name: redis
|
||||
image: redis:latest
|
||||
networks:
|
||||
- filebrowser
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
cat > /tmp/users.acl <<EOF
|
||||
user default on >filebrowser ~* +@all
|
||||
EOF
|
||||
redis-server --aclfile /tmp/users.acl
|
||||
+2
-7
@@ -1,12 +1,7 @@
|
||||
# Run using:
|
||||
# docker compose up --build
|
||||
|
||||
services:
|
||||
filebrowser:
|
||||
container_name: filebrowser
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
image: filebrowser/filebrowser:latest
|
||||
networks:
|
||||
- filebrowser
|
||||
ports:
|
||||
@@ -14,7 +9,7 @@ services:
|
||||
volumes:
|
||||
- filebrowser:/flux/vault
|
||||
environment:
|
||||
- REDIS_CACHE_URL=redis://default:filebrowser@redis:6379 # Use rediss:// for ssl
|
||||
- FB_REDIS_CACHE_URL=redis://default:filebrowser@redis:6379 # Use rediss:// for ssl
|
||||
|
||||
redis:
|
||||
container_name: redis
|
||||
|
||||
+2
-1
@@ -21,8 +21,9 @@ var (
|
||||
ErrPermissionDenied = errors.New("permission denied")
|
||||
ErrInvalidRequestParams = errors.New("invalid request params")
|
||||
ErrSourceIsParent = errors.New("source is parent")
|
||||
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
|
||||
ErrRootUserDeletion = errors.New("the sole admin can't be deleted")
|
||||
ErrCurrentPasswordIncorrect = errors.New("the current password is incorrect")
|
||||
ErrShareRequiresDownload = errors.New("permission to share requires permission to download")
|
||||
)
|
||||
|
||||
type ErrShortPassword struct {
|
||||
|
||||
@@ -133,6 +133,17 @@ func stat(opts *FileOptions) (*FileInfo, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// The path is a symlink. Refuse to follow it if its on-disk target escapes
|
||||
// the user's scoped root; otherwise a symlink that lives lexically inside
|
||||
// the scope but points outside it would let a restricted user read, write,
|
||||
// or share files beyond their boundary.
|
||||
if file != nil && file.IsSymlink {
|
||||
ok, scopeErr := WithinScope(opts.Fs, opts.Path)
|
||||
if scopeErr != nil || !ok {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
}
|
||||
|
||||
// fs doesn't support afero.Lstater interface or the file is a symlink
|
||||
info, err := opts.Fs.Stat(opts.Path)
|
||||
if err != nil {
|
||||
@@ -165,6 +176,50 @@ func stat(opts *FileOptions) (*FileInfo, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// WithinScope reports whether the on-disk target of p — after resolving any
|
||||
// symbolic links — stays within the scoped root of fsys. It exists to stop a
|
||||
// symlink that lives lexically inside a user's scope but points outside it
|
||||
// from being followed for reads, writes, or shares.
|
||||
//
|
||||
// Paths that do not exist yet (e.g. a brand-new file being created) are
|
||||
// validated against their nearest existing ancestor, so legitimate new files
|
||||
// are always allowed. For a filesystem that is not scoped with BasePathFs the
|
||||
// check is a no-op and returns true.
|
||||
//
|
||||
// Note: a dangling symlink whose target does not yet exist resolves to its
|
||||
// containing directory and is therefore allowed; writing through such a link
|
||||
// could still create a file outside the scope. Callers that create files
|
||||
// should treat this as best-effort and rely on rejecting existing escaping
|
||||
// symlinks, which covers the disclosure and overwrite vectors.
|
||||
func WithinScope(fsys afero.Fs, p string) (bool, error) {
|
||||
bfs, ok := fsys.(*afero.BasePathFs)
|
||||
if !ok {
|
||||
// Not a scoped filesystem; nothing to enforce.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
root, err := filepath.EvalSymlinks(afero.FullBaseFsPath(bfs, "/"))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
target := afero.FullBaseFsPath(bfs, p)
|
||||
resolved, err := filepath.EvalSymlinks(target)
|
||||
for errors.Is(err, fs.ErrNotExist) {
|
||||
parent := filepath.Dir(target)
|
||||
if parent == target {
|
||||
break
|
||||
}
|
||||
target = parent
|
||||
resolved, err = filepath.EvalSymlinks(target)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return resolved == root || strings.HasPrefix(resolved, root+string(filepath.Separator)), nil
|
||||
}
|
||||
|
||||
// Checksum checksums a given File for a given User, using a specific
|
||||
// algorithm. The checksums data is saved on File object.
|
||||
func (i *FileInfo) Checksum(algo string) error {
|
||||
|
||||
+11
-8
@@ -14,13 +14,15 @@
|
||||
"typecheck": "vue-tsc -p ./tsconfig.app.json --noEmit",
|
||||
"lint": "eslint src/",
|
||||
"lint:fix": "eslint --fix src/",
|
||||
"format": "prettier --write ."
|
||||
"format": "prettier --write .",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chenfengyuan/vue-number-input": "^2.0.1",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/integrations": "^14.0.0",
|
||||
"ace-builds": "^1.43.2",
|
||||
"csv-parse": "^6.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"dompurify": "^3.2.6",
|
||||
"epubjs": "^0.3.93",
|
||||
@@ -28,7 +30,8 @@
|
||||
"js-base64": "^3.7.7",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^17.0.0",
|
||||
"marked": "^18.0.0",
|
||||
"marked-katex-extension": "^5.1.6",
|
||||
"material-icons": "^1.13.14",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^3.0.4",
|
||||
@@ -40,7 +43,6 @@
|
||||
"videojs-hotkeys": "^0.2.28",
|
||||
"videojs-mobile-ui": "^1.1.1",
|
||||
"vue": "^3.5.17",
|
||||
"vue-final-modal": "^4.5.5",
|
||||
"vue-i18n": "^11.1.10",
|
||||
"vue-lazyload": "^3.0.0",
|
||||
"vue-reader": "^1.2.17",
|
||||
@@ -53,13 +55,13 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
||||
"@vitejs/plugin-legacy": "^7.2.1",
|
||||
"@vitejs/plugin-legacy": "^8.0.0",
|
||||
"@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": "^9.31.0",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"eslint-plugin-vue": "^10.5.1",
|
||||
@@ -67,9 +69,10 @@
|
||||
"prettier": "^3.6.2",
|
||||
"terser": "^5.43.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.2.2",
|
||||
"vite": "^8.0.0",
|
||||
"vite-plugin-compression2": "^2.3.1",
|
||||
"vitest": "^4.1.0",
|
||||
"vue-tsc": "^3.1.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.1+sha512.48dae233635a645768a3028d19545cacc1688639eeb1f3734e42d6d6b971afbf22aa1ac9af52a173d9c3a20c15857cfa400f19994d79a2f626fcc73fccda9bbc"
|
||||
"packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800"
|
||||
}
|
||||
|
||||
Generated
+2230
-1823
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,25 @@ import { useLayoutStore } from "@/stores/layout";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import { upload as postTus, useTus } from "./tus";
|
||||
import { createURL, fetchURL, removePrefix, StatusError } from "./utils";
|
||||
import { isEncodableResponse, makeRawResource } from "@/utils/encodings";
|
||||
|
||||
export async function fetch(url: string, signal?: AbortSignal) {
|
||||
const encoding = isEncodableResponse(url);
|
||||
url = removePrefix(url);
|
||||
const res = await fetchURL(`/api/resources${url}`, { signal });
|
||||
const res = await fetchURL(`/api/resources${url}`, {
|
||||
signal,
|
||||
headers: {
|
||||
"X-Encoding": encoding ? "true" : "false",
|
||||
},
|
||||
});
|
||||
|
||||
let data: Resource;
|
||||
try {
|
||||
data = (await res.json()) as Resource;
|
||||
if (res.headers.get("Content-Type") == "application/octet-stream") {
|
||||
data = await makeRawResource(res, url);
|
||||
} else {
|
||||
data = (await res.json()) as Resource;
|
||||
}
|
||||
} catch (e) {
|
||||
// Check if the error is an intentional cancellation
|
||||
if (e instanceof Error && e.name === "AbortError") {
|
||||
@@ -38,6 +49,12 @@ export async function fetch(url: string, signal?: AbortSignal) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function fetchAll(url: string): Promise<RecursiveEntry[]> {
|
||||
url = removePrefix(url);
|
||||
const res = await fetchURL(`/api/resources/recursive${url}`, {});
|
||||
return (await res.json()) as RecursiveEntry[];
|
||||
}
|
||||
|
||||
async function resourceAction(url: string, method: ApiMethod, content?: any) {
|
||||
url = removePrefix(url);
|
||||
|
||||
@@ -140,9 +157,9 @@ async function postResources(
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText);
|
||||
} else if (request.status === 409) {
|
||||
reject(request.status);
|
||||
reject(new Error(request.status.toString()));
|
||||
} else {
|
||||
reject(request.responseText);
|
||||
reject(new Error(request.responseText));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,9 +183,12 @@ function moveCopy(
|
||||
for (const item of items) {
|
||||
const from = item.from;
|
||||
const to = encodeURIComponent(removePrefix(item.to ?? ""));
|
||||
const finalOverwrite =
|
||||
item.overwrite == undefined ? overwrite : item.overwrite;
|
||||
const finalRename = item.rename == undefined ? rename : item.rename;
|
||||
const url = `${from}?action=${
|
||||
copy ? "copy" : "rename"
|
||||
}&destination=${to}&override=${overwrite}&rename=${rename}`;
|
||||
}&destination=${to}&override=${finalOverwrite}&rename=${finalRename}`;
|
||||
promises.push(resourceAction(url, "PATCH"));
|
||||
}
|
||||
layoutStore.closeHovers();
|
||||
|
||||
@@ -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>
|
||||
@@ -92,7 +92,7 @@
|
||||
>
|
||||
<progress-bar :val="usage.usedPercentage" size="small"></progress-bar>
|
||||
<br />
|
||||
{{ usage.used }} of {{ usage.total }} used
|
||||
{{ $t("sidebar.diskUsed", { used: usage.used, total: usage.total }) }}
|
||||
</div>
|
||||
|
||||
<p class="credits">
|
||||
|
||||
@@ -1,10 +1,64 @@
|
||||
<template>
|
||||
<div class="csv-viewer">
|
||||
<div class="csv-header">
|
||||
<div class="header-select">
|
||||
<label for="columnSeparator">{{ $t("files.columnSeparator") }}</label>
|
||||
<select
|
||||
id="columnSeparator"
|
||||
class="input input--block"
|
||||
v-model="columnSeparator"
|
||||
>
|
||||
<option :value="[',']">
|
||||
{{ $t("files.csvSeparators.comma") }}
|
||||
</option>
|
||||
<option :value="[';']">
|
||||
{{ $t("files.csvSeparators.semicolon") }}
|
||||
</option>
|
||||
<option :value="[',', ';']">
|
||||
{{ $t("files.csvSeparators.both") }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="header-select" v-if="isEncodedContent">
|
||||
<label for="fileEncoding">{{ $t("files.fileEncoding") }}</label>
|
||||
<DropdownModal
|
||||
v-model="isEncondingDropdownOpen"
|
||||
:close-on-click="false"
|
||||
>
|
||||
<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">
|
||||
<i class="material-icons">error</i>
|
||||
<p>{{ displayError }}</p>
|
||||
</div>
|
||||
<div v-else-if="data.headers.length === 0" class="csv-empty">
|
||||
<div v-else-if="parsed.headers.length === 0" class="csv-empty">
|
||||
<i class="material-icons">description</i>
|
||||
<p>{{ $t("files.lonely") }}</p>
|
||||
</div>
|
||||
@@ -12,13 +66,13 @@
|
||||
<table class="csv-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="(header, index) in data.headers" :key="index">
|
||||
<th v-for="(header, index) in parsed.headers" :key="index">
|
||||
{{ header || `Column ${index + 1}` }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, rowIndex) in data.rows" :key="rowIndex">
|
||||
<tr v-for="(row, rowIndex) in parsed.rows" :key="rowIndex">
|
||||
<td v-for="(cell, cellIndex) in row" :key="cellIndex">
|
||||
{{ cell }}
|
||||
</td>
|
||||
@@ -26,29 +80,11 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="csv-footer">
|
||||
<div class="csv-info" v-if="data.rows.length > 100">
|
||||
<div class="csv-info" v-if="parsed.rows.length > 100">
|
||||
<i class="material-icons">info</i>
|
||||
<span>
|
||||
{{ $t("files.showingRows", { count: data.rows.length }) }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="column-separator">
|
||||
<label for="columnSeparator">{{ $t("files.columnSeparator") }}</label>
|
||||
<select
|
||||
id="columnSeparator"
|
||||
class="input input--block"
|
||||
v-model="columnSeparator"
|
||||
>
|
||||
<option :value="[',']">
|
||||
{{ $t("files.csvSeparators.comma") }}
|
||||
</option>
|
||||
<option :value="[';']">
|
||||
{{ $t("files.csvSeparators.semicolon") }}
|
||||
</option>
|
||||
<option :value="[',', ';']">
|
||||
{{ $t("files.csvSeparators.both") }}
|
||||
</option>
|
||||
</select>
|
||||
{{ $t("files.showingRows", { count: parsed.rows.length }) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,11 +92,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { parseCSV, type CsvData } from "@/utils/csv";
|
||||
import { computed, ref } 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({});
|
||||
|
||||
interface Props {
|
||||
content: string;
|
||||
content: ArrayBuffer | string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@@ -68,31 +109,62 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
error: "",
|
||||
});
|
||||
|
||||
const columnSeparator = ref([","]);
|
||||
const isEncondingDropdownOpen = ref(false);
|
||||
|
||||
const data = computed<CsvData>(() => {
|
||||
try {
|
||||
return parseCSV(props.content, columnSeparator.value);
|
||||
} catch (e) {
|
||||
console.error("Failed to parse CSV:", e);
|
||||
return { headers: [], rows: [] };
|
||||
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");
|
||||
|
||||
const parsed = ref<CsvData>({ headers: [], rows: [] });
|
||||
|
||||
const displayError = ref<string | null>(null);
|
||||
|
||||
const isEncodedContent = computed(() => {
|
||||
return props.content instanceof ArrayBuffer;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.content !== "" && columnSeparator.value.length > 0) {
|
||||
const content = isEncodedContent.value
|
||||
? decode(props.content as ArrayBuffer, selectedEncoding.value)
|
||||
: props.content;
|
||||
parse(
|
||||
content as string,
|
||||
{
|
||||
delimiter: columnSeparator.value,
|
||||
skip_empty_lines: true,
|
||||
relax_column_count: true,
|
||||
},
|
||||
(error, output) => {
|
||||
if (error) {
|
||||
console.error("Failed to parse CSV:", error);
|
||||
parsed.value = { headers: [], rows: [] };
|
||||
displayError.value = t("files.csvLoadFailed", {
|
||||
error: error.toString(),
|
||||
});
|
||||
} else {
|
||||
parsed.value = {
|
||||
headers: output[0],
|
||||
rows: output.slice(1),
|
||||
};
|
||||
displayError.value = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const displayError = computed(() => {
|
||||
// External error takes priority (e.g., file too large)
|
||||
if (props.error) {
|
||||
return props.error;
|
||||
}
|
||||
// Check for parse errors
|
||||
if (
|
||||
props.content &&
|
||||
props.content.trim().length > 0 &&
|
||||
data.value.headers.length === 0
|
||||
) {
|
||||
return "Failed to parse CSV file";
|
||||
}
|
||||
return null;
|
||||
watch(selectedEncoding, () => {
|
||||
isEncondingDropdownOpen.value = false;
|
||||
encodingSearch.value = "";
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -213,10 +285,6 @@ const displayError = computed(() => {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.csv-footer > :only-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.csv-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -230,18 +298,32 @@ const displayError = computed(() => {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.column-separator {
|
||||
.csv-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.header-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
flex-direction: column;
|
||||
@media (width >= 640px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.column-separator > label {
|
||||
.header-select > label {
|
||||
font-size: small;
|
||||
text-align: end;
|
||||
@media (width >= 640px) {
|
||||
max-width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.column-separator > select {
|
||||
.header-select > select,
|
||||
.header-select > div {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -249,4 +331,40 @@ const displayError = computed(() => {
|
||||
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>
|
||||
|
||||
@@ -178,6 +178,10 @@ const drop = async (event: Event) => {
|
||||
from: fileStore.req?.items[i].url,
|
||||
to: props.url + encodeURIComponent(fileStore.req?.items[i].name),
|
||||
name: fileStore.req?.items[i].name,
|
||||
size: fileStore.req?.items[i].size,
|
||||
modified: fileStore.req?.items[i].modified,
|
||||
overwrite: false,
|
||||
rename: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -187,39 +191,50 @@ const drop = async (event: Event) => {
|
||||
return;
|
||||
}
|
||||
const path = el.__vue__.url;
|
||||
const baseItems = (await api.fetch(path)).items;
|
||||
|
||||
const action = (overwrite: boolean, rename: boolean) => {
|
||||
api
|
||||
.move(items, overwrite, rename)
|
||||
const action = (overwrite?: boolean, rename?: boolean) => {
|
||||
const action =
|
||||
(event as KeyboardEvent).ctrlKey || (event as KeyboardEvent).metaKey
|
||||
? api.copy
|
||||
: api.move;
|
||||
action(items, overwrite, rename)
|
||||
.then(() => {
|
||||
fileStore.reload = true;
|
||||
})
|
||||
.catch($showError);
|
||||
};
|
||||
|
||||
const conflict = upload.checkConflict(items, baseItems);
|
||||
const conflict = await upload.checkConflict(items, path);
|
||||
|
||||
let overwrite = false;
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
if (conflict.length > 0) {
|
||||
layoutStore.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event: Event, option: any) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
},
|
||||
confirm: (event: Event, result: Array<ConflictingResource>) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
action(overwrite, rename);
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
items[item.index].rename = true;
|
||||
} else if (item.checked.length == 1 && item.checked[0] == "origin") {
|
||||
items[item.index].overwrite = true;
|
||||
} else {
|
||||
items.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (items.length > 0) {
|
||||
action();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
action(overwrite, rename);
|
||||
action(false, false);
|
||||
};
|
||||
|
||||
const itemClick = (event: Event | KeyboardEvent) => {
|
||||
|
||||
@@ -147,26 +147,33 @@ interface LanguageImports {
|
||||
}
|
||||
|
||||
const languageImports: LanguageImports = {
|
||||
he: () => import("video.js/dist/lang/he.json"),
|
||||
hu: () => import("video.js/dist/lang/hu.json"),
|
||||
ar: () => import("video.js/dist/lang/ar.json"),
|
||||
bg: () => import("video.js/dist/lang/bg.json"),
|
||||
cs: () => import("video.js/dist/lang/cs.json"),
|
||||
de: () => import("video.js/dist/lang/de.json"),
|
||||
el: () => import("video.js/dist/lang/el.json"),
|
||||
en: () => import("video.js/dist/lang/en.json"),
|
||||
es: () => import("video.js/dist/lang/es.json"),
|
||||
fr: () => import("video.js/dist/lang/fr.json"),
|
||||
he: () => import("video.js/dist/lang/he.json"),
|
||||
hr: () => import("video.js/dist/lang/hr.json"),
|
||||
hu: () => import("video.js/dist/lang/hu.json"),
|
||||
it: () => import("video.js/dist/lang/it.json"),
|
||||
ja: () => import("video.js/dist/lang/ja.json"),
|
||||
ko: () => import("video.js/dist/lang/ko.json"),
|
||||
lv: () => import("video.js/dist/lang/lv.json"),
|
||||
nb: () => import("video.js/dist/lang/nb.json"),
|
||||
nl: () => import("video.js/dist/lang/nl.json"),
|
||||
"nl-be": () => import("video.js/dist/lang/nl.json"),
|
||||
pl: () => import("video.js/dist/lang/pl.json"),
|
||||
"pt-br": () => import("video.js/dist/lang/pt-BR.json"),
|
||||
pt: () => import("video.js/dist/lang/pt-PT.json"),
|
||||
"pt-pt": () => import("video.js/dist/lang/pt-PT.json"),
|
||||
ro: () => import("video.js/dist/lang/ro.json"),
|
||||
ru: () => import("video.js/dist/lang/ru.json"),
|
||||
sk: () => import("video.js/dist/lang/sk.json"),
|
||||
tr: () => import("video.js/dist/lang/tr.json"),
|
||||
uk: () => import("video.js/dist/lang/uk.json"),
|
||||
vi: () => import("video.js/dist/lang/vi.json"),
|
||||
"zh-cn": () => import("video.js/dist/lang/zh-CN.json"),
|
||||
"zh-tw": () => import("video.js/dist/lang/zh-TW.json"),
|
||||
};
|
||||
|
||||
@@ -1,21 +1,61 @@
|
||||
<template>
|
||||
<VueFinalModal
|
||||
class="vfm-modal"
|
||||
overlay-transition="vfm-fade"
|
||||
content-transition="vfm-fade"
|
||||
@closed="layoutStore.closeHovers"
|
||||
:focus-trap="{
|
||||
initialFocus: '#focus-prompt',
|
||||
fallbackFocus: 'div.vfm__content',
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</VueFinalModal>
|
||||
<div id="modal-background" @click="backgroundClick">
|
||||
<div ref="modalContainer">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueFinalModal } from "vue-final-modal";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
const emit = defineEmits(["closed"]);
|
||||
|
||||
const modalContainer = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
const element = document.querySelector("#focus-prompt") as HTMLElement | null;
|
||||
if (element) {
|
||||
element.focus();
|
||||
} else if (modalContainer.value) {
|
||||
(modalContainer.value as HTMLElement).focus();
|
||||
}
|
||||
});
|
||||
|
||||
const backgroundClick = (event: Event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.id == "modal-background") {
|
||||
emit("closed");
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.stopImmediatePropagation();
|
||||
emit("closed");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#modal-background {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: #00000096;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
animation: ease-in 150ms opacity-enter;
|
||||
}
|
||||
|
||||
@keyframes opacity-enter {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -91,6 +91,10 @@ export default {
|
||||
from: this.req.items[item].url,
|
||||
to: this.dest + encodeURIComponent(this.req.items[item].name),
|
||||
name: this.req.items[item].name,
|
||||
size: this.req.items[item].size,
|
||||
modified: this.req.items[item].modified,
|
||||
overwrite: false,
|
||||
rename: this.$route.path === this.dest,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,36 +122,40 @@ export default {
|
||||
});
|
||||
};
|
||||
|
||||
if (this.$route.path === this.dest) {
|
||||
this.closeHovers();
|
||||
action(false, true);
|
||||
const conflict = upload.checkConflict(items, this.dest);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const dstItems = (await api.fetch(this.dest)).items;
|
||||
const conflict = upload.checkConflict(items, dstItems);
|
||||
|
||||
let overwrite = false;
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
if (conflict.length > 0) {
|
||||
this.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event, option) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
},
|
||||
confirm: (event, result) => {
|
||||
event.preventDefault();
|
||||
this.closeHovers();
|
||||
action(overwrite, rename);
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
items[item.index].rename = true;
|
||||
} else if (
|
||||
item.checked.length == 1 &&
|
||||
item.checked[0] == "origin"
|
||||
) {
|
||||
items[item.index].overwrite = true;
|
||||
} else {
|
||||
items.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (items.length > 0) {
|
||||
action();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
action(overwrite, rename);
|
||||
action(false, false);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -35,12 +35,17 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const container = ref<HTMLElement | null>(null);
|
||||
|
||||
const path = computed(() => {
|
||||
let basePath = fileStore.isFiles ? route.path : url.removeLastDir(route.path);
|
||||
const routePath = props.path || route.path;
|
||||
let basePath = fileStore.isFiles ? routePath : url.removeLastDir(routePath);
|
||||
if (!basePath.endsWith("/")) {
|
||||
basePath += "/";
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -168,7 +168,13 @@ export default {
|
||||
this.showHover({
|
||||
prompt: "newDir",
|
||||
action: null,
|
||||
confirm: null,
|
||||
confirm: (url) => {
|
||||
const paths = url.split("/");
|
||||
this.items.push({
|
||||
name: paths[paths.length - 2],
|
||||
url: url,
|
||||
});
|
||||
},
|
||||
props: {
|
||||
redirect: false,
|
||||
base: this.current === this.$route.path ? null : this.current,
|
||||
|
||||
@@ -97,6 +97,10 @@ export default {
|
||||
from: this.req.items[item].url,
|
||||
to: this.dest + encodeURIComponent(this.req.items[item].name),
|
||||
name: this.req.items[item].name,
|
||||
size: this.req.items[item].size,
|
||||
modified: this.req.items[item].modified,
|
||||
overwrite: false,
|
||||
rename: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,29 +122,41 @@ export default {
|
||||
});
|
||||
};
|
||||
|
||||
const dstItems = (await api.fetch(this.dest)).items;
|
||||
const conflict = upload.checkConflict(items, dstItems);
|
||||
const conflict = upload.checkConflict(items, this.dest);
|
||||
|
||||
let overwrite = false;
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
if (conflict.length > 0) {
|
||||
this.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event, option) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
files: items,
|
||||
},
|
||||
confirm: (event, result) => {
|
||||
event.preventDefault();
|
||||
this.closeHovers();
|
||||
action(overwrite, rename);
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
items[item.index].rename = true;
|
||||
} else if (
|
||||
item.checked.length == 1 &&
|
||||
item.checked[0] == "origin"
|
||||
) {
|
||||
items[item.index].overwrite = true;
|
||||
} else {
|
||||
items.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (items.length > 0) {
|
||||
action();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
action(overwrite, rename);
|
||||
action(false, false);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
v-model.trim="name"
|
||||
tabindex="1"
|
||||
/>
|
||||
<CreateFilePath :name="name" :is-dir="true" />
|
||||
<CreateFilePath :name="name" :is-dir="true" :path="base" />
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
@@ -41,7 +41,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject, ref } from "vue";
|
||||
import { computed, inject, ref } from "vue";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
@@ -53,17 +53,13 @@ import CreateFilePath from "@/components/prompts/CreateFilePath.vue";
|
||||
|
||||
const $showError = inject<IToastError>("$showError")!;
|
||||
|
||||
const props = defineProps({
|
||||
base: String,
|
||||
redirect: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const fileStore = useFileStore();
|
||||
const layoutStore = useLayoutStore();
|
||||
|
||||
const base = computed(() => {
|
||||
return layoutStore.currentPrompt?.props?.base;
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
@@ -76,7 +72,7 @@ const submit = async (event: Event) => {
|
||||
|
||||
// Build the path of the new directory.
|
||||
let uri: string;
|
||||
if (props.base) uri = props.base;
|
||||
if (base.value) uri = base.value;
|
||||
else if (fileStore.isFiles) uri = route.path + "/";
|
||||
else uri = "/";
|
||||
|
||||
@@ -89,12 +85,15 @@ const submit = async (event: Event) => {
|
||||
|
||||
try {
|
||||
await api.post(uri);
|
||||
if (props.redirect) {
|
||||
if (layoutStore.currentPrompt?.props?.redirect) {
|
||||
router.push({ path: uri });
|
||||
} else if (!props.base) {
|
||||
} else if (!base.value) {
|
||||
const res = await api.fetch(url.removeLastDir(uri) + "/");
|
||||
fileStore.updateRequest(res);
|
||||
}
|
||||
if (layoutStore.currentPrompt?.confirm) {
|
||||
layoutStore.currentPrompt?.confirm(uri);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
$showError(e);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<ModalsContainer />
|
||||
<base-modal v-if="modal != null" :prompt="currentPromptName" @closed="close">
|
||||
<keep-alive>
|
||||
<component :is="modal" />
|
||||
</keep-alive>
|
||||
</base-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from "vue";
|
||||
import { ModalsContainer, useModal } from "vue-final-modal";
|
||||
import { computed } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
@@ -20,11 +23,12 @@ import Copy from "./Copy.vue";
|
||||
import NewFile from "./NewFile.vue";
|
||||
import NewDir from "./NewDir.vue";
|
||||
import Replace from "./Replace.vue";
|
||||
import ReplaceRename from "./ReplaceRename.vue";
|
||||
import Share from "./Share.vue";
|
||||
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();
|
||||
|
||||
@@ -41,35 +45,24 @@ const components = new Map<string, any>([
|
||||
["newDir", NewDir],
|
||||
["download", Download],
|
||||
["replace", Replace],
|
||||
["replace-rename", ReplaceRename],
|
||||
["share", Share],
|
||||
["upload", Upload],
|
||||
["share-delete", ShareDelete],
|
||||
["deleteUser", DeleteUser],
|
||||
["discardEditorChanges", DiscardEditorChanges],
|
||||
["resolve-conflict", ResolveConflict],
|
||||
["current-password", CurrentPassword],
|
||||
]);
|
||||
|
||||
watch(currentPromptName, (newValue) => {
|
||||
const modal = components.get(newValue!);
|
||||
if (!modal) return;
|
||||
const modal = computed(() => {
|
||||
const modal = components.get(currentPromptName.value!);
|
||||
if (!modal) null;
|
||||
|
||||
const { open, close } = useModal({
|
||||
component: BaseModal,
|
||||
slots: {
|
||||
default: modal,
|
||||
},
|
||||
});
|
||||
|
||||
layoutStore.setCloseOnPrompt(close, newValue!);
|
||||
open();
|
||||
return modal;
|
||||
});
|
||||
|
||||
window.addEventListener("keydown", (event) => {
|
||||
const close = () => {
|
||||
if (!layoutStore.currentPrompt) return;
|
||||
|
||||
if (event.key === "Escape") {
|
||||
event.stopImmediatePropagation();
|
||||
layoutStore.closeHovers();
|
||||
}
|
||||
});
|
||||
layoutStore.closeHovers();
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t("prompts.replace") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t("prompts.replaceMessage") }}</p>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
tabindex="3"
|
||||
>
|
||||
{{ $t("buttons.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
class="button button--flat button--blue"
|
||||
@click="(event) => currentPrompt.confirm(event, 'rename')"
|
||||
:aria-label="$t('buttons.rename')"
|
||||
:title="$t('buttons.rename')"
|
||||
tabindex="2"
|
||||
>
|
||||
{{ $t("buttons.rename") }}
|
||||
</button>
|
||||
<button
|
||||
id="focus-prompt"
|
||||
class="button button--flat button--red"
|
||||
@click="(event) => currentPrompt.confirm(event, 'overwrite')"
|
||||
:aria-label="$t('buttons.replace')"
|
||||
:title="$t('buttons.replace')"
|
||||
tabindex="1"
|
||||
>
|
||||
{{ $t("buttons.replace") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "replace-rename",
|
||||
computed: {
|
||||
...mapState(useLayoutStore, ["currentPrompt"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,366 @@
|
||||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>
|
||||
{{
|
||||
personalized
|
||||
? $t("prompts.resolveConflict")
|
||||
: $t("prompts.replaceOrSkip")
|
||||
}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<template v-if="personalized">
|
||||
<p v-if="isUploadAction != true">
|
||||
{{ $t("prompts.singleConflictResolve") }}
|
||||
</p>
|
||||
<div class="conflict-list-container">
|
||||
<div>
|
||||
<p>
|
||||
<input
|
||||
@change="toogleCheckAll"
|
||||
type="checkbox"
|
||||
:checked="originAllChecked"
|
||||
value="origin"
|
||||
/>
|
||||
{{
|
||||
isUploadAction != true
|
||||
? $t("prompts.filesInOrigin")
|
||||
: $t("prompts.uploadingFiles")
|
||||
}}
|
||||
</p>
|
||||
<p>
|
||||
<input
|
||||
@change="toogleCheckAll"
|
||||
type="checkbox"
|
||||
:checked="destAllChecked"
|
||||
value="dest"
|
||||
/>
|
||||
{{ $t("prompts.filesInDest") }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<template v-for="(item, index) in conflict" :key="index">
|
||||
<div class="conflict-file-name">
|
||||
<span>{{ item.name }}</span>
|
||||
|
||||
<template v-if="item.checked.length == 2">
|
||||
<span v-if="isUploadAction != true" class="result-rename">
|
||||
{{ $t("prompts.rename") }}
|
||||
</span>
|
||||
<span v-else class="result-error">
|
||||
{{ $t("prompts.forbiddenError") }}
|
||||
</span>
|
||||
</template>
|
||||
<span
|
||||
v-else-if="
|
||||
item.checked.length == 1 && item.checked[0] == 'origin'
|
||||
"
|
||||
class="result-override"
|
||||
>
|
||||
{{ $t("prompts.override") }}
|
||||
</span>
|
||||
<span v-else class="result-skip">
|
||||
{{ $t("prompts.skip") }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<input v-model="item.checked" type="checkbox" value="origin" />
|
||||
<div>
|
||||
<p class="conflict-file-value">
|
||||
{{ humanTime(item.origin.lastModified) }}
|
||||
</p>
|
||||
<p class="conflict-file-value">
|
||||
{{ humanSize(item.origin.size) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input v-model="item.checked" type="checkbox" value="dest" />
|
||||
<div>
|
||||
<p class="conflict-file-value">
|
||||
{{ humanTime(item.dest.lastModified) }}
|
||||
</p>
|
||||
<p class="conflict-file-value">
|
||||
{{ humanSize(item.dest.size) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>
|
||||
{{ $t("prompts.fastConflictResolve", { count: conflict.length }) }}
|
||||
</p>
|
||||
|
||||
<div class="result-buttons">
|
||||
<button @click="(e) => resolve(e, ['origin'])">
|
||||
<i class="material-icons">done_all</i>
|
||||
{{ $t("buttons.overrideAll") }}
|
||||
</button>
|
||||
<button
|
||||
v-if="isUploadAction != true"
|
||||
@click="(e) => resolve(e, ['origin', 'dest'])"
|
||||
>
|
||||
<i class="material-icons">folder_copy</i>
|
||||
{{ $t("buttons.renameAll") }}
|
||||
</button>
|
||||
<button @click="(e) => resolve(e, ['dest'])">
|
||||
<i class="material-icons">undo</i>
|
||||
{{ $t("buttons.skipAll") }}
|
||||
</button>
|
||||
<button @click="(e) => resume(e)">
|
||||
<i class="material-icons">replay</i>
|
||||
{{ $t("buttons.resumeTransfer") }}
|
||||
<span class="info-tooltip" @click.stop="() => {}">
|
||||
<i class="material-icons info-icon">info_outline</i>
|
||||
<span class="info-tooltip-text">
|
||||
{{ $t("buttons.resumeTransferTooltip") }}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<button @click="personalized = true">
|
||||
<i class="material-icons">checklist</i>
|
||||
{{ $t("buttons.singleDecision") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="card-action" style="display: flex; justify-content: end">
|
||||
<div>
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="close"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
tabindex="4"
|
||||
>
|
||||
{{ $t("buttons.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
v-if="personalized"
|
||||
id="focus-prompt"
|
||||
class="button button--flat"
|
||||
@click="(event) => currentPrompt?.confirm(event, conflict)"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')"
|
||||
tabindex="1"
|
||||
>
|
||||
{{ $t("buttons.ok") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { filesize } from "@/utils";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
const { currentPrompt } = layoutStore;
|
||||
|
||||
const conflict = ref<ConflictingResource[]>(currentPrompt?.props.conflict);
|
||||
|
||||
const isUploadAction = ref<boolean | undefined>(
|
||||
currentPrompt?.props.isUploadAction
|
||||
);
|
||||
|
||||
const personalized = ref(false);
|
||||
|
||||
const originAllChecked = computed(() => {
|
||||
for (const item of conflict.value) {
|
||||
if (!item.checked.includes("origin")) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const destAllChecked = computed(() => {
|
||||
for (const item of conflict.value) {
|
||||
if (!item.checked.includes("dest")) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
layoutStore.closeHovers();
|
||||
};
|
||||
|
||||
const humanSize = (size: number | undefined) => {
|
||||
return size == undefined ? "Unknown size" : filesize(size);
|
||||
};
|
||||
|
||||
const humanTime = (modified: string | number | undefined) => {
|
||||
if (modified == undefined) return "Unknown date";
|
||||
|
||||
return dayjs(modified).format("L LT");
|
||||
};
|
||||
|
||||
const resume = (event: Event) => {
|
||||
conflict.value.forEach((item) => {
|
||||
if (item.isSmallerOnServer) {
|
||||
item.checked = ["origin"];
|
||||
} else {
|
||||
item.checked = ["dest"];
|
||||
}
|
||||
});
|
||||
currentPrompt?.confirm(event, conflict.value);
|
||||
};
|
||||
|
||||
const resolve = (event: Event, result: Array<"origin" | "dest">) => {
|
||||
for (const item of conflict.value) {
|
||||
item.checked = result;
|
||||
}
|
||||
currentPrompt?.confirm(event, conflict.value);
|
||||
};
|
||||
|
||||
const toogleCheckAll = (e: Event) => {
|
||||
const target = e.currentTarget as HTMLInputElement;
|
||||
const value = target.value as "origin" | "dest" | "both";
|
||||
const checked = target.checked;
|
||||
|
||||
for (const item of conflict.value) {
|
||||
if (value == "both") {
|
||||
item.checked = ["origin", "dest"];
|
||||
} else {
|
||||
if (!item.checked.includes(value)) {
|
||||
if (checked) {
|
||||
item.checked.push(value);
|
||||
}
|
||||
} else {
|
||||
if (!checked) {
|
||||
item.checked = value == "dest" ? ["origin"] : ["dest"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.conflict-list-container {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.conflict-list-container > div {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
border-bottom: solid 1px var(--textPrimary);
|
||||
gap: 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
.conflict-list-container > div:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.conflict-list-container > div > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.conflict-file-name {
|
||||
grid-column: 1 / -1;
|
||||
color: var(--textPrimary);
|
||||
font-size: 0.8rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
.conflict-file-value {
|
||||
color: var(--textPrimary);
|
||||
font-size: 0.9rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.result-rename,
|
||||
.result-override,
|
||||
.result-error,
|
||||
.result-skip {
|
||||
font-size: 0.75rem;
|
||||
line-height: 0.75rem;
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
}
|
||||
|
||||
.result-override {
|
||||
background-color: var(--input-green);
|
||||
}
|
||||
|
||||
.result-error {
|
||||
background-color: var(--icon-red);
|
||||
}
|
||||
.result-rename {
|
||||
background-color: var(--icon-orange);
|
||||
}
|
||||
.result-skip {
|
||||
background-color: var(--icon-blue);
|
||||
}
|
||||
|
||||
.result-buttons > button {
|
||||
padding: 0.75rem;
|
||||
color: var(--textPrimary);
|
||||
margin: 0.25rem 0;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: transparent;
|
||||
border: solid 1px transparent;
|
||||
width: 100%;
|
||||
transition: all ease-in-out 200ms;
|
||||
cursor: pointer;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.result-buttons > button:hover {
|
||||
border: solid 1px var(--icon-blue);
|
||||
}
|
||||
|
||||
.info-tooltip {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
font-size: 1rem;
|
||||
color: var(--icon-blue);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.info-tooltip-text {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: var(--surfacePrimary, #333);
|
||||
color: var(--textPrimary, #fff);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.3;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
width: 220px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
transition: opacity 200ms ease-in-out;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.info-tooltip:hover .info-tooltip-text {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -35,7 +35,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import * as upload from "@/utils/upload";
|
||||
@@ -43,11 +42,10 @@ import * as upload from "@/utils/upload";
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const fileStore = useFileStore();
|
||||
const layoutStore = useLayoutStore();
|
||||
|
||||
// TODO: this is a copy of the same function in FileListing.vue
|
||||
const uploadInput = (event: Event) => {
|
||||
const uploadInput = async (event: Event) => {
|
||||
const files = (event.currentTarget as HTMLInputElement)?.files;
|
||||
if (files === null) return;
|
||||
|
||||
@@ -67,20 +65,32 @@ const uploadInput = (event: Event) => {
|
||||
}
|
||||
|
||||
const path = route.path.endsWith("/") ? route.path : route.path + "/";
|
||||
const conflict = upload.checkConflict(uploadFiles, fileStore.req!.items);
|
||||
|
||||
if (conflict) {
|
||||
const conflict = await upload.checkConflict(uploadFiles, path);
|
||||
|
||||
if (conflict.length > 0) {
|
||||
layoutStore.showHover({
|
||||
prompt: "replace",
|
||||
action: (event: Event) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
upload.handleFiles(uploadFiles, path, false);
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
isUploadAction: true,
|
||||
},
|
||||
confirm: (event: Event) => {
|
||||
confirm: (event: Event, result: Array<ConflictingResource>) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
upload.handleFiles(uploadFiles, path, true);
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
continue;
|
||||
} else if (item.checked.length == 1 && item.checked[0] == "origin") {
|
||||
uploadFiles[item.index].overwrite = true;
|
||||
} else {
|
||||
uploadFiles.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (uploadFiles.length > 0) {
|
||||
upload.handleFiles(uploadFiles, path);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -32,10 +32,12 @@ export default {
|
||||
ja: "日本語",
|
||||
ko: "한국어",
|
||||
no: "Norsk",
|
||||
"nl-be": "Dutch (Belgium)",
|
||||
nl: "Nederlands (Nederland)",
|
||||
"nl-be": "Nederlands (België)",
|
||||
lv: "Latviešu",
|
||||
pl: "Polski",
|
||||
"pt-br": "Português (Brasil)",
|
||||
pt: "Português (Portugal)",
|
||||
"pt-pt": "Português (Portugal)",
|
||||
ro: "Romanian",
|
||||
ru: "Русский",
|
||||
sk: "Slovenčina",
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
{{ $t("settings.perm.delete") }}
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" :disabled="admin" v-model="perm.download" />
|
||||
<input
|
||||
type="checkbox"
|
||||
:disabled="admin || perm.share"
|
||||
v-model="perm.download"
|
||||
/>
|
||||
{{ $t("settings.perm.download") }}
|
||||
</p>
|
||||
<p>
|
||||
@@ -61,5 +65,15 @@ export default {
|
||||
},
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
watch: {
|
||||
perm: {
|
||||
deep: true,
|
||||
handler() {
|
||||
if (this.perm.share === true) {
|
||||
this.perm.download = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -182,10 +182,11 @@ html[dir="rtl"] .breadcrumbs a {
|
||||
background: var(--textSecondary) !important;
|
||||
}
|
||||
|
||||
.vfm-modal {
|
||||
z-index: 9999999 !important;
|
||||
}
|
||||
|
||||
body > div[style*="z-index: 9990"] {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
|
||||
#modal-background .button:focus {
|
||||
outline: 1px solid #2195f32d;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@import "normalize.css/normalize.css";
|
||||
@import "vue-toastification/dist/index.css";
|
||||
@import "vue-final-modal/style.css";
|
||||
@import "./_variables.css";
|
||||
@import "./_buttons.css";
|
||||
@import "./_inputs.css";
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "تحديث",
|
||||
"upload": "رفع",
|
||||
"openFile": "فتح الملف",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "إلغاء التغييرات",
|
||||
"stopSearch": "توقف عن البحث",
|
||||
"saveChanges": "حفظ التغييرات",
|
||||
"editAsText": "تعديل على شكل نص",
|
||||
"increaseFontSize": "زيادة حجم الخط",
|
||||
"decreaseFontSize": "تصغير حجم الخط"
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "تحميل الملف",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "حدد الملف أو المجلد",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "إختر الملفات التي تريد رفعها.",
|
||||
"optionalPassword": "كلمة مرور إختيارية",
|
||||
"resolution": "الدقة",
|
||||
"discardEditorChanges": "هل تريد بالتأكيد إلغاء التغييرات؟"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "الصور",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Обнови",
|
||||
"upload": "Качи",
|
||||
"openFile": "Отвори файл",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Изчисти",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Запиши промените",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Свали файл",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "избери файл или директория",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Изберете опция за качване.",
|
||||
"optionalPassword": "Опционална парола",
|
||||
"resolution": "Резолюция",
|
||||
"discardEditorChanges": "Сигурни ли сте, че искате да откажете направените промени?"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Изображения",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Actualitzar",
|
||||
"upload": "Pujar",
|
||||
"openFile": "Obrir fitxer",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Descartar",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar fitxer",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar fitxer o carpeta",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Seleccioneu una opció per pujar.",
|
||||
"optionalPassword": "Contrasenya opcional",
|
||||
"resolution": "Resolució",
|
||||
"discardEditorChanges": "Esteu segur que voleu descartar els canvis que heu fet?"
|
||||
"discardEditorChanges": "Esteu segur que voleu descartar els canvis que heu fet?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Imatges",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Aktualizovat",
|
||||
"upload": "Nahrát",
|
||||
"openFile": "Otevřít soubor",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Zrušit změny",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stáhnout soubor",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "vyberte soubor nebo adresář",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Vyberte možnost pro nahrání.",
|
||||
"optionalPassword": "Volitelné heslo",
|
||||
"resolution": "Rozlišení",
|
||||
"discardEditorChanges": "Opravdu chcete zrušit provedené změny?"
|
||||
"discardEditorChanges": "Opravdu chcete zrušit provedené změny?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Obrázky",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Umbenennen",
|
||||
"replace": "Ersetzen",
|
||||
"reportIssue": "Problem melden",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Speichern",
|
||||
"schedule": "Planen",
|
||||
"search": "Suchen",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Aktualisieren",
|
||||
"upload": "Hochladen",
|
||||
"openFile": "Datei öffnen",
|
||||
"openDirect": "Original anzeigen",
|
||||
"discardChanges": "Verwerfen",
|
||||
"stopSearch": "Suche beenden",
|
||||
"saveChanges": "Änderungen speichern",
|
||||
"editAsText": "Als Text bearbeiten",
|
||||
"increaseFontSize": "Schrift vergrößern",
|
||||
"decreaseFontSize": "Schrift verkleinern"
|
||||
"decreaseFontSize": "Schrift verkleinern",
|
||||
"overrideAll": "Alle Dateien im Zielordner ersetzen",
|
||||
"skipAll": "Alle in Konflikt stehenden Dateien überspringen",
|
||||
"renameAll": "Alle Dateien umbenennen (Kopie erstellen)",
|
||||
"singleDecision": "Bei jeder Datei mit Konflikt entscheiden"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Datei herunterladen",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Komma (,)",
|
||||
"semicolon": "Semikolon (;)",
|
||||
"both": "Beides (,) und (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "Dateikodierung"
|
||||
},
|
||||
"help": {
|
||||
"click": "Datei oder Verzeichnis auswählen",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Wähle eine Upload-Option.",
|
||||
"optionalPassword": "Optionales Passwort",
|
||||
"resolution": "Auflösung",
|
||||
"discardEditorChanges": "Möchtest du deine Änderungen wirklich verwerfen?"
|
||||
"discardEditorChanges": "Möchtest du deine Änderungen wirklich verwerfen?",
|
||||
"replaceOrSkip": "Dateien ersetzen oder überspringen",
|
||||
"resolveConflict": "Welche Dateien möchtest du behalten?",
|
||||
"singleConflictResolve": "Wenn du beide Versionen auswählst, wird dem Namen der kopierten Datei eine Nummer hinzugefügt.",
|
||||
"fastConflictResolve": "Der Zielordner enthält {count} Dateien mit demselben Namen.",
|
||||
"uploadingFiles": "Dateien werden hochgeladen",
|
||||
"filesInOrigin": "Dateien im Ursprungsordner",
|
||||
"filesInDest": "Dateien im Zielordner",
|
||||
"override": "Überschreiben",
|
||||
"skip": "Überspringen",
|
||||
"forbiddenError": "Zugriff verweigert",
|
||||
"currentPassword": "Dein Passwort",
|
||||
"currentPasswordMessage": "Gib dein Passwort ein, um diese Aktion zu bestätigen."
|
||||
},
|
||||
"search": {
|
||||
"images": "Bilder",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "Befehle ausführen",
|
||||
"modify": "Dateien bearbeiten",
|
||||
"rename": "Dateien und Verzeichnisse umbenennen oder verschieben",
|
||||
"share": "Dateien teilen"
|
||||
"share": "Dateien freigeben (Download-Berechtigung erforderlich)"
|
||||
},
|
||||
"permissions": "Berechtigungen",
|
||||
"permissionsHelp": "Du kannst den Benutzer zum Administrator machen oder die Berechtigungen einzeln festlegen. Wenn du \"Administrator\" wählst, werden automatisch alle anderen Optionen aktiviert. Die Benutzerverwaltung bleibt ein Administratorrecht.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "Dein aktuelles Passwort"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Hilfe",
|
||||
"hugoNew": "Hugo Neu",
|
||||
"login": "Anmelden",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Ενημέρωση",
|
||||
"upload": "Μεταφόρτωση",
|
||||
"openFile": "Άνοιγμα αρχείου",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Λήψη αρχείου",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "επιλέξτε αρχείο ή φάκελο",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Επιλέξτε μια επιλογή για τη μεταφόρτωση.",
|
||||
"optionalPassword": "Προαιρετικός κωδικός πρόσβασης",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Εικόνες",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Rename",
|
||||
"replace": "Replace",
|
||||
"reportIssue": "Report Issue",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Save",
|
||||
"schedule": "Schedule",
|
||||
"search": "Search",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Update",
|
||||
"upload": "Upload",
|
||||
"openFile": "Open file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "select file or directory",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Select an option to upload.",
|
||||
"optionalPassword": "Optional password",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Images",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "Execute commands",
|
||||
"modify": "Edit files",
|
||||
"rename": "Rename or move files and directories",
|
||||
"share": "Share files"
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "Permissions",
|
||||
"permissionsHelp": "You can set the user to be an administrator or choose the permissions individually. If you select \"Administrator\", all of the other options will be automatically checked. The management of users remains a privilege of an administrator.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "Your Current Password"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Help",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "Login",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Actualizar",
|
||||
"upload": "Subir",
|
||||
"openFile": "Abrir archivo",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Guardar cambios",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descargar fichero",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar archivo o carpeta",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Seleccione una opción para subir.",
|
||||
"optionalPassword": "Contraseña opcional",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Imágenes",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "به روز سانی",
|
||||
"upload": "آپلود",
|
||||
"openFile": "باز کردن فایل",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "لغو کردن",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "دانلود فایل",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "انتخاب فایل یا پوشه",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "یک گزینه برای آپلود انتخاب کنید.",
|
||||
"optionalPassword": "رمز عبور اختیاری",
|
||||
"resolution": "وضوح تصویر",
|
||||
"discardEditorChanges": "آیا مطمئن هستید که میخواهید تغییراتی را که ایجاد کردهاید، لغو کنید؟"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "تصاویر",
|
||||
|
||||
+49
-28
@@ -29,6 +29,8 @@
|
||||
"rename": "Renommer",
|
||||
"replace": "Remplacer",
|
||||
"reportIssue": "Signaler un problème",
|
||||
"resumeTransfer": "Reprendre le transfert précédent",
|
||||
"resumeTransferTooltip": "Ignorez tous les fichiers en conflit, à l'exception de ceux dont la taille est inférieure sur le serveur, car nous supposons que leur transfert a été interrompu.",
|
||||
"save": "Enregistrer",
|
||||
"schedule": "Planifier",
|
||||
"search": "Rechercher",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Mettre à jour",
|
||||
"upload": "Importer",
|
||||
"openFile": "Ouvrir le fichier",
|
||||
"openDirect": "Affichage brut",
|
||||
"discardChanges": "Annuler",
|
||||
"stopSearch": "Arrêter recherche",
|
||||
"saveChanges": "Enregistrer changements",
|
||||
"editAsText": "Editer comme Texte",
|
||||
"increaseFontSize": "Augmenter taille police",
|
||||
"decreaseFontSize": "Réduire taille police"
|
||||
"decreaseFontSize": "Réduire taille police",
|
||||
"overrideAll": "Remplacer tous les fichiers du dossier de destination",
|
||||
"skipAll": "Ignorer tous les fichiers en conflit",
|
||||
"renameAll": " Renommer tous les fichiers (créer une copie)",
|
||||
"singleDecision": "Choisissez pour chaque fichier en conflit"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Télécharger le fichier",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Virgule (,)",
|
||||
"semicolon": "Point-virgule (;)",
|
||||
"both": "Les (,) et (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "Encodage du fichier"
|
||||
},
|
||||
"help": {
|
||||
"click": "Sélectionner un fichier ou dossier",
|
||||
@@ -112,8 +120,8 @@
|
||||
"passwordsDontMatch": "Les mots de passe ne concordent pas",
|
||||
"signup": "S'inscrire",
|
||||
"submit": "Se connecter",
|
||||
"username": "Utilisateur·ice",
|
||||
"usernameTaken": "Le nom d'utilisateur·ice est déjà pris",
|
||||
"username": "Utilisateur",
|
||||
"usernameTaken": "Le nom d'utilisateur est déjà pris",
|
||||
"wrongCredentials": "Identifiants incorrects !",
|
||||
"passwordTooShort": "Le mot de passe doit contenir au moins {min} caractères",
|
||||
"logout_reasons": {
|
||||
@@ -128,7 +136,7 @@
|
||||
"deleteMessageMultiple": "Êtes-vous sûr de vouloir supprimer ces {count} élément(s) ?",
|
||||
"deleteMessageSingle": "Êtes-vous sûr de vouloir supprimer cet élément ?",
|
||||
"deleteMessageShare": "Êtes-vous sûr de vouloir supprimer ce partage ({path}) ?",
|
||||
"deleteUser": "Êtes-vous sûr de vouloir supprimer cet·te utilisateur·ice ?",
|
||||
"deleteUser": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
|
||||
"deleteTitle": "Supprimer",
|
||||
"displayName": "Nom :",
|
||||
"download": "Télécharger",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Sélectionnez une option d'import.",
|
||||
"optionalPassword": "Mot de passe optionnel",
|
||||
"resolution": "Résolution",
|
||||
"discardEditorChanges": "Êtes-vous sûr de vouloir annuler les modifications apportées ?"
|
||||
"discardEditorChanges": "Êtes-vous sûr de vouloir annuler les modifications apportées ?",
|
||||
"replaceOrSkip": "Remplacer ou ignorer les fichiers",
|
||||
"resolveConflict": "Quels fichiers souhaitez-vous conserver ?",
|
||||
"singleConflictResolve": "Si vous sélectionnez les deux versions, un numéro sera ajouté au nom du fichier copié.",
|
||||
"fastConflictResolve": "Le dossier de destination contient {count} fichiers portant le même nom.",
|
||||
"uploadingFiles": "Importation de fichiers",
|
||||
"filesInOrigin": "Fichiers d'origine",
|
||||
"filesInDest": "Fichiers de destination",
|
||||
"override": "Remplacer",
|
||||
"skip": "Passer",
|
||||
"forbiddenError": "Action non autorisée",
|
||||
"currentPassword": "Votre mot de passe",
|
||||
"currentPasswordMessage": "Entrez votre mot de passe pour valider cette action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Images",
|
||||
@@ -174,12 +194,12 @@
|
||||
"settings": {
|
||||
"aceEditorTheme": "Éditeur de Thème Ace",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrateur·ice",
|
||||
"administrator": "Administrateur",
|
||||
"allowCommands": "Exécuter des commandes",
|
||||
"allowEdit": "Éditer, renommer et supprimer des fichiers ou des dossiers",
|
||||
"allowNew": "Créer de nouveaux fichiers et dossiers",
|
||||
"allowPublish": "Publier de nouveaux posts et pages",
|
||||
"allowSignup": "Autoriser les utilisateur·ices à s'inscrire",
|
||||
"allowSignup": "Autoriser les utilisateurs à s'inscrire",
|
||||
"hideLoginButton": "Cacher le bouton d’identification sur les pages publiques",
|
||||
"avoidChanges": "(Laisser vide pour conserver l'actuel)",
|
||||
"branding": "Image de marque",
|
||||
@@ -189,17 +209,17 @@
|
||||
"commandRunner": "Exécuteur de commandes",
|
||||
"commandRunnerHelp": "Ici, vous pouvez définir les commandes qui seront exécutées lors des événements nommés précédemments. Vous devez en écrire une par ligne. Les variables d'environnement {0} et {1} seront disponibles, {0} étant relatif à {1}. Pour plus d'informations sur cette fonctionnalité et les variables d'environnement disponibles, veuillez lire la {2}.",
|
||||
"commandsUpdated": "Commandes mises à jour !",
|
||||
"createUserDir": "Créer automatiquement un dossier pour l'utilisateur·ice",
|
||||
"createUserDir": "Créer automatiquement un dossier pour l'utilisateur",
|
||||
"minimumPasswordLength": "Taille minimale du mot de passe",
|
||||
"tusUploads": "Uploads segmentés",
|
||||
"tusUploadsHelp": "File Browser prend en charge les uploads segmentés afin de permettre une gestion efficace, fiable et reprenable sur des réseaux instables.",
|
||||
"tusUploadsChunkSize": "Taille maximale autorisée par segment (les uploads directs seront utilisés pour les fichiers plus petits). Vous pouvez entrer un entier en octets ou une chaîne telle que 10MB, 1GB, etc.",
|
||||
"tusUploadsRetryCount": "Nombre de tentatives en cas d'échec d'un segment.",
|
||||
"userHomeBasePath": "Chemin de base pour les dossiers personnels des utilisateur·ices",
|
||||
"userHomeBasePath": "Chemin de base pour les dossiers personnels des utilisateurs",
|
||||
"userScopeGenerationPlaceholder": "Le périmètre sera généré automatiquement",
|
||||
"createUserHomeDirectory": "Créer le dossier personnel de l'utilisateur·ice",
|
||||
"createUserHomeDirectory": "Créer le dossier personnel de l'utilisateur",
|
||||
"customStylesheet": "Feuille de style personnalisée",
|
||||
"defaultUserDescription": "Paramètres par défaut pour les nouveaux utilisateur·ices.",
|
||||
"defaultUserDescription": "Paramètres par défaut pour les nouveaux utilisateurs.",
|
||||
"disableExternalLinks": "Désactiver les liens externes (sauf la documentation)",
|
||||
"disableUsedDiskPercentage": "Désactiver le graphique de pourcentage d'utilisation du disque",
|
||||
"documentation": "documentation",
|
||||
@@ -213,7 +233,7 @@
|
||||
"insertRegex": "Insérer une expression régulière",
|
||||
"instanceName": "Nom de l'instance",
|
||||
"language": "Langue",
|
||||
"lockPassword": "Empêcher l'utilisateur·ice de changer son mot de passe",
|
||||
"lockPassword": "Empêcher l'utilisateur de changer son mot de passe",
|
||||
"newPassword": "Votre nouveau mot de passe",
|
||||
"newPasswordConfirm": "Confirmation du nouveau mot de passe",
|
||||
"newUser": "Nouvel utilisateur",
|
||||
@@ -227,17 +247,17 @@
|
||||
"execute": "Exécuter des commandes",
|
||||
"modify": "Modifier des fichiers",
|
||||
"rename": "Renommer ou déplacer des fichiers ou des dossiers",
|
||||
"share": "Partager des fichiers"
|
||||
"share": "Partager des fichiers (autorisation de téléchargement requise)"
|
||||
},
|
||||
"permissions": "Permissions",
|
||||
"permissionsHelp": "Vous pouvez définir l'utilisateur·ice comme étant administrateur·ice ou encore choisir les permissions individuellement. Si vous sélectionnez \"Administrateur·ice\", toutes les autres options seront automatiquement activées. La gestion des utilisateur·ices est un privilège que seul l'administrateur·ice possède.\n",
|
||||
"permissionsHelp": "Vous pouvez définir l'utilisateur comme étant un administrateur ou encore choisir les permissions individuellement. Si vous sélectionnez \"Administrateur\", toutes les autres options seront automatiquement activées. La gestion des utilisateurs est un privilège que seul l'administrateur possède.\n",
|
||||
"profileSettings": "Paramètres du profil",
|
||||
"redirectAfterCopyMove": "Redirect to destination after copy/move",
|
||||
"redirectAfterCopyMove": "Rediriger vers la destination après une copie/déplacement",
|
||||
"ruleExample1": "Bloque l'accès à tous les fichiers commençant par un point (comme par exemple .git, .gitignore) dans tous les dossiers.\n",
|
||||
"ruleExample2": "Bloque l'accès au fichier nommé \"Caddyfile\" à la racine du dossier utilisateur·ice.",
|
||||
"ruleExample2": "Bloque l'accès au fichier nommé \"Caddyfile\" à la racine du dossier utilisateur",
|
||||
"rules": "Règles",
|
||||
"rulesHelp": "Vous pouvez définir ici un ensemble de règles pour cet utilisateur·ice. Les fichiers bloqués ne seront pas affichés et ne seront pas accessibles par l'utilisateur·ice. Les expressions régulières sont supportées et les chemins d'accès sont relatifs par rapport au dossier de l'utilisateur·ice.\n",
|
||||
"scope": "Portée du dossier utilisateur·ice",
|
||||
"rulesHelp": "Vous pouvez définir ici un ensemble de règles pour cet utilisateur. Les fichiers bloqués ne seront pas affichés et ne seront pas accessibles par l'utilisateur. Les expressions régulières sont supportées et les chemins d'accès sont relatifs par rapport au dossier de l'utilisateur.\n",
|
||||
"scope": "Portée du dossier utilisateur",
|
||||
"setDateFormat": "Définir le format de la date",
|
||||
"settingsUpdated": "Les paramètres ont été mis à jour !",
|
||||
"shareDuration": "Durée du partage",
|
||||
@@ -250,19 +270,20 @@
|
||||
"light": "Clair",
|
||||
"title": "Thème"
|
||||
},
|
||||
"user": "Utilisateur·ice",
|
||||
"user": "Utilisateur",
|
||||
"userCommands": "Commandes",
|
||||
"userCommandsHelp": "Une liste séparée par des espaces des commandes permises pour l'utilisateur·ice. Exemple :\n",
|
||||
"userCreated": "Utilisateur·ice créé !",
|
||||
"userDefaults": "Paramètres par défaut de l'utilisateur.ice",
|
||||
"userDeleted": "Utilisateur·ice supprimé !",
|
||||
"userManagement": "Gestion des utilisateur·ices",
|
||||
"userUpdated": "Utilisateur·ice mis à jour !",
|
||||
"username": "Nom d'utilisateur·ice",
|
||||
"users": "Utilisateur·ices",
|
||||
"userCommandsHelp": "Une liste séparée par des espaces des commandes permises pour l'utilisateur. Exemple :\n",
|
||||
"userCreated": "Utilisateur créé !",
|
||||
"userDefaults": "Paramètres par défaut de l'utilisateur",
|
||||
"userDeleted": "Utilisateur supprimé !",
|
||||
"userManagement": "Gestion des utilisateurs",
|
||||
"userUpdated": "Utilisateur mis à jour !",
|
||||
"username": "Nom d'utilisateur",
|
||||
"users": "Utilisateurs",
|
||||
"currentPassword": "Mot de Passe Actuel"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} sur {total} utilisés",
|
||||
"help": "Aide",
|
||||
"hugoNew": "Nouveau Hugo",
|
||||
"login": "Login",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "עדכון",
|
||||
"upload": "העלאה",
|
||||
"openFile": "פתח קובץ",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "זריקת השינויים",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "הורד קובץ",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "בחר קובץ או תיקייה",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "בחר אפשרות העלאה.",
|
||||
"optionalPassword": "סיסמא אופציונלית",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "האם אתה בטוח שברצונך לבטל את השינויים שביצעת?"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "תמונות",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Preimenuj",
|
||||
"replace": "Zamijeni",
|
||||
"reportIssue": "Prijavi grešku",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Spremi",
|
||||
"schedule": "Zakaži",
|
||||
"search": "Pretraži",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Ažuriraj",
|
||||
"upload": "Prenesi",
|
||||
"openFile": "Otvori datoteku",
|
||||
"openDirect": "Pregled neobrađenog",
|
||||
"discardChanges": "Odbaci",
|
||||
"stopSearch": "Stop searching",
|
||||
"stopSearch": "Prestanak pretraživanja",
|
||||
"saveChanges": "Spremi promjene",
|
||||
"editAsText": "Uredi kao tekst",
|
||||
"increaseFontSize": "Povećaj veličinu fonta",
|
||||
"decreaseFontSize": "Smanji veličinu fonta"
|
||||
"decreaseFontSize": "Smanji veličinu fonta",
|
||||
"overrideAll": "Zamijeni sve datoteke u destinacijskoj mapi",
|
||||
"skipAll": "Preskoči sve konfliktirajuće datoteke",
|
||||
"renameAll": "Preimenuj sve datoteke (stvori kopiju)",
|
||||
"singleDecision": "Odluči za svaku konfliktirajuću datoteku"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Preuzmi Datoteku",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Zarez (,)",
|
||||
"semicolon": "Točka zarez (;)",
|
||||
"both": "I (,) i (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "Kodiranje datoteka"
|
||||
},
|
||||
"help": {
|
||||
"click": "odaberi datoteku ili mapu",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Odaberite opciju za prijenos.",
|
||||
"optionalPassword": "Opcionalna lozinka",
|
||||
"resolution": "Rezolucija",
|
||||
"discardEditorChanges": "Jeste li sigurni da želite odbaciti promjene koje ste napravili?"
|
||||
"discardEditorChanges": "Jeste li sigurni da želite odbaciti promjene koje ste napravili?",
|
||||
"replaceOrSkip": "Zamijeni ili preskoči datoteke",
|
||||
"resolveConflict": "Koje datoteke želite sačuvati?",
|
||||
"singleConflictResolve": "Ako odaberete obje verzije, u ime kopirane datoteke bit će dodan broj.",
|
||||
"fastConflictResolve": "Destinacijska mapa sadrži {count} istoimenih datoteka.",
|
||||
"uploadingFiles": "Prenošenje datoteka",
|
||||
"filesInOrigin": "Datoteke u izvornoj mapi",
|
||||
"filesInDest": "Datoteke u destinacijskoj mapi",
|
||||
"override": "Prebrisivanje",
|
||||
"skip": "Preskoči",
|
||||
"forbiddenError": "Greška zabrane pristupa",
|
||||
"currentPassword": "Vaša lozinka",
|
||||
"currentPasswordMessage": "Unesite Vašu lozinku da biste validirali ovu akciju."
|
||||
},
|
||||
"search": {
|
||||
"images": "Slike",
|
||||
@@ -227,12 +247,12 @@
|
||||
"execute": "Izvršavanje naredbi",
|
||||
"modify": "Uređivanje datoteka",
|
||||
"rename": "Preimenovanje ili premještanje datoteka i mapa",
|
||||
"share": "Dijeljenje datoteka"
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "Dopuštenja",
|
||||
"permissionsHelp": "Korisnika možete postaviti administratorom ili odabrati dopuštenja individualno. Odabirom na \"Administrator\", sve druge opcije bit će automatski odabrane. Upravljanje korisnicima ostaje privilegija administratora.\n",
|
||||
"profileSettings": "Postavke profila",
|
||||
"redirectAfterCopyMove": "Redirect to destination after copy/move",
|
||||
"redirectAfterCopyMove": "Preusmjeri na destinaciju nakon kopiranja/premještanja",
|
||||
"ruleExample1": "onemogućava pristup svakoj datoteci koja započinje točkom (poput .git, .gitignore) u svakoj mapi.\n",
|
||||
"ruleExample2": "blokira pristup datoteci naziva Caddyfile na korijenu opsega.",
|
||||
"rules": "Pravila",
|
||||
@@ -260,9 +280,10 @@
|
||||
"userUpdated": "Korisnik ažuriran!",
|
||||
"username": "Korisničko ime",
|
||||
"users": "Korisnici",
|
||||
"currentPassword": "Your Current Password"
|
||||
"currentPassword": "Vaša trenutna lozinka"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Pomoć",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "Prijava",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Frissítés",
|
||||
"upload": "Feltöltés",
|
||||
"openFile": "Fájl megnyitása",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Fájl letöltése",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "mappa vagy fájl kijelölése",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Válasszon egy feltöltési módot.",
|
||||
"optionalPassword": "Választható jelszó",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Képek",
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createI18n } from "vue-i18n";
|
||||
|
||||
import("dayjs/locale/ar");
|
||||
import("dayjs/locale/bg");
|
||||
import("dayjs/locale/ca");
|
||||
import("dayjs/locale/cs");
|
||||
import("dayjs/locale/de");
|
||||
import("dayjs/locale/el");
|
||||
@@ -16,7 +17,9 @@ import("dayjs/locale/is");
|
||||
import("dayjs/locale/it");
|
||||
import("dayjs/locale/ja");
|
||||
import("dayjs/locale/ko");
|
||||
import("dayjs/locale/lv");
|
||||
import("dayjs/locale/nb");
|
||||
import("dayjs/locale/nl");
|
||||
import("dayjs/locale/nl-be");
|
||||
import("dayjs/locale/pl");
|
||||
import("dayjs/locale/pt-br");
|
||||
@@ -40,6 +43,18 @@ export function detectLocale() {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language
|
||||
let locale = navigator.language.toLowerCase();
|
||||
switch (true) {
|
||||
case /^ar\b/.test(locale):
|
||||
locale = "ar";
|
||||
break;
|
||||
case /^bg\b/.test(locale):
|
||||
locale = "bg";
|
||||
break;
|
||||
case /^cs\b/.test(locale):
|
||||
locale = "cs";
|
||||
break;
|
||||
case /^lv\b/.test(locale):
|
||||
locale = "lv";
|
||||
break;
|
||||
case /^he\b/.test(locale):
|
||||
locale = "he";
|
||||
break;
|
||||
@@ -49,9 +64,6 @@ export function detectLocale() {
|
||||
case /^hu\b/.test(locale):
|
||||
locale = "hu";
|
||||
break;
|
||||
case /^ar\b/.test(locale):
|
||||
locale = "ar";
|
||||
break;
|
||||
case /^el.*/i.test(locale):
|
||||
locale = "el";
|
||||
break;
|
||||
@@ -73,8 +85,9 @@ export function detectLocale() {
|
||||
case /^pt-br\b/.test(locale):
|
||||
locale = "pt-br";
|
||||
break;
|
||||
case /^pt-pt\b/.test(locale):
|
||||
case /^pt\b/.test(locale):
|
||||
locale = "pt";
|
||||
locale = "pt-pt";
|
||||
break;
|
||||
case /^ja\b/.test(locale):
|
||||
locale = "ja";
|
||||
@@ -110,7 +123,6 @@ export function detectLocale() {
|
||||
case /^uk\b/.test(locale):
|
||||
locale = "uk";
|
||||
break;
|
||||
|
||||
case /^vi\b/.test(locale):
|
||||
locale = "vi";
|
||||
break;
|
||||
@@ -118,6 +130,9 @@ export function detectLocale() {
|
||||
case /^sv\b/.test(locale):
|
||||
locale = "sv";
|
||||
break;
|
||||
case /^nl\b/.test(locale):
|
||||
locale = "nl";
|
||||
break;
|
||||
case /^nl-be\b/.test(locale):
|
||||
locale = "nl-be";
|
||||
break;
|
||||
@@ -125,9 +140,7 @@ export function detectLocale() {
|
||||
case /^no\b/.test(locale):
|
||||
locale = "no";
|
||||
break;
|
||||
case /^bg\b/.test(locale):
|
||||
locale = "bg";
|
||||
break;
|
||||
|
||||
default:
|
||||
locale = "en";
|
||||
}
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Vista",
|
||||
"upload": "Hlaða upp",
|
||||
"openFile": "Open file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Sækja skjal",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "velja skjal eða möppu",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Select an option to upload.",
|
||||
"optionalPassword": "Optional password",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Myndir",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Aggiorna",
|
||||
"upload": "Carica",
|
||||
"openFile": "Apri file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Ignora",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Scarica file",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "seleziona un file o una cartella",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Seleziona un'opzione per il caricamento.",
|
||||
"optionalPassword": "Password opzionale",
|
||||
"resolution": "Risoluzione",
|
||||
"discardEditorChanges": "Sei sicuro di voler scartare le modifiche apportate?"
|
||||
"discardEditorChanges": "Sei sicuro di voler scartare le modifiche apportate?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Immagini",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "更新",
|
||||
"upload": "アップロード",
|
||||
"openFile": "ファイルを開く",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "ファイルのダウンロード",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "ファイルやフォルダーを選択",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "アップロードするオプションを選択してください。",
|
||||
"optionalPassword": "パスワード(オプション)",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "画像",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "이름 바꾸기",
|
||||
"replace": "대체",
|
||||
"reportIssue": "문제 보고",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "저장",
|
||||
"schedule": "일정",
|
||||
"search": "검색",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "업데이트",
|
||||
"upload": "업로드",
|
||||
"openFile": "파일 열기",
|
||||
"openDirect": "원본 보기",
|
||||
"discardChanges": "변경 사항 취소",
|
||||
"stopSearch": "검색 중단",
|
||||
"saveChanges": "변경사항 저장",
|
||||
"editAsText": "텍스트로 편집",
|
||||
"increaseFontSize": "글꼴 크기 증가",
|
||||
"decreaseFontSize": "글꼴 크기 감소"
|
||||
"decreaseFontSize": "글꼴 크기 감소",
|
||||
"overrideAll": "대상 폴더의 모든 파일을 대체",
|
||||
"skipAll": "충돌하는 모든 파일을 건너뛰기",
|
||||
"renameAll": "모든 파일의 이름 변경 (복사본 생성)",
|
||||
"singleDecision": "충돌하는 각 파일을 확인"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "파일 다운로드",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "반점 (,)",
|
||||
"semicolon": "쌍반점 (;)",
|
||||
"both": "(,) 및 (;) 모두"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "파일 인코딩"
|
||||
},
|
||||
"help": {
|
||||
"click": "파일 또는 디렉터리 선택",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "업로드 옵션을 선택하세요.",
|
||||
"optionalPassword": "비밀번호 (선택)",
|
||||
"resolution": "해상도",
|
||||
"discardEditorChanges": "변경 사항을 취소하시겠습니까?"
|
||||
"discardEditorChanges": "변경 사항을 취소하시겠습니까?",
|
||||
"replaceOrSkip": "파일 대체 또는 건너뛰기",
|
||||
"resolveConflict": "어떤 파일을 유지하시겠습니까?",
|
||||
"singleConflictResolve": "모든 버전을 선택하면 복사한 파일의 이름에 번호를 추가합니다.",
|
||||
"fastConflictResolve": "대상 폴더에 같은 이름을 가진 파일이 {count}개 있습니다.",
|
||||
"uploadingFiles": "파일 업로드 중",
|
||||
"filesInOrigin": "원본 파일",
|
||||
"filesInDest": "대상 경로의 파일",
|
||||
"override": "덮어쓰기",
|
||||
"skip": "건너뛰기",
|
||||
"forbiddenError": "권한 오류",
|
||||
"currentPassword": "기존 비밀번호",
|
||||
"currentPasswordMessage": "기존 비밀번호를 입력해 이 동작을 확인하십시오."
|
||||
},
|
||||
"search": {
|
||||
"images": "이미지",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "명령 실행",
|
||||
"modify": "파일 편집",
|
||||
"rename": "파일 및 디렉터리의 이름 변경 또는 이동",
|
||||
"share": "파일 공유하기"
|
||||
"share": "파일 공유 (다운로드 권한 필요)"
|
||||
},
|
||||
"permissions": "권한",
|
||||
"permissionsHelp": "사용자를 관리자로 설정하거나 개별적으로 권한을 지정할 수 있습니다. \"관리자\"를 선택하면 다른 모든 옵션이 자동으로 선택됩니다. 사용자 관리는 관리자의 권한입니다.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "현재 비밀번호"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "도움말",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "로그인",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Pārdēvēt",
|
||||
"replace": "Aizstāt",
|
||||
"reportIssue": "Ziņot par problēmu",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Saglabāt",
|
||||
"schedule": "Grafiks",
|
||||
"search": "Meklēt",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Atjaunināt",
|
||||
"upload": "Augšupielādēt",
|
||||
"openFile": "Atvērt failu",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Izmest",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Saglabāt izmaiņas",
|
||||
"editAsText": "Rediģēt kā tekstu",
|
||||
"increaseFontSize": "Palieliniet fonta lielumu",
|
||||
"decreaseFontSize": "Samaziniet fonta lielumu"
|
||||
"decreaseFontSize": "Samaziniet fonta lielumu",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Lejupielādēt failu",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Komats (,)",
|
||||
"semicolon": "Semikols (;)",
|
||||
"both": "Gan (,) gan (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "atlasiet failu vai direktoriju",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Atlasiet augšupielādes opciju.",
|
||||
"optionalPassword": "Izvēles parole",
|
||||
"resolution": "Izšķirtspēja",
|
||||
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?"
|
||||
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Attēli",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "Izpildīt komandas",
|
||||
"modify": "Rediģēt failus",
|
||||
"rename": "Pārdēvēt vai pārvietot failus un direktorijus",
|
||||
"share": "Kopīgojiet failus"
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "Atļaujas",
|
||||
"permissionsHelp": "Varat iestatīt lietotāju kā administratoru vai izvēlēties atļaujas individuāli. Ja atlasīsiet “Administrators”, visas pārējās opcijas tiks automātiski atzīmētas. Lietotāju pārvaldība joprojām ir administratora privilēģija.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "Your Current Password"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Palīdzība",
|
||||
"hugoNew": "Hugo Jauns",
|
||||
"login": "Pieteikties",
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
{
|
||||
"buttons": {
|
||||
"cancel": "Atcelt",
|
||||
"clear": "Tīrs",
|
||||
"close": "Aizvērt",
|
||||
"continue": "Turpināt",
|
||||
"copy": "Kopēt",
|
||||
"copyFile": "Kopēt failu",
|
||||
"copyToClipboard": "Kopēt starpliktuvē",
|
||||
"copyDownloadLinkToClipboard": "Kopēt lejupielādes saiti starpliktuvē",
|
||||
"create": "Izveidot",
|
||||
"delete": "Dzēst",
|
||||
"download": "Lejupielādēt",
|
||||
"file": "Fails",
|
||||
"folder": "Mape",
|
||||
"fullScreen": "Pārslēgt pilnekrāna režīmu",
|
||||
"hideDotfiles": "Slēpt punktfailus",
|
||||
"info": "Informācija",
|
||||
"more": "Vairāk",
|
||||
"move": "Pārvietot",
|
||||
"moveFile": "Pārvietot failu",
|
||||
"new": "Jauns",
|
||||
"next": "Nākamais",
|
||||
"ok": "Labi",
|
||||
"permalink": "Iegūt pastāvīgo saiti",
|
||||
"previous": "Iepriekšējais",
|
||||
"preview": "Priekšskatījums",
|
||||
"publish": "Publicēt",
|
||||
"rename": "Pārdēvēt",
|
||||
"replace": "Aizstāt",
|
||||
"reportIssue": "Ziņot par problēmu",
|
||||
"save": "Saglabāt",
|
||||
"schedule": "Grafiks",
|
||||
"search": "Meklēt",
|
||||
"select": "Izvēlieties",
|
||||
"selectMultiple": "Izvēlieties vairākus",
|
||||
"share": "Dalīties",
|
||||
"shell": "Pārslēgt apvalku",
|
||||
"submit": "Iesniegt",
|
||||
"switchView": "Pārslēgt skatu",
|
||||
"toggleSidebar": "Pārslēgt sānjoslu",
|
||||
"update": "Atjaunināt",
|
||||
"upload": "Augšupielādēt",
|
||||
"openFile": "Atvērt failu",
|
||||
"discardChanges": "Izmest",
|
||||
"stopSearch": "Beigt meklēšanu",
|
||||
"saveChanges": "Saglabāt izmaiņas",
|
||||
"editAsText": "Rediģēt kā tekstu",
|
||||
"increaseFontSize": "Palieliniet fonta lielumu",
|
||||
"decreaseFontSize": "Samaziniet fonta lielumu"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Lejupielādēt failu",
|
||||
"downloadFolder": "Lejupielādēt mapi",
|
||||
"downloadSelected": "Lejupielādēt atlasīto"
|
||||
},
|
||||
"upload": {
|
||||
"abortUpload": "Vai tiešām vēlaties pārtraukt?"
|
||||
},
|
||||
"errors": {
|
||||
"forbidden": "Jums nav atļaujas piekļūt šim.",
|
||||
"internal": "Kaut kas tiešām nogāja greizi.",
|
||||
"notFound": "Šo atrašanās vietu nevar sasniegt.",
|
||||
"connection": "Ar serveri nevar sazināties."
|
||||
},
|
||||
"files": {
|
||||
"body": "Ķermenis",
|
||||
"closePreview": "Aizvērt priekšskatījumu",
|
||||
"files": "Faili",
|
||||
"folders": "Mapes",
|
||||
"home": "Sākums",
|
||||
"lastModified": "Pēdējoreiz modificēts",
|
||||
"loading": "Notiek ielāde...",
|
||||
"lonely": "Šeit ir tukša vieta...",
|
||||
"metadata": "Metadati",
|
||||
"multipleSelectionEnabled": "Vairākas atlases ir iespējotas",
|
||||
"name": "Vārds",
|
||||
"size": "Izmērs",
|
||||
"sortByLastModified": "Kārtot pēc pēdējās modifikācijas",
|
||||
"sortByName": "Kārtot pēc nosaukuma",
|
||||
"sortBySize": "Kārtot pēc izmēra",
|
||||
"noPreview": "Šim failam nav pieejams priekšskatījums.",
|
||||
"csvTooLarge": "CSV fails ir pārāk liels priekšskatīšanai (>5 MB). Lūdzu, lejupielādējiet to, lai skatītu",
|
||||
"csvLoadFailed": "Neizdevās ielādēt CSV failu.",
|
||||
"showingRows": "Rāda {count} rindu(as)",
|
||||
"columnSeparator": "Kolonnu atdalītājs",
|
||||
"csvSeparators": {
|
||||
"comma": "Komats (,)",
|
||||
"semicolon": "Semikols (;)",
|
||||
"both": "Gan (,) gan (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "atlasiet failu vai direktoriju",
|
||||
"ctrl": {
|
||||
"click": "atlasīt vairākus failus vai direktorijus",
|
||||
"f": "atver meklēšanu",
|
||||
"s": "saglabājiet failu vai lejupielādējiet direktoriju, kurā atrodaties"
|
||||
},
|
||||
"del": "dzēst atlasītos vienumus",
|
||||
"doubleClick": "atvērt failu vai direktoriju",
|
||||
"esc": "notīrīt atlasi un/vai aizvērt uzvedni",
|
||||
"f1": "šo informāciju",
|
||||
"f2": "pārdēvēt failu",
|
||||
"help": "Palīdzība"
|
||||
},
|
||||
"login": {
|
||||
"createAnAccount": "Izveidot kontu",
|
||||
"loginInstead": "Jau ir konts",
|
||||
"password": "Parole",
|
||||
"passwordConfirm": "Paroles apstiprināšana",
|
||||
"passwordsDontMatch": "Paroles nesakrīt",
|
||||
"signup": "Reģistrēšanās",
|
||||
"submit": "Pieteikties",
|
||||
"username": "Lietotājvārds",
|
||||
"usernameTaken": "Lietotājvārds jau aizņemts",
|
||||
"wrongCredentials": "Nepareizi akreditācijas dati",
|
||||
"passwordTooShort": "Parolei jābūt vismaz {min} rakstzīmju garai",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Jūs esat atteicies no sistēmas neaktivitātes dēļ."
|
||||
}
|
||||
},
|
||||
"permanent": "Pastāvīgs",
|
||||
"prompts": {
|
||||
"copy": "Kopēt",
|
||||
"copyMessage": "Izvēlieties atrašanās vietu, uz kuru kopēt failus:",
|
||||
"currentlyNavigating": "Pašlaik navigācija:",
|
||||
"deleteMessageMultiple": "Vai tiešām vēlaties dzēst {count} failu(s)?",
|
||||
"deleteMessageSingle": "Vai tiešām vēlaties dzēst šo failu/mapi?",
|
||||
"deleteMessageShare": "Vai tiešām vēlaties dzēst šo koplietojumu({path})?",
|
||||
"deleteUser": "Vai tiešām vēlaties dzēst šo lietotāju?",
|
||||
"deleteTitle": "Dzēst failus",
|
||||
"displayName": "Displeja nosaukums:",
|
||||
"download": "Lejupielādēt failus",
|
||||
"downloadMessage": "Izvēlieties formātu, kuru vēlaties lejupielādēt.",
|
||||
"error": "Kaut kas nogāja greizi",
|
||||
"fileInfo": "Informācija par failu",
|
||||
"filesSelected": "{count} atlasīti faili",
|
||||
"lastModified": "Pēdējās izmaiņas",
|
||||
"move": "Pārvietot",
|
||||
"moveMessage": "Izvēlieties jaunu mājvietu failam(iem)/mapei(ēm):",
|
||||
"newArchetype": "Izveidojiet jaunu ierakstu, pamatojoties uz arhtipu. Jūsu fails tiks izveidots satura mapē.",
|
||||
"newDir": "Jauna direktorija",
|
||||
"newDirMessage": "Nosaukums savai jaunajai direktorijai.",
|
||||
"newFile": "Jauns fails",
|
||||
"newFileMessage": "Nosauciet savu jauno failu.",
|
||||
"numberDirs": "Katalogu skaits",
|
||||
"numberFiles": "Failu skaits",
|
||||
"rename": "Pārdēvēt",
|
||||
"renameMessage": "Ievietojiet jaunu nosaukumu",
|
||||
"replace": "Aizstāt",
|
||||
"replaceMessage": "Vienam no failiem, kurus mēģināt augšupielādēt, ir konfliktējošs nosaukums. Vai vēlaties izlaist šo failu un turpināt augšupielādi vai aizstāt esošo?\n",
|
||||
"schedule": "Grafiks",
|
||||
"scheduleMessage": "Izvēlieties datumu un laiku, lai ieplānotu šī ieraksta publicēšanu.",
|
||||
"show": "Rādīt",
|
||||
"size": "Izmērs",
|
||||
"upload": "Augšupielādēt",
|
||||
"uploadFiles": "Notiek {files} failu augšupielāde...",
|
||||
"uploadMessage": "Atlasiet augšupielādes opciju.",
|
||||
"optionalPassword": "Izvēles parole",
|
||||
"resolution": "Izšķirtspēja",
|
||||
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?"
|
||||
},
|
||||
"search": {
|
||||
"images": "Attēli",
|
||||
"music": "Mūzika",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Nospiediet taustiņu Enter, lai meklētu...",
|
||||
"search": "Meklē...",
|
||||
"typeToSearch": "Ierakstiet, lai meklētu...",
|
||||
"types": "Veidi",
|
||||
"video": "Video"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace redaktora tēma",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrator",
|
||||
"allowCommands": "Izpildīt komandas",
|
||||
"allowEdit": "Rediģēt, pārdēvēt un dzēst failus vai direktorijus",
|
||||
"allowNew": "Izveidojiet jaunus failus un direktorijus",
|
||||
"allowPublish": "Publicēt jaunus ierakstus un lapas",
|
||||
"allowSignup": "Atļaut lietotājiem reģistrēties",
|
||||
"hideLoginButton": "Paslēpt pieteikšanās pogu publiskajās lapās",
|
||||
"avoidChanges": "(atstājiet tukšu, lai izvairītos no izmaiņām)",
|
||||
"branding": "Zīmols",
|
||||
"brandingDirectoryPath": "Zīmola direktorijas ceļš",
|
||||
"brandingHelp": "Jūs varat pielāgot sava failu pārlūka instances izskatu un darbību, mainot tās nosaukumu, aizstājot logotipu, pievienojot pielāgotus stilus un pat atspējojot ārējās saites uz GitHub.\nLai iegūtu plašāku informāciju par pielāgotu zīmola veidošanu, lūdzu, skatiet {0}.",
|
||||
"changePassword": "Mainīt paroli",
|
||||
"commandRunner": "Komandu skrējējs",
|
||||
"commandRunnerHelp": "Šeit varat iestatīt komandas, kas tiek izpildītas nosauktajos notikumos. Katrā rindā jāraksta pa vienai. Būs pieejami vides mainīgie {0} un {1}, kas ir {0} relatīvi pret {1}. Lai iegūtu plašāku informāciju par šo funkciju un pieejamajiem vides mainīgajiem, lūdzu, izlasiet {2}.",
|
||||
"commandsUpdated": "Komandas atjauninātas!",
|
||||
"createUserDir": "Automātiski izveidot lietotāja mājas direktoriju, pievienojot jaunu lietotāju",
|
||||
"minimumPasswordLength": "Minimālais paroles garums",
|
||||
"tusUploads": "Sadalītas augšupielādes",
|
||||
"tusUploadsHelp": "Failu pārlūks atbalsta failu augšupielādi fragmentos, ļaujot izveidot efektīvu, uzticamu, atsākamu un fragmentos sadalītu failu augšupielādi pat neuzticamos tīklos.",
|
||||
"tusUploadsChunkSize": "Norāda pieprasījuma maksimālo izmēru (mazākiem augšupielādes apjomiem tiks izmantota tieša augšupielāde). Varat ievadīt vienkāršu veselu skaitli, kas apzīmē baitu izmēru, vai virkni, piemēram, 10 MB, 1 GB utt.",
|
||||
"tusUploadsRetryCount": "Atkārtotu mēģinājumu skaits, kas jāveic, ja fragmenta augšupielāde neizdodas.",
|
||||
"userHomeBasePath": "Lietotāja mājas direktoriju bāzes ceļš",
|
||||
"userScopeGenerationPlaceholder": "Darbības joma tiks ģenerēta automātiski",
|
||||
"createUserHomeDirectory": "Izveidojiet lietotāja mājas direktoriju",
|
||||
"customStylesheet": "Pielāgota stila lapa",
|
||||
"defaultUserDescription": "Šie ir noklusējuma iestatījumi jaunajiem lietotājiem.",
|
||||
"disableExternalLinks": "Atspējot ārējās saites (izņemot dokumentāciju)",
|
||||
"disableUsedDiskPercentage": "Atspējot izmantotā diska procentuālās daļas grafiku",
|
||||
"documentation": "dokumentācija",
|
||||
"examples": "Piemēri",
|
||||
"executeOnShell": "Izpildīt uz čaulas",
|
||||
"executeOnShellDescription": "Pēc noklusējuma failu pārlūks izpilda komandas, tieši izsaucot to bināros failus. Ja vēlaties tās palaist čaulā (piemēram, Bash vai PowerShell), varat to definēt šeit ar nepieciešamajiem argumentiem un karodziņiem. Ja tas ir iestatīts, izpildītā komanda tiks pievienota kā arguments. Tas attiecas gan uz lietotāja komandām, gan notikumu piesaistes rīkiem.",
|
||||
"globalRules": "Šis ir globāls atļaušanas un aizliegšanas noteikumu kopums. Tie attiecas uz katru lietotāju. Katra lietotāja iestatījumos varat definēt konkrētus noteikumus, lai tos ignorētu.",
|
||||
"globalSettings": "Globālie iestatījumi",
|
||||
"hideDotfiles": "Slēpt punktfailus",
|
||||
"insertPath": "Ievietojiet ceļu",
|
||||
"insertRegex": "Ievietojiet regex izteiksmi",
|
||||
"instanceName": "Gadījuma nosaukums",
|
||||
"language": "Valoda",
|
||||
"lockPassword": "Neļaut lietotājam mainīt paroli",
|
||||
"newPassword": "Jūsu jaunā parole",
|
||||
"newPasswordConfirm": "Apstipriniet savu jauno paroli",
|
||||
"newUser": "Jauns lietotājs",
|
||||
"password": "Parole",
|
||||
"passwordUpdated": "Parole atjaunināta!",
|
||||
"path": "Ceļš",
|
||||
"perm": {
|
||||
"create": "Izveidojiet failus un direktorijus",
|
||||
"delete": "Dzēst failus un direktorijus",
|
||||
"download": "Lejupielādēt",
|
||||
"execute": "Izpildīt komandas",
|
||||
"modify": "Rediģēt failus",
|
||||
"rename": "Pārdēvēt vai pārvietot failus un direktorijus",
|
||||
"share": "Kopīgojiet failus"
|
||||
},
|
||||
"permissions": "Atļaujas",
|
||||
"permissionsHelp": "Varat iestatīt lietotāju kā administratoru vai izvēlēties atļaujas individuāli. Ja atlasīsiet “Administrators”, visas pārējās opcijas tiks automātiski atzīmētas. Lietotāju pārvaldība joprojām ir administratora privilēģija.\n",
|
||||
"profileSettings": "Profila iestatījumi",
|
||||
"redirectAfterCopyMove": "Pārvirzīt uz galapunktu pēc kopēšanas/pārvietošanas",
|
||||
"ruleExample1": "neļauj piekļūt jebkuram dotfile failam (piemēram, .git, .gitignore) katrā mapē.\n",
|
||||
"ruleExample2": "bloķē piekļuvi failam ar nosaukumu Caddyfile darbības jomas saknē.",
|
||||
"rules": "Noteikumi",
|
||||
"rulesHelp": "Šeit varat definēt atļaušanas un aizliegšanas noteikumu kopu šim konkrētajam lietotājam. Bloķētie faili netiks rādīti sarakstos, un lietotājs tiem nevarēs piekļūt. Mēs atbalstām regulārās izteiksmes un ceļus attiecībā pret lietotāja darbības jomu.\n",
|
||||
"scope": "Darbības joma",
|
||||
"setDateFormat": "Iestatiet precīzu datuma formātu",
|
||||
"settingsUpdated": "Iestatījumi atjaunināti!",
|
||||
"shareDuration": "Kopīgošanas ilgums",
|
||||
"shareManagement": "Kopīgošanas pārvaldība",
|
||||
"shareDeleted": "Kopīgojums izdzēsts!",
|
||||
"singleClick": "Failu un direktoriju atvēršanai izmantojiet vienus klikšķi",
|
||||
"themes": {
|
||||
"default": "Sistēmas noklusējums",
|
||||
"dark": "Tumša",
|
||||
"light": "Gaiša",
|
||||
"title": "Tēma"
|
||||
},
|
||||
"user": "Lietotājs",
|
||||
"userCommands": "Komandas",
|
||||
"userCommandsHelp": "Ar atstarpi atdalīts saraksts ar šim lietotājam pieejamajām komandām. Piemērs:\n",
|
||||
"userCreated": "Lietotājs izveidots!",
|
||||
"userDefaults": "Lietotāja noklusējuma iestatījumi",
|
||||
"userDeleted": "Lietotājs izdzēsts!",
|
||||
"userManagement": "Lietotāju pārvaldība",
|
||||
"userUpdated": "Lietotājs atjaunināts!",
|
||||
"username": "Lietotājvārds",
|
||||
"users": "Lietotāji",
|
||||
"currentPassword": "Esošā Parole"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Palīdzība",
|
||||
"hugoNew": "Hugo Jauns",
|
||||
"login": "Pieteikties",
|
||||
"logout": "Atteikties",
|
||||
"myFiles": "Mani faili",
|
||||
"newFile": "Jauns fails",
|
||||
"newFolder": "Jauna mape",
|
||||
"preview": "Priekšskatījums",
|
||||
"settings": "Iestatījumi",
|
||||
"signup": "Reģistrēties",
|
||||
"siteSettings": "Vietnes iestatījumi"
|
||||
},
|
||||
"success": {
|
||||
"linkCopied": "Saite nokopēta!"
|
||||
},
|
||||
"time": {
|
||||
"days": "Dienas",
|
||||
"hours": "Stundas",
|
||||
"minutes": "Minūtes",
|
||||
"seconds": "Sekundes",
|
||||
"unit": "Laika vienība"
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Updaten",
|
||||
"upload": "Uploaden",
|
||||
"openFile": "Open file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Bestand downloaden",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "selecteer bestand of map",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Select an option to upload.",
|
||||
"optionalPassword": "Optional password",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Afbeeldingen",
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
{
|
||||
"buttons": {
|
||||
"cancel": "Annuleren",
|
||||
"clear": "Wissen",
|
||||
"close": "Sluiten",
|
||||
"continue": "Doorgaan",
|
||||
"copy": "Kopiëren",
|
||||
"copyFile": "Bestand kopiëren",
|
||||
"copyToClipboard": "Kopiëren naar klembord",
|
||||
"copyDownloadLinkToClipboard": "Download-link kopiëren naar klembord",
|
||||
"create": "Aanmaken",
|
||||
"delete": "Verwijderen",
|
||||
"download": "Downloaden",
|
||||
"file": "Bestand",
|
||||
"folder": "Map",
|
||||
"fullScreen": "Volledig scherm wisselen",
|
||||
"hideDotfiles": "dot-bestanden verbergen",
|
||||
"info": "Info",
|
||||
"more": "Meer",
|
||||
"move": "Verplaatsen",
|
||||
"moveFile": "Bestand verplaatsen",
|
||||
"new": "Nieuw",
|
||||
"next": "Volgende",
|
||||
"ok": "OK",
|
||||
"permalink": "Permanente link verkrijgen",
|
||||
"previous": "Vorige",
|
||||
"preview": "Voorbeeld",
|
||||
"publish": "Publiceren",
|
||||
"rename": "Hernoemen",
|
||||
"replace": "Vervangen",
|
||||
"reportIssue": "Probleem melden",
|
||||
"resumeTransfer": "Vorige overdracht hervatten",
|
||||
"resumeTransferTooltip": "Alle conflicterende bestanden overslaan, behalve de bestanden die kleiner zijn op de server, omdat we veronderstellen dat hun overdracht is onderbroken.",
|
||||
"save": "Opslaan",
|
||||
"schedule": "Plannen",
|
||||
"search": "Zoeken",
|
||||
"select": "Selecteren",
|
||||
"selectMultiple": "Meervoudige selectie",
|
||||
"share": "Delen",
|
||||
"shell": "Opdrachtvenster wisselen",
|
||||
"submit": "Indienen",
|
||||
"switchView": "Weergave wisselen",
|
||||
"toggleSidebar": "Zijbalk wisselen",
|
||||
"update": "Bijwerken",
|
||||
"upload": "Uploaden",
|
||||
"openFile": "Bestand openen",
|
||||
"openDirect": "Raw weergeven",
|
||||
"discardChanges": "Weggooien",
|
||||
"stopSearch": "Stoppen met zoeken",
|
||||
"saveChanges": "Wijzigingen opslaan",
|
||||
"editAsText": "Als tekst bewerken",
|
||||
"increaseFontSize": "Lettertype vergroten",
|
||||
"decreaseFontSize": "Lettertype verkleinen",
|
||||
"overrideAll": "Alle bestanden in doelmap vervangen",
|
||||
"skipAll": "Alle conflicterende bestanden overslaan",
|
||||
"renameAll": "Alle bestanden hernoemen (een kopie maken)",
|
||||
"singleDecision": "Voor elk conflicterend bestand besluiten"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Bestand downloaden",
|
||||
"downloadFolder": "Map downloaden",
|
||||
"downloadSelected": "Selectie downloaden"
|
||||
},
|
||||
"upload": {
|
||||
"abortUpload": "Weet u zeker dat u wilt afbreken?"
|
||||
},
|
||||
"errors": {
|
||||
"forbidden": "U hebt hier geen toegangsrechten.",
|
||||
"internal": "Er ging iets mis.",
|
||||
"notFound": "Deze locatie kan niet worden bereikt.",
|
||||
"connection": "De server kan niet worden bereikt."
|
||||
},
|
||||
"files": {
|
||||
"body": "Inoud",
|
||||
"closePreview": "Voorbeeld sluiten",
|
||||
"files": "Bestanden",
|
||||
"folders": "Mappen",
|
||||
"home": "Thuis",
|
||||
"lastModified": "Laatst aangepast",
|
||||
"loading": "Laden...",
|
||||
"lonely": "Het is hier wat leeg...",
|
||||
"metadata": "Metagegevens",
|
||||
"multipleSelectionEnabled": "Meervoudige selectie ingeschakeld",
|
||||
"name": "Naam",
|
||||
"size": "Grootte",
|
||||
"sortByLastModified": "Sorteren op laatst aangepast",
|
||||
"sortByName": "Sorteren op naam",
|
||||
"sortBySize": "Sorteren op grootte",
|
||||
"noPreview": "Geen voorbeeld beschikbaar voor dit bestand.",
|
||||
"csvTooLarge": "CSV-bestand is te groot voor voorbeeld (>5MB). Download het om het te bekijken.",
|
||||
"csvLoadFailed": "Laden van CSV-bestand is mislukt.",
|
||||
"showingRows": "{count} rij(en) weergeven",
|
||||
"columnSeparator": "Kolom-scheidingsteken",
|
||||
"csvSeparators": {
|
||||
"comma": "Komma (,)",
|
||||
"semicolon": "Puntkomma (;)",
|
||||
"both": "Beide (,) en (;)"
|
||||
},
|
||||
"fileEncoding": "Bestandscodering"
|
||||
},
|
||||
"help": {
|
||||
"click": "selecteer bestand of map",
|
||||
"ctrl": {
|
||||
"click": "selecteer meerdere bestanden of mappen",
|
||||
"f": "open zoekopdracht",
|
||||
"s": "sla een bestand op of download de huidige map"
|
||||
},
|
||||
"del": "verwijder geselecteerde items",
|
||||
"doubleClick": "open een bestand of map",
|
||||
"esc": "wis de selectie en/of sluit het opdrachtvenster",
|
||||
"f1": "deze informatie",
|
||||
"f2": "hernoem bestand",
|
||||
"help": "Hulp"
|
||||
},
|
||||
"login": {
|
||||
"createAnAccount": "Account aanmaken",
|
||||
"loginInstead": "Heeft al een account",
|
||||
"password": "Wachtwoord",
|
||||
"passwordConfirm": "Wachtwoord bevestigen",
|
||||
"passwordsDontMatch": "Wachtwoorden komen niet overeen",
|
||||
"signup": "Registeren",
|
||||
"submit": "Inloggen",
|
||||
"username": "Gebruikersnaam",
|
||||
"usernameTaken": "Gebruikersnaam reeds in gebruik",
|
||||
"wrongCredentials": "Inloggegevens zijn incorrect",
|
||||
"passwordTooShort": "Wachtwoord moet minstens {min} tekens bevatten",
|
||||
"logout_reasons": {
|
||||
"inactivity": "U bent uitgelogd vanwege inactiviteit."
|
||||
}
|
||||
},
|
||||
"permanent": "Permanent",
|
||||
"prompts": {
|
||||
"copy": "Kopiëren",
|
||||
"copyMessage": "Kies een locatie om uw bestanden te kopiëren:",
|
||||
"currentlyNavigating": "Navigeren op:",
|
||||
"deleteMessageMultiple": "Weet u zeker dat u {count} bestand(en) wilt verwijderen?",
|
||||
"deleteMessageSingle": "Weet u zeker dat u dit bestand/deze map wilt verwijderen?",
|
||||
"deleteMessageShare": "Weet u zeker dat u deze gedeelde map ({path}) wilt verwijderen?",
|
||||
"deleteUser": "Weet u zeker dat u deze gebruiker wilt verwijderen?",
|
||||
"deleteTitle": "Bestanden verwijderen",
|
||||
"displayName": "Weergavenaam:",
|
||||
"download": "Bestanden downloaden",
|
||||
"downloadMessage": "Kies het formaat dat u wilt downloaden.",
|
||||
"error": "Er ging iets fout",
|
||||
"fileInfo": "Bestandsinformatie",
|
||||
"filesSelected": "{count} geselecteerde bestanden.",
|
||||
"lastModified": "Laatst aangepast",
|
||||
"move": "Verplaatsen",
|
||||
"moveMessage": "Kies een nieuw Thuis voor uw bestand(en)/map(pen):",
|
||||
"newArchetype": "Maak een nieuw bericht op basis van een archetype. Uw bestand wordt aangemaakt in de inhoudsmap.",
|
||||
"newDir": "Nieuwe map",
|
||||
"newDirMessage": "Voer een naam in voor de nieuwe map.",
|
||||
"newFile": "Nieuw bestand",
|
||||
"newFileMessage": "Voer een naam in voor het nieuwe bestand.",
|
||||
"numberDirs": "Aantal mappen",
|
||||
"numberFiles": "Aantal bestanden",
|
||||
"rename": "Hernoemen",
|
||||
"renameMessage": "Voer een nieuwe naam in voor",
|
||||
"replace": "Vervangen",
|
||||
"replaceMessage": "Een van de bestanden die u probeert te uploaden, geeft conflicten i.v.m. de naamgeving. Wilt u de bestaande bestanden vervangen?\n",
|
||||
"schedule": "Plannen",
|
||||
"scheduleMessage": "Kies een datum en tijd om de publicatie van dit bericht in te plannen.",
|
||||
"show": "Weergeven",
|
||||
"size": "Grootte",
|
||||
"upload": "Uploaden",
|
||||
"uploadFiles": "{files} bestanden uploaden...",
|
||||
"uploadMessage": "Kies een optie bij de upload.",
|
||||
"optionalPassword": "Optioneel wachtwoord",
|
||||
"resolution": "Resolutie",
|
||||
"discardEditorChanges": "Weet u zeker dat u de door u aangebrachte wijzigingen wilt weggooien?",
|
||||
"replaceOrSkip": "Bestanden vervangen of overslaan",
|
||||
"resolveConflict": "Welke bestanden wilt u behouden?",
|
||||
"singleConflictResolve": "Als u beide versies selecteert, wordt er een nummer toegevoegd aan de naam van het gekopieerde bestand.",
|
||||
"fastConflictResolve": "De doelmap daar bevat {count} bestanden met dezelfde naam.",
|
||||
"uploadingFiles": "Bestanden uploaden",
|
||||
"filesInOrigin": "Bestanden in origineel",
|
||||
"filesInDest": "Bestanden in bestemming",
|
||||
"override": "Overschrijven",
|
||||
"skip": "Overslaan",
|
||||
"forbiddenError": "Verboden Fout",
|
||||
"currentPassword": "Uw wachtwoord",
|
||||
"currentPasswordMessage": "Voer uw wachtwoord in om deze actie te valideren."
|
||||
},
|
||||
"search": {
|
||||
"images": "Afbeeldingen",
|
||||
"music": "Muziek",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Druk op enter om te zoeken...",
|
||||
"search": "Zoeken...",
|
||||
"typeToSearch": "Typ om te zoeken...",
|
||||
"types": "Types",
|
||||
"video": "Video"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace editor-thema",
|
||||
"admin": "Admin",
|
||||
"administrator": "Beheerder",
|
||||
"allowCommands": "Opdrachten uitvoeren",
|
||||
"allowEdit": "Bestanden of mappen bewerken, hernoemen of verwijderen",
|
||||
"allowNew": "Nieuwe bestanden of mappen aanmaken",
|
||||
"allowPublish": "Nieuwe berichten en pagina's publiceren",
|
||||
"allowSignup": "Gebruikers toestaan om zich te registreren",
|
||||
"hideLoginButton": "De inlogknop verbergen van openbare pagina's",
|
||||
"avoidChanges": "(leeg laten om wijzigingen te voorkomen)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Pad naar map met Branding",
|
||||
"brandingHelp": "U kunt het uiterlijk van uw File Browser aanpassen door de naam te wijzigen, het logo te vervangen, aangepaste stijlen toe te voegen en zelfs externe links naar GitHub uit te schakelen.\nVoor meer informatie over custom branding, bekijk {0}.",
|
||||
"changePassword": "Wachtwoord wijzigen",
|
||||
"commandRunner": "Opdracht uitvoeren",
|
||||
"commandRunnerHelp": "Hier kunt u opdrachten instellen die worden uitgevoerd in de benoemde gebeurtenissen. Noteer één opdracht per regel. De omgevingsvariabelen {0} en {1} zijn beschikbaar, waarbij {0} relatief is ten opzichte van {1}. Raadpleeg {2} voor meer informatie over deze functie en de beschikbare omgevingsvariabelen.",
|
||||
"commandsUpdated": "Opdrachten bijgewerkt!",
|
||||
"createUserDir": "Maak bij het aanmaken van een nieuwe gebruiker automatisch een thuismap aan",
|
||||
"minimumPasswordLength": "Minimale wachtwoordlengte",
|
||||
"tusUploads": "Uploads in brokken",
|
||||
"tusUploadsHelp": "File Browser ondersteunt bestandsuploads in brokken, waardoor efficiënte, betrouwbare, hervatbare bestandsuploads kunnen worden verricht, zelfs over onbetrouwbare netwerken.",
|
||||
"tusUploadsChunkSize": "Geeft de maximale grootte van een verzoek aan (directe uploads worden gebruikt voor kleinere uploads). U kunt een geheel getal invoeren dat de bytegrootte aangeeft, of een tekenreeks zoals 10MB, 1GB enz.",
|
||||
"tusUploadsRetryCount": "Aantal nieuwe pogingen om uit te voeren als een deel niet kan worden geüpload.",
|
||||
"userHomeBasePath": "Basispad voor Thuismappen van de gebruikers",
|
||||
"userScopeGenerationPlaceholder": "Het bereik wordt automatisch gegenereeerd",
|
||||
"createUserHomeDirectory": "Thuismap voor de gebruiker aanmaken",
|
||||
"customStylesheet": "Aangepaste stylesheet",
|
||||
"defaultUserDescription": "Dit zijn de standaardinstellingen voor nieuwe gebruikers.",
|
||||
"disableExternalLinks": "Externe links uitschakelen (behalve documentatie)",
|
||||
"disableUsedDiskPercentage": "Grafiek van gebruikte schijfruimte uitschakelen",
|
||||
"documentation": "documentatie",
|
||||
"examples": "Voorbeelden",
|
||||
"executeOnShell": "Uitvoeren in opdrachtvenster",
|
||||
"executeOnShellDescription": "File Browser voert de opdrachten standaard uit door het rechtstreeks oproepen van hun hun binaire bestanden. Als u ze in plaats daarvan wilt uitvoeren in een opdrachtvenster (zoals Bash of PowerShell), kunt u dit hier definiëren met de vereiste argumenten en vlaggen. Zo wordt de opdracht die u uitvoert, toegevoegd als een argument. Dit is van toepassing op zowel gebruikersopdrachten als event-hooks.",
|
||||
"globalRules": "Dit is een algemene reeks toestaan/niet toestaan-regels welke van toepassing zijn op alle gebruikers. U kunt voor elke gebruiker specifieke regels definiëren om deze te overschrijven.",
|
||||
"globalSettings": "Algemene instellingen",
|
||||
"hideDotfiles": "dot-bestanden verbergen",
|
||||
"insertPath": "Pad invoegen",
|
||||
"insertRegex": "Regex-expressie invoegen",
|
||||
"instanceName": "Naam van de instantie",
|
||||
"language": "Taal",
|
||||
"lockPassword": "Voorkomen dat de gebruiker het wachtwoord wijzigt",
|
||||
"newPassword": "Uw nieuwe wachtwoord",
|
||||
"newPasswordConfirm": "Uw nieuwe wachtwoord bevestigen",
|
||||
"newUser": "Nieuwe gebruiker",
|
||||
"password": "Wachtwoord",
|
||||
"passwordUpdated": "Wachtwoord bijgewerkt!",
|
||||
"path": "Pad",
|
||||
"perm": {
|
||||
"create": "Bestanden en mappen aanmaken",
|
||||
"delete": "Bestanden en mappen verwijderen",
|
||||
"download": "Downloaden",
|
||||
"execute": "Opdrachten uitvoeren",
|
||||
"modify": "Bestanden bewerken",
|
||||
"rename": "Bestanden of mappen hernoemen of verplaatsen",
|
||||
"share": "Bestanden delen (download toestemming vereisen)"
|
||||
},
|
||||
"permissions": "Machtigingen",
|
||||
"permissionsHelp": " kunt de gebruiker instellen als beheerder of de machtigingen afzonderlijk kiezen. Als u \"Beheerder\" selecteert, worden alle overige opties automatisch ingeschakeld. Het beheer van gebruikers blijft een privilege van een beheerder.\n",
|
||||
"profileSettings": "Profielinstellingen",
|
||||
"redirectAfterCopyMove": "Doorverwijzen naar bestemming na kopiëren/verplaatsen",
|
||||
"ruleExample1": "voorkomt de toegang tot dot-bestanden (zoals .git, .gitignore) in elke map.\n",
|
||||
"ruleExample2": "blokkeert de toegang tot het bestand met de naam Caddyfile in de hoofdmap van het bereik.",
|
||||
"rules": "Regels",
|
||||
"rulesHelp": "Hier kunt u voor deze specifieke gebruiker een reeks toestaan/niet toestaan-regels definiëren. De geblokkeerde bestanden verschijnen niet in de lijsten en zijn niet toegankelijk voor de gebruiker. Regex wordt ondersteund en paden zijn relatief ten opzichte van het bereik van gebruikers.\n",
|
||||
"scope": "Bereik",
|
||||
"setDateFormat": "Exacte datumformaat instellen",
|
||||
"settingsUpdated": "Instellingen bijgewerkt!",
|
||||
"shareDuration": "Duur van het delen",
|
||||
"shareManagement": "Beheer van gedeelde mappen en bestanden",
|
||||
"shareDeleted": "Delen opheffen!",
|
||||
"singleClick": "Bestanden en mappen met een enkele klik openen",
|
||||
"themes": {
|
||||
"default": "Systeem standaard",
|
||||
"dark": "Donker",
|
||||
"light": "Licht",
|
||||
"title": "Thema"
|
||||
},
|
||||
"user": "Gebruiker",
|
||||
"userCommands": "Opdrachten",
|
||||
"userCommandsHelp": "Een lijst met beschikbare opdrachten voor de gebruiker gescheiden door spaties. Voorbeeld:\n",
|
||||
"userCreated": "Gebruiker aangemaakt!",
|
||||
"userDefaults": "Standaardinstellingen van gebruiker",
|
||||
"userDeleted": "Gebruiker verwijderd!",
|
||||
"userManagement": "Gebruikers beheren",
|
||||
"userUpdated": "Gebruiker bijgewerkt!",
|
||||
"username": "Gebruikersnaam",
|
||||
"users": "Gebruikers",
|
||||
"currentPassword": "Uw huidige wachtwoord"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} van {total} gebruikt",
|
||||
"help": "Hulp",
|
||||
"hugoNew": "Hugo Nieuw",
|
||||
"login": "Inloggen",
|
||||
"logout": "Uitloggen",
|
||||
"myFiles": "Mijn bestanden",
|
||||
"newFile": "Nieuw bestand",
|
||||
"newFolder": "Nieuwe map",
|
||||
"preview": "Voorbeeld",
|
||||
"settings": "Instellingen",
|
||||
"signup": "Registeren",
|
||||
"siteSettings": "Site-instellingen"
|
||||
},
|
||||
"success": {
|
||||
"linkCopied": "Link gekopieerd!"
|
||||
},
|
||||
"time": {
|
||||
"days": "Dagen",
|
||||
"hours": "Uren",
|
||||
"minutes": "Minuten",
|
||||
"seconds": "Seconden",
|
||||
"unit": "Tijdseenheid"
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Opptater",
|
||||
"upload": "Last opp",
|
||||
"openFile": "Open file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Slett",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Lagre Endringane ",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Nedlast filen",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "velg fil eller katalog",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Velg et alternativ for opplasting.",
|
||||
"optionalPassword": "Valgfritt passord",
|
||||
"resolution": "Oppløysning",
|
||||
"discardEditorChanges": "Er du sikker på at du vil forkaste endringene du har gjort?"
|
||||
"discardEditorChanges": "Er du sikker på at du vil forkaste endringene du har gjort?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Bilde",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Zmień nazwę",
|
||||
"replace": "Zamień",
|
||||
"reportIssue": "Zgłoś problem",
|
||||
"resumeTransfer": "Wznów poprzedni transfer",
|
||||
"resumeTransferTooltip": "Pomiń wszystkie pliki powodujące konflikty, z wyjątkiem tych, które są mniejsze na serwerze, ponieważ podejrzewamy, że ich transfer został przerwany.",
|
||||
"save": "Zapisz",
|
||||
"schedule": "Harmonogram",
|
||||
"search": "Szukaj",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Aktualizuj",
|
||||
"upload": "Wyślij",
|
||||
"openFile": "Otwórz plik",
|
||||
"openDirect": "Otwórz bezpośrednio",
|
||||
"discardChanges": "Odrzuć",
|
||||
"stopSearch": "Zatrzymaj wyszukiwanie",
|
||||
"saveChanges": "Zapisz zmiany",
|
||||
"editAsText": "Edytuj jako tekst",
|
||||
"increaseFontSize": "Zwiększ rozmiar czcionki",
|
||||
"decreaseFontSize": "Zmniejsz rozmiar czcionki"
|
||||
"decreaseFontSize": "Zmniejsz rozmiar czcionki",
|
||||
"overrideAll": "Zastąp wszystkie pliki w folderze docelowym",
|
||||
"skipAll": "Pomiń wszystkie pliki powodujące konflikty",
|
||||
"renameAll": "Zmień nazwy wszystkich plików (utwórz kopię)",
|
||||
"singleDecision": "Podejmij decyzję dla każdego pliku powodującego konflikt"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Pobierz plik",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Przecinek (,)",
|
||||
"semicolon": "Średnik (;)",
|
||||
"both": "Zarówno (,), jak i (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "Kodowanie pliku"
|
||||
},
|
||||
"help": {
|
||||
"click": "zaznacz plik lub folder",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Wybierz opcję przesyłania.",
|
||||
"optionalPassword": "Opcjonalne hasło",
|
||||
"resolution": "Rozdzielczość",
|
||||
"discardEditorChanges": "Czy na pewno chcesz odrzucić wprowadzone zmiany?"
|
||||
"discardEditorChanges": "Czy na pewno chcesz odrzucić wprowadzone zmiany?",
|
||||
"replaceOrSkip": "Zastąp lub pomiń pliki",
|
||||
"resolveConflict": "Które pliki chcesz zachować?",
|
||||
"singleConflictResolve": "Jeśli wybierzesz obie wersje, do nazwy kopiowanego pliku zostanie dodany numer.",
|
||||
"fastConflictResolve": "W folderze docelowym liczba plików o tej samej nazwie to {count}.",
|
||||
"uploadingFiles": "Wysyłanie plików",
|
||||
"filesInOrigin": "Pliki w miejscu pochodzenia",
|
||||
"filesInDest": "Pliki w miejscu docelowym",
|
||||
"override": "Zastąp",
|
||||
"skip": "Pomiń",
|
||||
"forbiddenError": "Błąd zabronionego dostępu",
|
||||
"currentPassword": "Twoje hasło",
|
||||
"currentPasswordMessage": "Wpisz swoje hasło, aby zatwierdzić tę czynność."
|
||||
},
|
||||
"search": {
|
||||
"images": "Obrazy",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "Wykonywanie poleceń",
|
||||
"modify": "Edytowanie plików",
|
||||
"rename": "Zmienianie nazwy lub przenoszenie plików i katalogów",
|
||||
"share": "Udostępnianie plików"
|
||||
"share": "Udostępnianie plików (wymaga uprawnienia do pobierania)"
|
||||
},
|
||||
"permissions": "Uprawnienia",
|
||||
"permissionsHelp": "Możesz ustawić użytkownika jako administratora lub wybrać uprawnienia indywidualnie. Jeśli wybierzesz „Administrator”, wszystkie pozostałe opcje zostaną automatycznie zaznaczone. Zarządzanie użytkownikami pozostaje przywilejem administratora.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "Twoje aktualne hasło"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "Wykorzystano {used} z {total}",
|
||||
"help": "Pomoc",
|
||||
"hugoNew": "Nowy Hugo",
|
||||
"login": "Zaloguj",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Atualizar",
|
||||
"upload": "Enviar",
|
||||
"openFile": "Abrir",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Baixar arquivo",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "selecionar pasta ou arquivo",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Selecione uma opção para enviar.",
|
||||
"optionalPassword": "Senha opcional",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagens",
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
{
|
||||
"buttons": {
|
||||
"cancel": "Cancelar",
|
||||
"clear": "Limpar",
|
||||
"close": "Fechar",
|
||||
"continue": "Continuar",
|
||||
"copy": "Copiar",
|
||||
"copyFile": "Copiar ficheiro",
|
||||
"copyToClipboard": "Copiar para a área de transferência",
|
||||
"copyDownloadLinkToClipboard": "Copiar link do download para a área de transferência",
|
||||
"create": "Criar",
|
||||
"delete": "Eliminar",
|
||||
"download": "Descarregar",
|
||||
"file": "Ficheiro",
|
||||
"folder": "Pasta",
|
||||
"fullScreen": "Alternar ecrã inteiro",
|
||||
"hideDotfiles": "Ocultar ficheiros começados por ponto",
|
||||
"info": "Info",
|
||||
"more": "Mais",
|
||||
"move": "Mover",
|
||||
"moveFile": "Mover ficheiro",
|
||||
"new": "Novo",
|
||||
"next": "Seguinte",
|
||||
"ok": "OK",
|
||||
"permalink": "Obter link permanente",
|
||||
"previous": "Anterior",
|
||||
"preview": "Pré-visualizar",
|
||||
"publish": "Publicar",
|
||||
"rename": "Mudar o nome",
|
||||
"replace": "Substituir",
|
||||
"reportIssue": "Reportar problema",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Guardar",
|
||||
"schedule": "Agendar",
|
||||
"search": "Pesquisar",
|
||||
"select": "Seleccionar",
|
||||
"selectMultiple": "Seleccionar vários",
|
||||
"share": "Partilhar",
|
||||
"shell": "Alternar shell",
|
||||
"submit": "Enviar",
|
||||
"switchView": "Alterar vista",
|
||||
"toggleSidebar": "Alternar barra lateral",
|
||||
"update": "Actualizar",
|
||||
"upload": "Enviar",
|
||||
"openFile": "Abrir ficheiro",
|
||||
"openDirect": "Ver raw",
|
||||
"discardChanges": "Descartar",
|
||||
"stopSearch": "Parar a procura",
|
||||
"saveChanges": "Guardar alterações",
|
||||
"editAsText": "Editar como Texto",
|
||||
"increaseFontSize": "Aumentar o tamanho da letra",
|
||||
"decreaseFontSize": "Diminuir o tamanho da letra",
|
||||
"overrideAll": "Substituir todos os ficheiros na pasta de destino",
|
||||
"skipAll": "Ignorar todos os ficheiros em conflito",
|
||||
"renameAll": "Mudar o nome a todos os ficheiros (criar uma cópia)",
|
||||
"singleDecision": "Decidir para cada ficheiro em conflito"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar Ficheiro",
|
||||
"downloadFolder": "Descarregar Pasta",
|
||||
"downloadSelected": "Descarregar Seleccionados"
|
||||
},
|
||||
"upload": {
|
||||
"abortUpload": "Tem a certeza que quer abortar?"
|
||||
},
|
||||
"errors": {
|
||||
"forbidden": "Não tem permissão para aceder a isto.",
|
||||
"internal": "Algo correu mal.",
|
||||
"notFound": "Esta localização não pôde ser alcançada.",
|
||||
"connection": "Este servidor não pôde ser alcançado."
|
||||
},
|
||||
"files": {
|
||||
"body": "Corpo",
|
||||
"closePreview": "Fechar pré-visualização",
|
||||
"files": "Ficheiros",
|
||||
"folders": "Pastas",
|
||||
"home": "Início",
|
||||
"lastModified": "Última modificação",
|
||||
"loading": "A carregar...",
|
||||
"lonely": "Sinto-me sózinho...",
|
||||
"metadata": "Metadados",
|
||||
"multipleSelectionEnabled": "Selecção múltipla activada",
|
||||
"name": "Nome",
|
||||
"size": "Tamanho",
|
||||
"sortByLastModified": "Ordenar pela última modificação",
|
||||
"sortByName": "Ordenar pelo nome",
|
||||
"sortBySize": "Ordenar pelo tamanho",
|
||||
"noPreview": "Pré-visualização não disponível para este ficheiro.",
|
||||
"csvTooLarge": "O ficheiro CSV é demasiado grande para pré-visualizar (>5MB). Por favor, descarregue-o para visualizá-lo.",
|
||||
"csvLoadFailed": "Falha ao carregar o ficheiro CSV.",
|
||||
"showingRows": "A mostrar {count} linha(s)",
|
||||
"columnSeparator": "Separador de Coluna",
|
||||
"csvSeparators": {
|
||||
"comma": "Vírgula (,)",
|
||||
"semicolon": "Ponto e vírgula (;)",
|
||||
"both": "Ambos (,) e (;)"
|
||||
},
|
||||
"fileEncoding": "Codificação do Ficheiro"
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar ficheiro ou pasta",
|
||||
"ctrl": {
|
||||
"click": "seleccionar vários ficheiros e pastas",
|
||||
"f": "abre a pesquisa",
|
||||
"s": "guarda um ficheiro ou descarrega a pasta onde estiver"
|
||||
},
|
||||
"del": "elimina os itens seleccionados",
|
||||
"doubleClick": "abre um ficheiro ou pasta",
|
||||
"esc": "limpa a selecção e/ou fecha o pedido",
|
||||
"f1": "esta informação",
|
||||
"f2": "muda o nome ao ficheiro",
|
||||
"help": "Ajuda"
|
||||
},
|
||||
"login": {
|
||||
"createAnAccount": "Criar uma conta",
|
||||
"loginInstead": "Já tenho uma conta",
|
||||
"password": "Palavra-passe",
|
||||
"passwordConfirm": "Confirmação da Palavra-passe",
|
||||
"passwordsDontMatch": "As palavras-passe não coincidem",
|
||||
"signup": "Registar",
|
||||
"submit": "Entrar",
|
||||
"username": "Nome de utilizador",
|
||||
"usernameTaken": "O nome de utilizador já existe",
|
||||
"wrongCredentials": "Credenciais erradas",
|
||||
"passwordTooShort": "A palavra-passe tem de ter pelo menos {min} caracteres",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Foi terminada a sessão por inactividade."
|
||||
}
|
||||
},
|
||||
"permanent": "Permanente",
|
||||
"prompts": {
|
||||
"copy": "Copiar",
|
||||
"copyMessage": "Escolha a localização para copiar os ficheiros:",
|
||||
"currentlyNavigating": "Actualmente a navegar em:",
|
||||
"deleteMessageMultiple": "Tem a certeza que quer eliminar {count} ficheiro(s)?",
|
||||
"deleteMessageSingle": "Tem a certeza que quer eliminar este ficheiro/pasta?",
|
||||
"deleteMessageShare": "Tem a certeza que quer eliminar esta partilha ({path})?",
|
||||
"deleteUser": "Tem a certeza que quer eliminar este utilizador?",
|
||||
"deleteTitle": "Eliminar ficheiros",
|
||||
"displayName": "Nome a mostrar:",
|
||||
"download": "Descarregar ficheiros",
|
||||
"downloadMessage": "Escolha o formato que pretende descarregar.",
|
||||
"error": "Algo correu mal.",
|
||||
"fileInfo": "Informação do Ficheiro",
|
||||
"filesSelected": "{count} ficheiro(s) seleccionado(s)",
|
||||
"lastModified": "última modificação",
|
||||
"move": "Mover",
|
||||
"moveMessage": "Escolha uma nova casa para o(s) seu(s) ficheiro(s)/pasta(s):",
|
||||
"newArchetype": "Criar uma nova publicação baseado no archetype. O seu ficheiro irá ser criado no conteúdo da pasta.",
|
||||
"newDir": "Nova pasta",
|
||||
"newDirMessage": "Nomeie a sua nova pasta.",
|
||||
"newFile": "Novo ficheiro",
|
||||
"newFileMessage": "Nomeie o seu novo ficheiro.",
|
||||
"numberDirs": "Número de pastas",
|
||||
"numberFiles": "Número de ficheiros",
|
||||
"rename": "Mudar o nome",
|
||||
"renameMessage": "Introduza um novo nome para",
|
||||
"replace": "Substituir",
|
||||
"replaceMessage": "Um dos ficheiros que está a tentar enviar tem um nome em conflito. Quer ignorar este ficheiro e continuar a enviar ou substituir o existente?\none?\n",
|
||||
"schedule": "Agendar",
|
||||
"scheduleMessage": "Escolha a data e hora para agendar a publicação desta publicação.",
|
||||
"show": "Mostrar",
|
||||
"size": "Tamanho",
|
||||
"upload": "Enviar",
|
||||
"uploadFiles": "A enviar {files} ficheiros...",
|
||||
"uploadMessage": "Seleccione uma opção para enviar.",
|
||||
"optionalPassword": "Palavra-passe opcional",
|
||||
"resolution": "Resolução",
|
||||
"discardEditorChanges": "Tem a certeza que quer descartar as alterações que fez?",
|
||||
"replaceOrSkip": "Substituir ou ignorar ficheiros",
|
||||
"resolveConflict": "Que ficheiros quer manter?",
|
||||
"singleConflictResolve": "Se seleccionar ambas as versões, um número será adicionar ao nome do ficheiro copiado.",
|
||||
"fastConflictResolve": "Na pasta de destino há {count} ficheiros com o mesmo nome.",
|
||||
"uploadingFiles": "A enviar ficheiros",
|
||||
"filesInOrigin": "Ficheiros na origem",
|
||||
"filesInDest": "Ficheiros no destino",
|
||||
"override": "Substituir",
|
||||
"skip": "Ignorar",
|
||||
"forbiddenError": "Erro de Proibido",
|
||||
"currentPassword": "A sua palavra-passe",
|
||||
"currentPasswordMessage": "Insira a sua palavra-passe para validar esta acção."
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagens",
|
||||
"music": "Música",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Prima Enter para procurar...",
|
||||
"search": "Procurar...",
|
||||
"typeToSearch": "Digite para procurar...",
|
||||
"types": "Tipos",
|
||||
"video": "Vídeo"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Editor de temas audaz",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrador",
|
||||
"allowCommands": "Executar comandos",
|
||||
"allowEdit": "Editar, mudar o nome e eliminar ficheiros ou pastas",
|
||||
"allowNew": "Criar novos ficheiros e pastas",
|
||||
"allowPublish": "Publicar novas publicações e páginas",
|
||||
"allowSignup": "Permitir que utilizadores se registem",
|
||||
"hideLoginButton": "Ocultar o botão de início de sessão nas páginas públicas",
|
||||
"avoidChanges": "(deixe em branco para evitar alterações)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Caminho da pasta da marca",
|
||||
"brandingHelp": "Pode personalizar a aparência da instânciado seu File Browser\nao alterar o seu nome, alterar o logótipo, adicionar estilos personalizados e até mesmo desactivar links externos para o Github.\nPara mais informação acerca de personalização de marca, por favor, veja {0}.",
|
||||
"changePassword": "Alterar Palavra-passe",
|
||||
"commandRunner": "Executador de comandos",
|
||||
"commandRunnerHelp": "Aqui pode definir comandos que são executados nos eventos nomeados. Tem de escrever um por linha. A variável de ambiente {0} e {1} irá estar disponível, sendo {0} relativo a {1}.\nPara mais informação acerca desta funcionalidade e variáveis de ambiente disponíveis, por favor, leia o {2}.",
|
||||
"commandsUpdated": "Comandos actualizados!",
|
||||
"createUserDir": "Criar automaticamente a pasta da casa do utilizador quando adicionar um novo utilizador",
|
||||
"minimumPasswordLength": "Comprimento mínimo da palavra-passe",
|
||||
"tusUploads": "Envios aos Bocados",
|
||||
"tusUploadsHelp": "O File Browser suporta envio de ficheiros aos bocados, permitindo a criação de envios eficiente, de confiança e retomáveis de ficheiros aos bocados mesmo em redes instáveis.",
|
||||
"tusUploadsChunkSize": "Indica o tamanho máximo do pedido (envios directos serão utilizadas para envios pequenos). Pode introduzir um valor inteiro para tamanhos de byte ou uma cadeia como 10MB, 1GB, etc.",
|
||||
"tusUploadsRetryCount": "Número de tentativas a realizar se um bocado falha ao ser enviado.",
|
||||
"userHomeBasePath": "O caminho base para a pasta da casa do utilizador",
|
||||
"userScopeGenerationPlaceholder": "O escopo será gerado automaticamente",
|
||||
"createUserHomeDirectory": "Criar pasta da casa do utilizador",
|
||||
"customStylesheet": "Folha de estilo Personalizada",
|
||||
"defaultUserDescription": "Estes são as definições por defeito para novos utilizadores",
|
||||
"disableExternalLinks": "Eliminar links externos (excepto documentação)",
|
||||
"disableUsedDiskPercentage": "Desactivar gráfico de percentagem de uso de disco",
|
||||
"documentation": "documentação\ndocumentation",
|
||||
"examples": "Exemplos",
|
||||
"executeOnShell": "Executar na shell",
|
||||
"executeOnShellDescription": "Por defeito, o File Browser executa os comandos ao chamar os seus binários\ndirectamenta. Se quiser executá-los mesmo na shell (como o Bash ou PoweShell), pode defini-lo aqui com os argumentos pedidos e flags. Se definido, o comando que executar será acrescentado como um argumento. Isto aplica-se a ambos comandos de utilizador e hooks de eventos.",
|
||||
"globalRules": "Esta é um conjunto global de regras de permissão e negação. Elas aplicam-se a cada utilizador. Pode definir regras especificas em cada definições de utilizador para contornar estas.",
|
||||
"globalSettings": "Definições Globais",
|
||||
"hideDotfiles": "Ocultar ficheiros começados por ponto",
|
||||
"insertPath": "Introduza o caminho",
|
||||
"insertRegex": "Introduza expressão regex",
|
||||
"instanceName": "Nome da instância",
|
||||
"language": "Idioma",
|
||||
"lockPassword": "Impedir o utilizador de alterar a palavra-passe",
|
||||
"newPassword": "A sua nova palavra-passe",
|
||||
"newPasswordConfirm": "Confirmar a sua palavra-passe",
|
||||
"newUser": "Novo Utilizador",
|
||||
"password": "Palavra-passe",
|
||||
"passwordUpdated": "Palavras-passe não coincidem!",
|
||||
"path": "Caminho",
|
||||
"perm": {
|
||||
"create": "Criar ficheiros e pastas",
|
||||
"delete": "Eliminar ficheiros e pastas",
|
||||
"download": "Descarregar",
|
||||
"execute": "Executar comandos",
|
||||
"modify": "Editar ficheiros",
|
||||
"rename": "Mudar o nome ou mover ficheiros e pastas",
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "Permissões",
|
||||
"permissionsHelp": "Pode definir um utilizador um administrador ou escolher as permissões individualmente. Se escolher \"Administrador\", todas as outras opções serão automaticamente marcadas. A gestão de utilizadores continua a ser um privilégio de um administrador.\n",
|
||||
"profileSettings": "Definições de Perfil",
|
||||
"redirectAfterCopyMove": "Redireccionar para o destino depois de copiar/mover",
|
||||
"ruleExample1": "impede o acesso a qualquer ficheiro começado por ponto (como .git ou .gitignore) em cada pasta.\n",
|
||||
"ruleExample2": "bloqueia o acesso ao ficheiro chamado Caddyfile na raíz de um escopo.",
|
||||
"rules": "Regras",
|
||||
"rulesHelp": "Aqui pode definir um conjunto de regras de permissão e negação para este utilizador especifico. Os ficheiros bloqueados não serão mostrados nas listas e não são acessíveis ao utilizador. Suportamos regex e caminhos relativos ao escopo do utilizador.\n",
|
||||
"scope": "Escopo",
|
||||
"setDateFormat": "Definir formato exacto da data",
|
||||
"settingsUpdated": "Definições actualizadas!",
|
||||
"shareDuration": "Duração da Partilha",
|
||||
"shareManagement": "Gestão de Partilhas",
|
||||
"shareDeleted": "Partilha eliminada!",
|
||||
"singleClick": "Utilizar cliques únicos para abrir ficheiros e pastas",
|
||||
"themes": {
|
||||
"default": "Predefinição do sistema",
|
||||
"dark": "Escuro",
|
||||
"light": "Claro",
|
||||
"title": "Tema"
|
||||
},
|
||||
"user": "Utilizador",
|
||||
"userCommands": "Comandos",
|
||||
"userCommandsHelp": "Uma lista separada por espaços com os comandos disponíveis para este utilizador. Exemplo:\n",
|
||||
"userCreated": "Utilizador criado!",
|
||||
"userDefaults": "Definições de utilizador por defeito",
|
||||
"userDeleted": "Utilizador eliminado!",
|
||||
"userManagement": "Gestão de Utilizadores",
|
||||
"userUpdated": "Utilizador actualizado!",
|
||||
"username": "Nome de utilizador",
|
||||
"users": "Utilizadores",
|
||||
"currentPassword": "A Sua Palavra-passe Actual"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Ajuda",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "Iniciar sessão",
|
||||
"logout": "Sair",
|
||||
"myFiles": "Os meus ficheiros",
|
||||
"newFile": "Novo ficheiro",
|
||||
"newFolder": "Nova pasta",
|
||||
"preview": "Pré-visualizar",
|
||||
"settings": "Definições",
|
||||
"signup": "Registar",
|
||||
"siteSettings": "Definições do Site"
|
||||
},
|
||||
"success": {
|
||||
"linkCopied": "Link copiado!"
|
||||
},
|
||||
"time": {
|
||||
"days": "Dias",
|
||||
"hours": "Horas",
|
||||
"minutes": "Minutos",
|
||||
"seconds": "Segundos",
|
||||
"unit": "Unidade de Tempo"
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Alterar nome",
|
||||
"replace": "Substituir",
|
||||
"reportIssue": "Reportar erro",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Guardar",
|
||||
"schedule": "Agendar",
|
||||
"search": "Pesquisar",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Atualizar",
|
||||
"upload": "Enviar",
|
||||
"openFile": "Open file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar ficheiro",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "selecionar pasta ou ficheiro",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Select an option to upload.",
|
||||
"optionalPassword": "Optional password",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagens",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "Executar comandos",
|
||||
"modify": "Editar ficheiros",
|
||||
"rename": "Alterar o nome ou mover ficheiros e pastas",
|
||||
"share": "Partilhar ficheiros"
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "Permissões",
|
||||
"permissionsHelp": "Pode definir o utilizador como administrador ou escolher as permissões manualmente. Se selecionar a opção \"Administrador\", todas as outras opções serão automaticamente selecionadas. A gestão dos utilizadores é um privilégio restringido aos administradores.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "Your Current Password"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Ajuda",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "Entrar",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Actualizează",
|
||||
"upload": "Încarcă",
|
||||
"openFile": "Open file",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarcă fișier",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "alege fișier sau director",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Select an option to upload.",
|
||||
"optionalPassword": "Optional password",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagini",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "Переименовать",
|
||||
"replace": "Перезаписать",
|
||||
"reportIssue": "Сообщить о проблеме",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "Сохранить",
|
||||
"schedule": "Планировка",
|
||||
"search": "Поиск",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "Обновить",
|
||||
"upload": "Загрузить",
|
||||
"openFile": "Открыть файл",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Отказаться",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Сохранить",
|
||||
"editAsText": "Редактировать как текст",
|
||||
"increaseFontSize": "Увеличить размер шрифта",
|
||||
"decreaseFontSize": "Уменьшить размер шрифта"
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Скачать файл",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "Запятая (,)",
|
||||
"semicolon": "Точка с запятой (;)",
|
||||
"both": "Оба варианта — (,) и (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "выбрать файл или каталог",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "Выберите вариант для загрузки.",
|
||||
"optionalPassword": "Необязательный пароль",
|
||||
"resolution": "Разрешение",
|
||||
"discardEditorChanges": "Вы действительно желаете отменить ваши правки?"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Изображения",
|
||||
@@ -227,7 +247,7 @@
|
||||
"execute": "Выполнять команды",
|
||||
"modify": "Редактировать файлы",
|
||||
"rename": "Переименовывать или перемещать файлы и каталоги",
|
||||
"share": "Делиться файлами"
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "Права доступа",
|
||||
"permissionsHelp": "Можно настроить пользователя как администратора или выбрать разрешения индивидуально. При выборе \"Администратор\", все остальные параметры будут автоматически выбраны. Управление пользователями - привилегия администратора.\n",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "Your Current Password"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "Помощь",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "Войти",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Aktualizovať",
|
||||
"upload": "Nahrať",
|
||||
"openFile": "Otvoriť súbor",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Zahodiť",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Uložiť zmeny",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stiahnuť súbor",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "vyberie súbor alebo priečinok",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Zvoľte možnosť nahrávania.",
|
||||
"optionalPassword": "Voliteľné heslo",
|
||||
"resolution": "Rozlíšenie",
|
||||
"discardEditorChanges": "Naozaj chcete zahodiť vykonané zmeny?"
|
||||
"discardEditorChanges": "Naozaj chcete zahodiť vykonané zmeny?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Obrázky",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Uppdatera",
|
||||
"upload": "Ladda upp",
|
||||
"openFile": "Öppna fil",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Förkasta",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Spara ändringar",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Ladda ner fil",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "välj fil eller mapp",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Välj ett alternativ att ladda upp.",
|
||||
"optionalPassword": "Valfritt lösenord",
|
||||
"resolution": "Upplösning",
|
||||
"discardEditorChanges": "Är du säker på att du vill förkasta ändringarna du gjort?"
|
||||
"discardEditorChanges": "Är du säker på att du vill förkasta ändringarna du gjort?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Bilder",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Güncelle",
|
||||
"upload": "Yükle",
|
||||
"openFile": "Dosyayı aç",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Discard",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Dosyayı indir",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "dosya veya klasör seçin",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Yüklemek için bir seçenek belirleyin.",
|
||||
"optionalPassword": "İsteğe bağlı şifre",
|
||||
"resolution": "Resolution",
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
|
||||
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Görseller",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Оновити",
|
||||
"upload": "Вивантажити",
|
||||
"openFile": "Відкрити файл",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Скасувати",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Завантажити файл",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "вибрати файл чи каталог",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Виберіть варіант для вивантаження.",
|
||||
"optionalPassword": "Необов'язковий пароль",
|
||||
"resolution": "Розширення",
|
||||
"discardEditorChanges": "Чи дійсно ви хочете скасувати поточні зміни?"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Зображення",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "Cập nhật",
|
||||
"upload": "Tải lên",
|
||||
"openFile": "Mở tệp",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "Hủy bỏ thay đổi",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Tải xuống tệp tin",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "chọn tập tin hoặc thư mục",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "Chọn một tùy chọn để tải lên.",
|
||||
"optionalPassword": "Mật khẩu tùy chọn",
|
||||
"resolution": "Độ phân giải",
|
||||
"discardEditorChanges": "Bạn có chắc chắn muốn hủy bỏ các thay đổi đã thực hiện không?"
|
||||
"discardEditorChanges": "Bạn có chắc chắn muốn hủy bỏ các thay đổi đã thực hiện không?",
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "Hình ảnh",
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"rename": "重命名",
|
||||
"replace": "替换",
|
||||
"reportIssue": "报告问题",
|
||||
"resumeTransfer": "Resume previous transfer",
|
||||
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
|
||||
"save": "保存",
|
||||
"schedule": "计划",
|
||||
"search": "搜索",
|
||||
@@ -42,12 +44,17 @@
|
||||
"update": "更新",
|
||||
"upload": "上传",
|
||||
"openFile": "打开文件",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "放弃更改",
|
||||
"stopSearch": "停止搜索",
|
||||
"saveChanges": "保存更改",
|
||||
"editAsText": "以文本形式编辑",
|
||||
"increaseFontSize": "增大字体大小",
|
||||
"decreaseFontSize": "减小字体大小"
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下载文件",
|
||||
@@ -88,7 +95,8 @@
|
||||
"comma": "逗号 (,)",
|
||||
"semicolon": "分号 (;)",
|
||||
"both": "逗号 (,) 和分号 (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "选择文件或文件夹",
|
||||
@@ -159,7 +167,19 @@
|
||||
"uploadMessage": "选择上传选项。",
|
||||
"optionalPassword": "密码(选填,不填即无密码)",
|
||||
"resolution": "分辨率",
|
||||
"discardEditorChanges": "你确定要放弃所做的更改吗?"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "图像",
|
||||
@@ -227,12 +247,12 @@
|
||||
"execute": "执行命令",
|
||||
"modify": "编辑",
|
||||
"rename": "重命名或移动文件和文件夹",
|
||||
"share": "分享文件"
|
||||
"share": "Share files (require download permission)"
|
||||
},
|
||||
"permissions": "权限",
|
||||
"permissionsHelp": "你可以将该用户设置为管理员或单独选择各项权限。如果你选择了“管理员”,则其他的选项会被自动选中,同时该用户可以管理其他用户。\n",
|
||||
"profileSettings": "个人设置",
|
||||
"redirectAfterCopyMove": "Redirect to destination after copy/move",
|
||||
"redirectAfterCopyMove": "复制/移动后转到目标位置",
|
||||
"ruleExample1": "阻止用户访问所有文件夹下任何以 . 开头的文件(隐藏文件, 例如: .git, .gitignore)。\n",
|
||||
"ruleExample2": "阻止用户访问其目录范围的根目录下名为 Caddyfile 的文件。",
|
||||
"rules": "规则",
|
||||
@@ -263,6 +283,7 @@
|
||||
"currentPassword": "您当前的密码"
|
||||
},
|
||||
"sidebar": {
|
||||
"diskUsed": "{used} of {total} used",
|
||||
"help": "帮助",
|
||||
"hugoNew": "Hugo 新建",
|
||||
"login": "登录",
|
||||
|
||||
@@ -42,12 +42,17 @@
|
||||
"update": "更新",
|
||||
"upload": "上傳",
|
||||
"openFile": "開啟檔案",
|
||||
"openDirect": "View raw",
|
||||
"discardChanges": "放棄變更",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
"decreaseFontSize": "Decrease font size",
|
||||
"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"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下載檔案",
|
||||
@@ -88,7 +93,8 @@
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"fileEncoding": "File Encoding"
|
||||
},
|
||||
"help": {
|
||||
"click": "選擇檔案或目錄",
|
||||
@@ -159,7 +165,19 @@
|
||||
"uploadMessage": "選擇上傳項。",
|
||||
"optionalPassword": "密碼(選填,不填即無密碼)",
|
||||
"resolution": "解析度",
|
||||
"discardEditorChanges": "你確定要放棄所做的變更嗎?"
|
||||
"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",
|
||||
"currentPassword": "Your password",
|
||||
"currentPasswordMessage": "Enter your password to validate this action."
|
||||
},
|
||||
"search": {
|
||||
"images": "影像",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { disableExternal } from "@/utils/constants";
|
||||
import { createApp } from "vue";
|
||||
import VueNumberInput from "@chenfengyuan/vue-number-input";
|
||||
import VueLazyload from "vue-lazyload";
|
||||
import { createVfm } from "vue-final-modal";
|
||||
import Toast, { POSITION, useToast } from "vue-toastification";
|
||||
import type {
|
||||
ToastOptions,
|
||||
@@ -27,7 +26,6 @@ dayjs.extend(relativeTime);
|
||||
dayjs.extend(duration);
|
||||
|
||||
const pinia = createPinia(router);
|
||||
const vfm = createVfm();
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
@@ -39,7 +37,6 @@ app.use(Toast, {
|
||||
newestOnTop: true,
|
||||
} satisfies PluginOptions);
|
||||
|
||||
app.use(vfm);
|
||||
app.use(i18n);
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
|
||||
@@ -145,8 +145,11 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: "/:catchAll(.*)*",
|
||||
redirect: (to: RouteLocation) =>
|
||||
`/files/${[...to.params.catchAll].join("/")}`,
|
||||
redirect: (to: RouteLocation) => {
|
||||
const catchAll = to.params.catchAll;
|
||||
if (!catchAll) return "/files/";
|
||||
return `/files/${Array.isArray(catchAll) ? catchAll.join("/") : catchAll}`;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export const useLayoutStore = defineStore("layout", {
|
||||
});
|
||||
},
|
||||
closeHovers() {
|
||||
this.prompts.shift()?.close?.();
|
||||
this.prompts.pop()?.close?.();
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearLayout() {
|
||||
|
||||
Vendored
+30
@@ -21,6 +21,7 @@ interface Resource extends ResourceBase {
|
||||
index: number;
|
||||
subtitles?: string[];
|
||||
content?: string;
|
||||
rawContent?: ArrayBuffer;
|
||||
}
|
||||
|
||||
interface ResourceItem extends ResourceBase {
|
||||
@@ -51,9 +52,38 @@ type DownloadFormat =
|
||||
interface ClipItem {
|
||||
from: string;
|
||||
name: string;
|
||||
size?: number;
|
||||
modified?: string;
|
||||
}
|
||||
|
||||
interface BreadCrumb {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface ConflictingItem {
|
||||
lastModified: number | string | undefined;
|
||||
size: number | undefined;
|
||||
}
|
||||
|
||||
interface ConflictingResource {
|
||||
index: number;
|
||||
name: string;
|
||||
origin: ConflictingItem;
|
||||
dest: ConflictingItem;
|
||||
checked: Array<"origin" | "dest", "origin-resume">;
|
||||
isSmallerOnServer?: boolean;
|
||||
}
|
||||
|
||||
interface CsvData {
|
||||
headers: string[];
|
||||
rows: string[][];
|
||||
}
|
||||
|
||||
interface RecursiveEntry {
|
||||
path: string;
|
||||
name: string;
|
||||
size: number;
|
||||
modified: string;
|
||||
isDir: boolean;
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -10,4 +10,8 @@ declare global {
|
||||
// TODO: no idea what the exact type is
|
||||
__vue__: any;
|
||||
}
|
||||
|
||||
interface HTMLElement {
|
||||
clickOutsideEvent?: (event: Event) => void;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -16,7 +16,9 @@ interface UploadEntry {
|
||||
size: number;
|
||||
isDir: boolean;
|
||||
fullPath?: string;
|
||||
to?: string;
|
||||
file?: File;
|
||||
overwrite?: boolean;
|
||||
}
|
||||
|
||||
type UploadList = UploadEntry[];
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
/**
|
||||
* Reproduces the path-building logic from scanFiles() in upload.ts (lines 118-138).
|
||||
*
|
||||
* readReaderContent() is called when traversing a dropped folder. The browser's
|
||||
* FileSystemDirectoryReader.readEntries() returns entries in batches — you must
|
||||
* call it repeatedly until it returns an empty array. Each recursive call in the
|
||||
* current code appends "/" to the directory, so the second batch and beyond get
|
||||
* double (or triple, etc.) slashes in the constructed fullPath.
|
||||
*/
|
||||
|
||||
type Entry = { name: string; isFile: boolean; isDirectory: boolean };
|
||||
|
||||
function simulateScanFiles(dirName: string, entryBatches: Entry[][]): string[] {
|
||||
const paths: string[] = [];
|
||||
let batchIndex = 0;
|
||||
|
||||
// Mirrors readEntry() for files — records fullPath as `${directory}${file.name}`
|
||||
function readEntry(entry: Entry, directory = ""): void {
|
||||
if (entry.isFile) {
|
||||
paths.push(`${directory}${entry.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Mirrors readReaderContent() from upload.ts lines 118-138
|
||||
function readReaderContent(directory: string): void {
|
||||
const entries =
|
||||
batchIndex < entryBatches.length ? entryBatches[batchIndex] : [];
|
||||
batchIndex++;
|
||||
|
||||
if (entries.length > 0) {
|
||||
const dirWithSlash = directory.endsWith("/")
|
||||
? directory
|
||||
: `${directory}/`;
|
||||
for (const entry of entries) {
|
||||
readEntry(entry, dirWithSlash);
|
||||
}
|
||||
readReaderContent(dirWithSlash);
|
||||
}
|
||||
}
|
||||
|
||||
// Initial call mirrors readEntry() for a directory — upload.ts line 111-114
|
||||
readReaderContent(dirName);
|
||||
return paths;
|
||||
}
|
||||
|
||||
describe("scanFiles path construction", () => {
|
||||
it("should not produce double slashes when readEntries returns multiple batches", () => {
|
||||
// Two batches: simulates a large directory where the browser splits
|
||||
// readEntries() results across multiple calls
|
||||
const paths = simulateScanFiles("TestFolder", [
|
||||
[
|
||||
{ name: "file1.xlsx", isFile: true, isDirectory: false },
|
||||
{ name: "file2.xlsx", isFile: true, isDirectory: false },
|
||||
],
|
||||
[{ name: "file3.xlsx", isFile: true, isDirectory: false }],
|
||||
]);
|
||||
|
||||
expect(paths).toHaveLength(3);
|
||||
|
||||
for (const p of paths) {
|
||||
expect(p, `path "${p}" contains double slash`).not.toContain("//");
|
||||
}
|
||||
});
|
||||
|
||||
it("single batch should work fine (no regression)", () => {
|
||||
const paths = simulateScanFiles("TestFolder", [
|
||||
[{ name: "file1.xlsx", isFile: true, isDirectory: false }],
|
||||
]);
|
||||
|
||||
expect(paths).toEqual(["TestFolder/file1.xlsx"]);
|
||||
});
|
||||
});
|
||||
@@ -1,64 +0,0 @@
|
||||
export interface CsvData {
|
||||
headers: string[];
|
||||
rows: string[][];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CSV content into headers and rows
|
||||
* Supports quoted fields and handles commas within quotes
|
||||
*/
|
||||
export function parseCSV(
|
||||
content: string,
|
||||
columnSeparator: Array<string>
|
||||
): CsvData {
|
||||
if (!content || content.trim().length === 0) {
|
||||
return { headers: [], rows: [] };
|
||||
}
|
||||
|
||||
const lines = content.split(/\r?\n/);
|
||||
const result: string[][] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim().length === 0) continue;
|
||||
|
||||
const row: string[] = [];
|
||||
let currentField = "";
|
||||
let inQuotes = false;
|
||||
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const char = line[i];
|
||||
const nextChar = line[i + 1];
|
||||
|
||||
if (char === '"') {
|
||||
if (inQuotes && nextChar === '"') {
|
||||
// Escaped quote
|
||||
currentField += '"';
|
||||
i++; // Skip next quote
|
||||
} else {
|
||||
// Toggle quote state
|
||||
inQuotes = !inQuotes;
|
||||
}
|
||||
} else if (columnSeparator.includes(char) && !inQuotes) {
|
||||
// Field separator
|
||||
row.push(currentField);
|
||||
currentField = "";
|
||||
} else {
|
||||
currentField += char;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last field
|
||||
row.push(currentField);
|
||||
result.push(row);
|
||||
}
|
||||
|
||||
if (result.length === 0) {
|
||||
return { headers: [], rows: [] };
|
||||
}
|
||||
|
||||
// First row is headers
|
||||
const headers = result[0];
|
||||
const rows = result.slice(1);
|
||||
|
||||
return { headers, rows };
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
export const availableEncodings = [
|
||||
// encodings
|
||||
"utf-8",
|
||||
"ibm866",
|
||||
"iso-8859-2",
|
||||
"iso-8859-3",
|
||||
"iso-8859-4",
|
||||
"iso-8859-5",
|
||||
"iso-8859-6",
|
||||
"iso-8859-7",
|
||||
"iso-8859-8",
|
||||
"iso-8859-8-i",
|
||||
"iso-8859-10",
|
||||
"iso-8859-13",
|
||||
"iso-8859-14",
|
||||
"iso-8859-15",
|
||||
"iso-8859-16",
|
||||
"koi8-r",
|
||||
"koi8-u",
|
||||
"macintosh",
|
||||
"windows-874",
|
||||
"windows-1250",
|
||||
"windows-1251",
|
||||
"windows-1252",
|
||||
"windows-1253",
|
||||
"windows-1254",
|
||||
"windows-1255",
|
||||
"windows-1256",
|
||||
"windows-1257",
|
||||
"windows-1258",
|
||||
"x-mac-cyrillic",
|
||||
"gbk",
|
||||
"gb18030",
|
||||
"big5",
|
||||
"euc-jp",
|
||||
"iso-2022-jp",
|
||||
"shift_jis",
|
||||
"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 {
|
||||
const decoder = new TextDecoder(encoding);
|
||||
return decoder.decode(content);
|
||||
}
|
||||
|
||||
export function isEncodableResponse(url: string): boolean {
|
||||
const extensions = [".csv"];
|
||||
|
||||
if (typeof TextDecoder === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const extension of extensions) {
|
||||
if (url.endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function makeRawResource(
|
||||
res: Response,
|
||||
url: string
|
||||
): Promise<Resource> {
|
||||
const buffer = await res.arrayBuffer();
|
||||
return {
|
||||
items: [],
|
||||
numDirs: 0,
|
||||
numFiles: 0,
|
||||
sorting: {} as Sorting,
|
||||
index: 0,
|
||||
extension: getExtension(url),
|
||||
isDir: false,
|
||||
isSymlink: false,
|
||||
path: url,
|
||||
size: buffer.byteLength,
|
||||
modified: new Date().toISOString(),
|
||||
name: url.split("/").pop() || "",
|
||||
type: "text",
|
||||
mode: 0,
|
||||
url: `/files${url}`,
|
||||
rawContent: buffer,
|
||||
content: decode(buffer, "utf-8"),
|
||||
};
|
||||
}
|
||||
|
||||
function getExtension(url: string): string {
|
||||
const lastDotIndex = url.lastIndexOf(".");
|
||||
if (lastDotIndex === -1) {
|
||||
return "";
|
||||
}
|
||||
return url.substring(lastDotIndex);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
+161
-21
@@ -1,33 +1,164 @@
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { useUploadStore } from "@/stores/upload";
|
||||
import url from "@/utils/url";
|
||||
import { files as api } from "@/api";
|
||||
import { removePrefix } from "@/api/utils";
|
||||
|
||||
export function checkConflict(
|
||||
files: UploadList,
|
||||
dest: ResourceItem[]
|
||||
): boolean {
|
||||
if (typeof dest === "undefined" || dest === null) {
|
||||
dest = [];
|
||||
}
|
||||
interface UploadEntryWithChild extends UploadEntry {
|
||||
children?: UploadEntryWithChild[];
|
||||
originalIndex: number;
|
||||
}
|
||||
|
||||
const folder_upload = files[0].fullPath !== undefined;
|
||||
/**
|
||||
* Convert UploadList into a forest (array of root nodes).
|
||||
* This properly handles uploads with multiple top-level files/folders
|
||||
* instead of assuming a single root.
|
||||
* @param flatArray
|
||||
* @param basePath
|
||||
*/
|
||||
function flatToForest(
|
||||
flatArray: UploadList,
|
||||
basePath: string
|
||||
): UploadEntryWithChild[] {
|
||||
const nodeMap: Record<string, UploadEntryWithChild> = {};
|
||||
|
||||
const names = new Set<string>();
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
let name = file.name;
|
||||
// First pass: create all nodes
|
||||
flatArray.forEach((item, index) => {
|
||||
// File list is created from very different action and info available are not always the same.
|
||||
// By doing a drag and drop or upload a folder (both from the browser or from the OS) we have the fullPath property available
|
||||
// By uploading a single file using the file input, we only have the "name" property
|
||||
// By doing drag and drop from filebrowser to filebrowser, we have the "to" property available but not the fullPath
|
||||
const fullPathOrTo =
|
||||
item.fullPath || item.to?.replace(basePath, "") || item.name;
|
||||
nodeMap[fullPathOrTo!] = {
|
||||
fullPath: fullPathOrTo,
|
||||
isDir: item.isDir,
|
||||
name: item.name,
|
||||
size: item.size,
|
||||
originalIndex: index,
|
||||
...(item.isDir && { children: [] }),
|
||||
...(item.file && { file: item.file }),
|
||||
};
|
||||
});
|
||||
|
||||
if (folder_upload) {
|
||||
const dirs = file.fullPath?.split("/");
|
||||
if (dirs && dirs.length > 1) {
|
||||
name = dirs[0];
|
||||
const roots: UploadEntryWithChild[] = [];
|
||||
|
||||
// Second pass: build hierarchy
|
||||
flatArray.forEach((item) => {
|
||||
// see comment before to explanation
|
||||
const fullPathOrTo =
|
||||
item.fullPath || item.to?.replace(basePath, "") || item.name;
|
||||
|
||||
const node = nodeMap[fullPathOrTo!];
|
||||
const lastSlash = fullPathOrTo!.lastIndexOf("/");
|
||||
|
||||
if (lastSlash === -1) {
|
||||
roots.push(node);
|
||||
} else {
|
||||
const parentPath = fullPathOrTo!.substring(0, lastSlash);
|
||||
const parent = nodeMap[parentPath];
|
||||
if (parent?.children) {
|
||||
parent.children.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
names.add(name);
|
||||
return roots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return conflict files from
|
||||
* @param files - flat upload list to check
|
||||
* @param basePath - server destination path (e.g. "/files/uploads/")
|
||||
*/
|
||||
export async function checkConflict(
|
||||
files: UploadList,
|
||||
basePath: string
|
||||
): Promise<ConflictingResource[]> {
|
||||
console.log(
|
||||
"Starting conflict check, " + files.length + " possible conflict found."
|
||||
);
|
||||
|
||||
const forest = flatToForest(files, basePath);
|
||||
if (forest.length === 0) return [];
|
||||
|
||||
// Single API call: fetch the entire server tree under basePath.
|
||||
let serverEntries: RecursiveEntry[] = [];
|
||||
try {
|
||||
serverEntries = await api.fetchAll(basePath);
|
||||
} catch {
|
||||
console.error(
|
||||
`Failed to fetch recursive server listing for ${basePath}. ` +
|
||||
`Assuming directory doesn't exist and skipping conflict check.`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return dest.some((d) => names.has(d.name));
|
||||
// Build a lookup map keyed by the normalised server path for O(1) access.
|
||||
// The server returns paths relative to the user's scope (e.g. "/uploads/foo.txt").
|
||||
// We strip the basePath prefix so the key matches the upload entry's fullPath.
|
||||
const normBase = removePrefix(basePath).replace(/\/+$/, "");
|
||||
const serverMap = new Map<string, RecursiveEntry>();
|
||||
for (const entry of serverEntries) {
|
||||
// entry.path is absolute from server root, e.g. "/uploads/sub/file.txt"
|
||||
// We need the relative part after normBase, e.g. "sub/file.txt"
|
||||
let rel = entry.path;
|
||||
if (rel.startsWith(normBase)) {
|
||||
rel = rel.slice(normBase.length);
|
||||
}
|
||||
// Strip leading slash so it matches fullPath format ("sub/file.txt")
|
||||
rel = rel.replace(/^\/+/, "");
|
||||
serverMap.set(rel, entry);
|
||||
}
|
||||
|
||||
const conflicts: ConflictingResource[] = [];
|
||||
|
||||
/**
|
||||
* Walk the upload tree and compare each file node against the
|
||||
* pre-fetched server map. Directories only need to be recursed
|
||||
* when they appear in the map (otherwise no child can conflict).
|
||||
*/
|
||||
function recursiveCheckConflict(nodes: UploadEntryWithChild[]): void {
|
||||
for (const node of nodes) {
|
||||
if (node.isDir && node.children) {
|
||||
// Only recurse if this directory exists on the server
|
||||
const dirKey = node.fullPath!.replace(/^\/+/, "");
|
||||
if (serverMap.has(dirKey)) {
|
||||
recursiveCheckConflict(node.children);
|
||||
}
|
||||
} else {
|
||||
// File – check for a conflict against the server map
|
||||
const fileKey = node.fullPath!.replace(/^\/+/, "");
|
||||
const serverEntry = serverMap.get(fileKey);
|
||||
|
||||
if (serverEntry) {
|
||||
conflicts.push({
|
||||
index: node.originalIndex,
|
||||
name: serverEntry.path,
|
||||
origin: {
|
||||
lastModified: node.file?.lastModified,
|
||||
size: node.size,
|
||||
},
|
||||
dest: {
|
||||
lastModified: serverEntry.modified,
|
||||
size: serverEntry.size,
|
||||
},
|
||||
checked: ["origin"],
|
||||
isSmallerOnServer: node.size > serverEntry.size,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk all root nodes synchronously against the pre-fetched data
|
||||
recursiveCheckConflict(forest);
|
||||
|
||||
console.log(conflicts.length + " conflicts found.");
|
||||
|
||||
conflicts.sort((a, b) => a.index - b.index);
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
export function scanFiles(dt: DataTransfer): Promise<UploadList | FileList> {
|
||||
@@ -97,11 +228,14 @@ export function scanFiles(dt: DataTransfer): Promise<UploadList | FileList> {
|
||||
reader.readEntries((entries) => {
|
||||
reading--;
|
||||
if (entries.length > 0) {
|
||||
const dirWithSlash = directory.endsWith("/")
|
||||
? directory
|
||||
: `${directory}/`;
|
||||
for (const entry of entries) {
|
||||
readEntry(entry, `${directory}/`);
|
||||
readEntry(entry, dirWithSlash);
|
||||
}
|
||||
|
||||
readReaderContent(reader, `${directory}/`);
|
||||
readReaderContent(reader, dirWithSlash);
|
||||
}
|
||||
|
||||
if (reading === 0) {
|
||||
@@ -146,6 +280,12 @@ export function handleFiles(
|
||||
|
||||
const type = file.isDir ? "dir" : detectType((file.file as File).type);
|
||||
|
||||
uploadStore.upload(path, file.name, file.file ?? null, overwrite, type);
|
||||
uploadStore.upload(
|
||||
path,
|
||||
file.name,
|
||||
file.file ?? null,
|
||||
file.overwrite || overwrite,
|
||||
type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { getEditorTheme } from "@/utils/theme";
|
||||
import { marked } from "marked";
|
||||
import markedKatex from "marked-katex-extension";
|
||||
import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
|
||||
@@ -118,6 +119,11 @@ const previewContent = ref("");
|
||||
const isMarkdownFile =
|
||||
fileStore.req?.name.endsWith(".md") ||
|
||||
fileStore.req?.name.endsWith(".markdown");
|
||||
const katexOptions = {
|
||||
output: "mathml" as const,
|
||||
throwOnError: false,
|
||||
};
|
||||
marked.use(markedKatex(katexOptions));
|
||||
|
||||
const isSelectionEmpty = ref(true);
|
||||
|
||||
@@ -171,25 +177,19 @@ onMounted(() => {
|
||||
`https://cdn.jsdelivr.net/npm/ace-builds@${ace_version}/src-min-noconflict/`
|
||||
);
|
||||
|
||||
editor.value = ace.edit("editor", {
|
||||
value: fileContent,
|
||||
showPrintMargin: false,
|
||||
readOnly: fileStore.req?.type === "textImmutable",
|
||||
theme: getEditorTheme(authStore.user?.aceEditorTheme ?? ""),
|
||||
mode: modelist.getModeForPath(fileStore.req!.name).mode,
|
||||
wrap: true,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
});
|
||||
|
||||
editor.value.setFontSize(fontSize.value);
|
||||
editor.value.focus();
|
||||
|
||||
editor.value.getSelection().on("changeSelection", () => {
|
||||
isSelectionEmpty.value =
|
||||
editor.value == null || editor.value.getSelectedText().length == 0;
|
||||
});
|
||||
if (!layoutStore.loading) {
|
||||
initEditor(fileContent);
|
||||
} else {
|
||||
const unwatch = watchEffect(() => {
|
||||
// Initialize editor when layout is loaded
|
||||
if (!layoutStore.loading) {
|
||||
setTimeout(() => {
|
||||
initEditor(fileContent);
|
||||
unwatch();
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -218,6 +218,28 @@ onBeforeRouteUpdate((to, from, next) => {
|
||||
});
|
||||
});
|
||||
|
||||
const initEditor = (fileContent: string) => {
|
||||
editor.value = ace.edit("editor", {
|
||||
value: fileContent,
|
||||
showPrintMargin: false,
|
||||
readOnly: fileStore.req?.type === "textImmutable",
|
||||
theme: getEditorTheme(authStore.user?.aceEditorTheme ?? ""),
|
||||
mode: modelist.getModeForPath(fileStore.req!.name).mode,
|
||||
wrap: true,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
});
|
||||
|
||||
editor.value.setFontSize(fontSize.value);
|
||||
editor.value.focus();
|
||||
|
||||
const selection = editor.value?.getSelection();
|
||||
selection.on("changeSelection", function () {
|
||||
isSelectionEmpty.value = selection.isEmpty();
|
||||
});
|
||||
};
|
||||
|
||||
const keyEvent = (event: KeyboardEvent) => {
|
||||
if (event.code === "Escape") {
|
||||
close();
|
||||
@@ -279,6 +301,7 @@ const close = () => {
|
||||
prompt: "discardEditorChanges",
|
||||
confirm: (event: Event) => {
|
||||
event.preventDefault();
|
||||
editor.value?.session.getUndoManager().reset();
|
||||
finishClose();
|
||||
},
|
||||
saveAction: async () => {
|
||||
|
||||
@@ -480,7 +480,10 @@ const headerButtons = computed(() => {
|
||||
shell: authStore.user?.perm.execute && enableExec,
|
||||
delete: fileStore.selectedCount > 0 && authStore.user?.perm.delete,
|
||||
rename: fileStore.selectedCount === 1 && authStore.user?.perm.rename,
|
||||
share: fileStore.selectedCount === 1 && authStore.user?.perm.share,
|
||||
share:
|
||||
fileStore.selectedCount === 1 &&
|
||||
authStore.user?.perm.share &&
|
||||
authStore.user?.perm.download,
|
||||
move: fileStore.selectedCount > 0 && authStore.user?.perm.rename,
|
||||
copy: fileStore.selectedCount > 0 && authStore.user?.perm.create,
|
||||
};
|
||||
@@ -628,6 +631,8 @@ const copyCut = (event: Event | KeyboardEvent): void => {
|
||||
items.push({
|
||||
from: fileStore.req.items[i].url,
|
||||
name: fileStore.req.items[i].name,
|
||||
size: fileStore.req.items[i].size,
|
||||
modified: fileStore.req.items[i].modified,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -642,7 +647,7 @@ const copyCut = (event: Event | KeyboardEvent): void => {
|
||||
});
|
||||
};
|
||||
|
||||
const paste = (event: Event) => {
|
||||
const paste = async (event: Event) => {
|
||||
if ((event.target as HTMLElement).tagName?.toLowerCase() === "input") return;
|
||||
|
||||
// TODO router location should it be
|
||||
@@ -651,7 +656,15 @@ const paste = (event: Event) => {
|
||||
for (const item of clipboardStore.items) {
|
||||
const from = item.from.endsWith("/") ? item.from.slice(0, -1) : item.from;
|
||||
const to = route.path + encodeURIComponent(item.name);
|
||||
items.push({ from, to, name: item.name });
|
||||
items.push({
|
||||
from,
|
||||
to,
|
||||
name: item.name,
|
||||
size: item.size,
|
||||
modified: item.modified,
|
||||
overwrite: false,
|
||||
rename: clipboardStore.path == route.path,
|
||||
});
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
@@ -660,7 +673,7 @@ const paste = (event: Event) => {
|
||||
|
||||
const preselect = removePrefix(route.path) + items[0].name;
|
||||
|
||||
let action = (overwrite: boolean, rename: boolean) => {
|
||||
let action = (overwrite?: boolean, rename?: boolean) => {
|
||||
api
|
||||
.copy(items, overwrite, rename)
|
||||
.then(() => {
|
||||
@@ -683,34 +696,38 @@ const paste = (event: Event) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (clipboardStore.path == route.path) {
|
||||
action(false, true);
|
||||
const path = route.path.endsWith("/") ? route.path : route.path + "/";
|
||||
const conflict = await upload.checkConflict(items, path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const conflict = upload.checkConflict(items, fileStore.req!.items);
|
||||
|
||||
let overwrite = false;
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
if (conflict.length > 0) {
|
||||
layoutStore.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event: Event, option: string) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
},
|
||||
confirm: (event: Event, result: Array<ConflictingResource>) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
action(overwrite, rename);
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
items[item.index].rename = true;
|
||||
} else if (item.checked.length == 1 && item.checked[0] == "origin") {
|
||||
items[item.index].overwrite = true;
|
||||
} else {
|
||||
items.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (items.length > 0) {
|
||||
action();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
action(overwrite, rename);
|
||||
action(false, false);
|
||||
};
|
||||
|
||||
const columnsResize = () => {
|
||||
@@ -783,7 +800,6 @@ const drop = async (event: DragEvent) => {
|
||||
}
|
||||
|
||||
const files: UploadList = (await upload.scanFiles(dt)) as UploadList;
|
||||
let items = fileStore.req.items;
|
||||
let path = route.path.endsWith("/") ? route.path : route.path + "/";
|
||||
|
||||
if (
|
||||
@@ -796,30 +812,41 @@ const drop = async (event: DragEvent) => {
|
||||
path = el.__vue__.url;
|
||||
|
||||
try {
|
||||
items = (await api.fetch(path)).items;
|
||||
(await api.fetch(path)).items;
|
||||
} catch (error: any) {
|
||||
$showError(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const conflict = upload.checkConflict(files, items);
|
||||
const conflict = await upload.checkConflict(files, path);
|
||||
|
||||
const preselect = removePrefix(path) + (files[0].fullPath || files[0].name);
|
||||
|
||||
if (conflict) {
|
||||
if (conflict.length > 0) {
|
||||
layoutStore.showHover({
|
||||
prompt: "replace",
|
||||
action: (event: Event) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
upload.handleFiles(files, path, false);
|
||||
fileStore.preselect = preselect;
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
isUploadAction: true,
|
||||
},
|
||||
confirm: (event: Event) => {
|
||||
confirm: (event: Event, result: Array<ConflictingResource>) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
upload.handleFiles(files, path, true);
|
||||
fileStore.preselect = preselect;
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
continue;
|
||||
} else if (item.checked.length == 1 && item.checked[0] == "origin") {
|
||||
files[item.index].overwrite = true;
|
||||
} else {
|
||||
files.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (files.length > 0) {
|
||||
upload.handleFiles(files, path, true);
|
||||
fileStore.preselect = preselect;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -830,7 +857,7 @@ const drop = async (event: DragEvent) => {
|
||||
fileStore.preselect = preselect;
|
||||
};
|
||||
|
||||
const uploadInput = (event: Event) => {
|
||||
const uploadInput = async (event: Event) => {
|
||||
const files = (event.currentTarget as HTMLInputElement)?.files;
|
||||
if (files === null) return;
|
||||
|
||||
@@ -850,20 +877,31 @@ const uploadInput = (event: Event) => {
|
||||
}
|
||||
|
||||
const path = route.path.endsWith("/") ? route.path : route.path + "/";
|
||||
const conflict = upload.checkConflict(uploadFiles, fileStore.req!.items);
|
||||
const conflict = await upload.checkConflict(uploadFiles, path);
|
||||
|
||||
if (conflict) {
|
||||
if (conflict.length > 0) {
|
||||
layoutStore.showHover({
|
||||
prompt: "replace",
|
||||
action: (event: Event) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
upload.handleFiles(uploadFiles, path, false);
|
||||
prompt: "resolve-conflict",
|
||||
props: {
|
||||
conflict: conflict,
|
||||
isUploadAction: true,
|
||||
},
|
||||
confirm: (event: Event) => {
|
||||
confirm: (event: Event, result: Array<ConflictingResource>) => {
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
upload.handleFiles(uploadFiles, path, true);
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const item = result[i];
|
||||
if (item.checked.length == 2) {
|
||||
continue;
|
||||
} else if (item.checked.length == 1 && item.checked[0] == "origin") {
|
||||
uploadFiles[item.index].overwrite = true;
|
||||
} else {
|
||||
uploadFiles.splice(item.index, 1);
|
||||
}
|
||||
}
|
||||
if (uploadFiles.length > 0) {
|
||||
upload.handleFiles(uploadFiles, path, true);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,16 @@
|
||||
:label="$t('buttons.download')"
|
||||
@action="download"
|
||||
/>
|
||||
<action
|
||||
:disabled="layoutStore.loading"
|
||||
v-if="
|
||||
['image', 'audio', 'video'].includes(fileStore.req?.type || '') &&
|
||||
authStore.user?.perm.download
|
||||
"
|
||||
icon="open_in_new"
|
||||
:label="t('buttons.openDirect')"
|
||||
@action="openDirect"
|
||||
/>
|
||||
<action
|
||||
:disabled="layoutStore.loading"
|
||||
icon="info"
|
||||
@@ -74,7 +84,6 @@
|
||||
}"
|
||||
:epubOptions="{
|
||||
allowPopups: true,
|
||||
allowScriptedContent: true,
|
||||
}"
|
||||
@update:location="locationChange"
|
||||
/>
|
||||
@@ -253,7 +262,7 @@ const hoverNav = ref<boolean>(false);
|
||||
const autoPlay = ref<boolean>(false);
|
||||
const previousRaw = ref<string>("");
|
||||
const nextRaw = ref<string>("");
|
||||
const csvContent = ref<string>("");
|
||||
const csvContent = ref<ArrayBuffer | string>("");
|
||||
const csvError = ref<string>("");
|
||||
|
||||
const player = ref<HTMLVideoElement | HTMLAudioElement | null>(null);
|
||||
@@ -277,6 +286,10 @@ const downloadUrl = computed(() =>
|
||||
fileStore.req ? api.getDownloadURL(fileStore.req, false) : ""
|
||||
);
|
||||
|
||||
const directUrl = computed(() =>
|
||||
fileStore.req ? api.getDownloadURL(fileStore.req, true) : ""
|
||||
);
|
||||
|
||||
const previewUrl = computed(() => {
|
||||
if (!fileStore.req) {
|
||||
return "";
|
||||
@@ -297,7 +310,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);
|
||||
|
||||
@@ -366,11 +383,19 @@ const key = (event: KeyboardEvent) => {
|
||||
if (layoutStore.currentPrompt !== null) {
|
||||
return;
|
||||
}
|
||||
if (event.which === 13 || event.which === 39) {
|
||||
// When previewing a video, let arrow keys fall through to video.js for
|
||||
// seeking instead of switching to the prev/next file. Enter still advances.
|
||||
const isVideo = fileStore.req?.type === "video";
|
||||
if (event.which === 13) {
|
||||
// enter
|
||||
if (hasNext.value) next();
|
||||
} else if (event.which === 39) {
|
||||
// right arrow
|
||||
if (isVideo) return;
|
||||
if (hasNext.value) next();
|
||||
} else if (event.which === 37) {
|
||||
// left arrow
|
||||
if (isVideo) return;
|
||||
if (hasPrevious.value) prev();
|
||||
} else if (event.which === 27) {
|
||||
// esc
|
||||
@@ -393,7 +418,11 @@ const updatePreview = async () => {
|
||||
if (fileStore.req.size > CSV_MAX_SIZE) {
|
||||
csvError.value = t("files.csvTooLarge");
|
||||
} else {
|
||||
csvContent.value = fileStore.req.content ?? "";
|
||||
if (fileStore.req.rawContent != null) {
|
||||
csvContent.value = fileStore.req.rawContent;
|
||||
} else {
|
||||
csvContent.value = fileStore.req.content ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,6 +495,7 @@ const close = () => {
|
||||
};
|
||||
|
||||
const download = () => window.open(downloadUrl.value);
|
||||
const openDirect = () => window.open(directUrl.value);
|
||||
|
||||
const editAsText = () => {
|
||||
router.push({ path: route.path, query: { edit: "true" } });
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<a
|
||||
class="link"
|
||||
target="_blank"
|
||||
href="https://filebrowser.org/configuration.html#custom-branding"
|
||||
href="https://filebrowser.org/customization.html#custom-branding"
|
||||
>{{ t("settings.documentation") }}</a
|
||||
>
|
||||
</i18n-t>
|
||||
@@ -209,7 +209,7 @@
|
||||
<a
|
||||
class="link"
|
||||
target="_blank"
|
||||
href="https://filebrowser.org/configuration.html#command-runner"
|
||||
href="https://filebrowser.org/command-execution.html#hook-runner"
|
||||
>{{ t("settings.documentation") }}</a
|
||||
>
|
||||
</i18n-t>
|
||||
|
||||
@@ -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">
|
||||
@@ -72,12 +59,12 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { StatusError } from "@/api/utils";
|
||||
import { authMethod } from "@/utils/constants";
|
||||
import { logout } from "@/utils/auth";
|
||||
|
||||
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,17 +121,35 @@ 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);
|
||||
router.push({ path: "/settings/users" });
|
||||
await api.remove(user.value.id, currentPassword);
|
||||
if (user.value.id == authStore.user?.id) {
|
||||
logout();
|
||||
} else {
|
||||
router.push({ path: "/settings/users" });
|
||||
}
|
||||
$showSuccess(t("settings.userDeleted"));
|
||||
} catch (err) {
|
||||
if (err instanceof StatusError) {
|
||||
@@ -157,8 +162,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 +192,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 +207,5 @@ const save = async (event: Event) => {
|
||||
} catch (e: any) {
|
||||
$showError(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module github.com/filebrowser/filebrowser/v2
|
||||
|
||||
go 1.25
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/asdine/storm/v3 v3.2.1
|
||||
github.com/asticode/go-astisub v0.38.0
|
||||
github.com/asticode/go-astisub v0.40.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dsoprea/go-exif/v3 v3.0.1
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
||||
@@ -16,18 +16,19 @@ require (
|
||||
github.com/marusama/semaphore/v2 v2.5.0
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/redis/go-redis/v9 v9.17.3
|
||||
github.com/samber/lo v1.52.0
|
||||
github.com/shirou/gopsutil/v4 v4.26.1
|
||||
github.com/redis/go-redis/v9 v9.19.0
|
||||
github.com/samber/lo v1.53.0
|
||||
github.com/shirou/gopsutil/v4 v4.26.4
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/image v0.35.0
|
||||
golang.org/x/text v0.33.0
|
||||
go.etcd.io/bbolt v1.4.3
|
||||
golang.org/x/crypto v0.50.0
|
||||
golang.org/x/image v0.39.0
|
||||
golang.org/x/text v0.36.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -43,11 +44,10 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
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
|
||||
@@ -73,12 +73,12 @@ require (
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -31,8 +31,8 @@ github.com/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xbl
|
||||
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
|
||||
github.com/asticode/go-astikit v0.56.0 h1:DmD2p7YnvxiPdF0h+dRmos3bsejNEXbycENsY5JfBqw=
|
||||
github.com/asticode/go-astikit v0.56.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE=
|
||||
github.com/asticode/go-astisub v0.38.0 h1:Qh3IO8Cotn0wwok5maid7xqsIJTwn2DtABT1UajKJaI=
|
||||
github.com/asticode/go-astisub v0.38.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
|
||||
github.com/asticode/go-astisub v0.40.0 h1:Z8tAXHngjkh/4L9zqt49ru9UdpDTqBIe+80xexKM3/I=
|
||||
github.com/asticode/go-astisub v0.40.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
|
||||
github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
|
||||
github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c=
|
||||
github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
|
||||
@@ -59,8 +59,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
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.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
@@ -83,8 +81,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=
|
||||
@@ -166,7 +164,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
@@ -202,8 +203,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/redis/go-redis/v9 v9.17.3 h1:fN29NdNrE17KttK5Ndf20buqfDZwGNgoUr9qjl1DQx4=
|
||||
github.com/redis/go-redis/v9 v9.17.3/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k=
|
||||
github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
@@ -212,10 +213,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.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY=
|
||||
github.com/shirou/gopsutil/v4 v4.26.4/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=
|
||||
@@ -257,6 +258,8 @@ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3i
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
@@ -264,6 +267,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
@@ -276,8 +281,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -289,8 +294,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
|
||||
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
|
||||
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
|
||||
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -329,8 +334,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -343,8 +348,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -370,8 +375,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -382,8 +387,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
+19
-1
@@ -20,6 +20,8 @@ import (
|
||||
|
||||
const (
|
||||
DefaultTokenExpirationTime = time.Hour * 2
|
||||
|
||||
maxAuthBodySize = 1 << 20 // 1 MiB
|
||||
)
|
||||
|
||||
type userInfo struct {
|
||||
@@ -120,6 +122,10 @@ func withAdmin(fn handleFunc) handleFunc {
|
||||
|
||||
func loginHandler(tokenExpireTime time.Duration) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if r.Body != nil {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxAuthBodySize)
|
||||
}
|
||||
|
||||
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
@@ -142,7 +148,7 @@ type signupBody struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.settings.Signup {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
@@ -151,6 +157,8 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int,
|
||||
return http.StatusBadRequest, nil
|
||||
}
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxAuthBodySize)
|
||||
|
||||
info := &signupBody{}
|
||||
err := json.NewDecoder(r.Body).Decode(info)
|
||||
if err != nil {
|
||||
@@ -167,6 +175,16 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int,
|
||||
|
||||
d.settings.Defaults.Apply(user)
|
||||
|
||||
// Users signed up via the signup handler should never become admins, even
|
||||
// if that is the default permission.
|
||||
user.Perm.Admin = false
|
||||
|
||||
// Self-registered users should not inherit execution capabilities from
|
||||
// default settings, regardless of what the administrator has configured
|
||||
// as the default. Execution rights must be explicitly granted by an admin.
|
||||
user.Perm.Execute = false
|
||||
user.Commands = []string{}
|
||||
|
||||
pwd, err := users.ValidateAndHashPwd(info.Password, d.settings.MinimumPasswordLength)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
|
||||
@@ -3,6 +3,7 @@ package fbhttp
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
gopath "path"
|
||||
"strconv"
|
||||
|
||||
"github.com/tomasen/realip"
|
||||
@@ -23,10 +24,25 @@ type data struct {
|
||||
store *storage.Storage
|
||||
user *users.User
|
||||
raw interface{}
|
||||
|
||||
// checkerPrefix is prepended to every path before evaluating rules. It is
|
||||
// set when the user's filesystem has been rebased onto a subdirectory (as
|
||||
// done for public shares), so that rules — which are relative to the user's
|
||||
// original scope — are still matched against the real path instead of the
|
||||
// rebased one. Empty for regular requests.
|
||||
checkerPrefix string
|
||||
}
|
||||
|
||||
// Check implements rules.Checker.
|
||||
func (d *data) Check(path string) bool {
|
||||
// When the filesystem has been rebased (e.g. a public share rooted at a
|
||||
// subdirectory), the incoming path is relative to that root. Resolve it
|
||||
// back to the user's original scope before matching rules, otherwise rules
|
||||
// targeting paths below the share root would be silently bypassed.
|
||||
if d.checkerPrefix != "" {
|
||||
path = gopath.Join(d.checkerPrefix, path)
|
||||
}
|
||||
|
||||
if d.user.HideDotfiles && rules.MatchHidden(path) {
|
||||
return false
|
||||
}
|
||||
|
||||
+2
-1
@@ -57,6 +57,7 @@ func NewHandler(
|
||||
users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET")
|
||||
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
|
||||
|
||||
api.PathPrefix("/resources/recursive").Handler(monkey(resourceGetRecursiveHandler, "/api/resources/recursive")).Methods("GET")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST")
|
||||
@@ -70,7 +71,7 @@ func NewHandler(
|
||||
|
||||
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
|
||||
|
||||
api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET")
|
||||
api.Handle("/shares", monkey(shareListHandler, "")).Methods("GET")
|
||||
api.PathPrefix("/share").Handler(monkey(shareGetsHandler, "/api/share")).Methods("GET")
|
||||
api.PathPrefix("/share").Handler(monkey(sharePostHandler, "/api/share")).Methods("POST")
|
||||
api.PathPrefix("/share").Handler(monkey(shareDeleteHandler, "/api/share")).Methods("DELETE")
|
||||
|
||||
+10
-1
@@ -33,6 +33,10 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if !user.Perm.Share || !user.Perm.Download {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
d.user = user
|
||||
|
||||
file, err := files.NewFileInfo(&files.FileOptions{
|
||||
@@ -56,13 +60,18 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
||||
filePath := ""
|
||||
|
||||
if file.IsDir {
|
||||
basePath = filepath.Dir(basePath)
|
||||
basePath = filepath.Clean(link.Path)
|
||||
filePath = ifPath
|
||||
}
|
||||
|
||||
// set fs root to the shared file/folder
|
||||
d.user.Fs = afero.NewBasePathFs(d.user.Fs, basePath)
|
||||
|
||||
// the filesystem is now rebased onto basePath, so paths handed to the
|
||||
// rule checker are relative to it. Resolve them back to the user's
|
||||
// original scope so deny rules below the share root keep applying.
|
||||
d.checkerPrefix = basePath
|
||||
|
||||
file, err = files.NewFileInfo(&files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: filePath,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user