Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d54046140 | ||
|
|
dec3d629d4 | ||
|
|
8118afd0ac | ||
|
|
577c0efa9c | ||
|
|
dcf0bc65bf | ||
|
|
c211b96719 | ||
|
|
1e7d3b25c2 | ||
|
|
b16982df0f | ||
|
|
540ddf47a7 | ||
|
|
02730bb9bf | ||
|
|
d1d8e3e340 | ||
|
|
42a39b3f1d | ||
|
|
dd503695a1 | ||
|
|
1d66bbe40a | ||
|
|
5da9d74da6 | ||
|
|
b14b9114f8 | ||
|
|
8a43413f88 | ||
|
|
c3bd1188aa | ||
|
|
fc209f64de | ||
|
|
96afaca0ad | ||
|
|
f663237a16 | ||
|
|
ac3ead8dce | ||
|
|
7c9a75e725 | ||
|
|
596c73288f | ||
|
|
d1d7b23da6 | ||
|
|
e677c78471 | ||
|
|
9734f707f0 | ||
|
|
e5fa96b666 | ||
|
|
bcef7d3f73 | ||
|
|
aed3af5838 | ||
|
|
6bd34c7632 | ||
|
|
040584c865 | ||
|
|
ecb2d1d81b | ||
|
|
a74c72db45 | ||
|
|
f5b1e10618 | ||
|
|
e7fed5a45b | ||
|
|
f8dfbf7eee | ||
|
|
fca5fc5b87 | ||
|
|
4ee19be63d | ||
|
|
b2ad3f7368 | ||
|
|
b73d278ded | ||
|
|
6366cf0b18 | ||
|
|
f73518029c | ||
|
|
c782f21b0f | ||
|
|
0942fc7042 | ||
|
|
c1987237d0 | ||
|
|
cf85404dd2 | ||
|
|
6f226fa549 | ||
|
|
228ebea66c | ||
|
|
bb19834042 | ||
|
|
7870e89bc0 | ||
|
|
8888b9f446 | ||
|
|
f6e5c6f0de | ||
|
|
e7659ea36b | ||
|
|
7730ccd611 | ||
|
|
80890075e8 | ||
|
|
9b04004120 | ||
|
|
a73d7f14b7 | ||
|
|
ffe960a8c2 | ||
|
|
73c80732d9 | ||
|
|
8e2663bf7b | ||
|
|
e697e58164 | ||
|
|
c01496624a | ||
|
|
8906408a8f | ||
|
|
3ec7951380 | ||
|
|
b30aefa522 | ||
|
|
bc8a750dfe | ||
|
|
f1f7f17ade | ||
|
|
9182d33e1c | ||
|
|
7d836a3728 | ||
|
|
010d16fc1d | ||
|
|
fa89ba4665 | ||
|
|
a0752904c1 | ||
|
|
371718634b | ||
|
|
0f4f8751f2 | ||
|
|
ec45ee471f | ||
|
|
6fffcbac4e | ||
|
|
2948589fcd | ||
|
|
ecd0b2ee0d | ||
|
|
205f11d677 | ||
|
|
949f0f277f | ||
|
|
665e45889c | ||
|
|
8d87e0d5f9 | ||
|
|
46d80464d2 | ||
|
|
829ed9fb6d | ||
|
|
988d3e5bdd | ||
|
|
6eb3ab0635 | ||
|
|
c2e03bbfab | ||
|
|
608a0015ee | ||
|
|
f81857acce | ||
|
|
b1e0d5b39f | ||
|
|
68cf7a2173 | ||
|
|
89d1c06441 | ||
|
|
2bebb5f0f8 | ||
|
|
683b11d265 | ||
|
|
4d1b9dd211 | ||
|
|
b8f35ce932 | ||
|
|
a078f0b787 | ||
|
|
7401d16e45 | ||
|
|
958a44f95e | ||
|
|
e08239781f | ||
|
|
c29698dffa | ||
|
|
81de95632a | ||
|
|
7f2d221083 | ||
|
|
74b7cd8e81 | ||
|
|
6cb51b4eb4 | ||
|
|
f09bf3e1d0 | ||
|
|
6f345be3e4 | ||
|
|
ddd4ffa4ca | ||
|
|
deabc80fd7 | ||
|
|
b6a51bed51 | ||
|
|
0426629a59 | ||
|
|
0358e42d2c | ||
|
|
3768e3345f | ||
|
|
16e434be66 | ||
|
|
bf303c536a | ||
|
|
43a460993c | ||
|
|
7f0673ee70 | ||
|
|
4c3099a086 | ||
|
|
f0bc9167b1 | ||
|
|
23d646c456 | ||
|
|
76add9e527 | ||
|
|
c63cc5a2d2 | ||
|
|
25c8788390 | ||
|
|
aa52b07bb1 | ||
|
|
76b466f649 | ||
|
|
8ecc2da947 | ||
|
|
8650d2ffe7 | ||
|
|
34d7d2c8c4 | ||
|
|
201329abce | ||
|
|
f2b5dd3787 | ||
|
|
5072bbb2cb | ||
|
|
6b19ab6613 | ||
|
|
730be5ef6b | ||
|
|
46ee595389 | ||
|
|
dee465ab86 | ||
|
|
209f9fa77f | ||
|
|
ba8c09f454 | ||
|
|
16a34defc0 | ||
|
|
7d1e03075d | ||
|
|
1c25f6ee69 | ||
|
|
aa172b8bb5 | ||
|
|
4711e7bcd5 | ||
|
|
8a47aee137 | ||
|
|
190cb99a79 | ||
|
|
603203848a | ||
|
|
5e6f14b5dc | ||
|
|
976eb5583d | ||
|
|
b92152693f | ||
|
|
7ec24d9d77 | ||
|
|
20ebbf6611 | ||
|
|
ba7e71a7c3 | ||
|
|
8973c4598f | ||
|
|
18889ad725 | ||
|
|
73ccbe912f | ||
|
|
84e3a98303 | ||
|
|
7dd5b34d42 | ||
|
|
4470d0a704 | ||
|
|
a76e01d2b7 | ||
|
|
2697093ac1 | ||
|
|
59f9964e80 | ||
|
|
1516d9932b | ||
|
|
fcb115f42d | ||
|
|
e410272e6b | ||
|
|
87f1881b42 | ||
|
|
c0d85f3d85 | ||
|
|
98d79b8ed9 | ||
|
|
fe80730bb1 | ||
|
|
6c8ee96e6a | ||
|
|
b521dec8f9 | ||
|
|
e9baf0c4b6 | ||
|
|
e1a6f593e1 | ||
|
|
4b068b3058 | ||
|
|
da54bd6c21 | ||
|
|
0d179eca4d | ||
|
|
dacd511d24 | ||
|
|
c44b37c50c | ||
|
|
a721dc1f31 | ||
|
|
d2e6d23741 | ||
|
|
5f4a0317ab | ||
|
|
22f4be8f54 | ||
|
|
eeadc532fe | ||
|
|
93a35ad251 | ||
|
|
99787287bb | ||
|
|
bdd523190e | ||
|
|
4c1dd5c097 |
@@ -1,2 +1,3 @@
|
|||||||
*
|
*
|
||||||
|
!docker/*
|
||||||
!filebrowser
|
!filebrowser
|
||||||
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -4,19 +4,19 @@ about: Create a report to help us improve
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Description**
|
**Description**
|
||||||
A clear and concise description of what the issue is about. What are you trying to do?
|
<!-- A clear and concise description of what the issue is about. What are you trying to do? -->
|
||||||
|
|
||||||
**Expected behaviour**
|
**Expected behaviour**
|
||||||
What did you expect to happen?
|
<!-- What did you expect to happen? -->
|
||||||
|
|
||||||
**What is happening instead?**
|
**What is happening instead?**
|
||||||
Please, give full error messages and/or log.
|
<!-- Please, give full error messages and/or log. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here. If applicable, add screenshots to help explain your problem.
|
<!-- Add any other context about the problem here. If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
**How to reproduce?**
|
**How to reproduce?**
|
||||||
Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?
|
<!-- Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behaviour as minimally as possible? -->
|
||||||
|
|
||||||
**Files**
|
**Files**
|
||||||
A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile.
|
<!-- A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile. -->
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/feature_request.md
vendored
8
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -4,13 +4,13 @@ about: Suggest an idea for this project
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
Add a clear and concise description of what the problem is. E.g. *I'm always frustrated when [...]*
|
<!-- Add a clear and concise description of what the problem is. E.g. *I'm always frustrated when [...]* -->
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
Add a clear and concise description of what you want to happen.
|
<!-- Add a clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
Add a clear and concise description of any alternative solutions or features you've considered.
|
<!-- Add a clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|||||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,6 +1,8 @@
|
|||||||
**Description**
|
**Description**
|
||||||
|
<!--
|
||||||
Please explain the changes you made here.
|
Please explain the changes you made here.
|
||||||
If the feature changes current behaviour, explain why your solution is better.
|
If the feature changes current behaviour, explain why your solution is better.
|
||||||
|
-->
|
||||||
|
|
||||||
:rotating_light: Before submitting your PR, please read [community](https://github.com/filebrowser/community), and indicate which issues (in any of the repos) 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/).
|
:rotating_light: Before submitting your PR, please read [community](https://github.com/filebrowser/community), and indicate which issues (in any of the repos) 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/).
|
||||||
|
|
||||||
@@ -11,6 +13,8 @@ If the feature changes current behaviour, explain why your solution is better.
|
|||||||
- [ ] AVOID breaking the continuous integration build.
|
- [ ] AVOID breaking the continuous integration build.
|
||||||
|
|
||||||
**Further comments**
|
**Further comments**
|
||||||
|
<!--
|
||||||
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did, what alternatives you considered, etc.
|
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did, what alternatives you considered, etc.
|
||||||
|
|
||||||
:heart: Thank you!
|
:heart: Thank you!
|
||||||
|
-->
|
||||||
|
|||||||
66
.github/workflows/main.yaml
vendored
66
.github/workflows/main.yaml
vendored
@@ -9,29 +9,63 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# linters
|
||||||
|
lint-frontend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: make lint-frontend
|
||||||
|
lint-backend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.18.3
|
||||||
|
- run: make lint-backend
|
||||||
|
lint-commits:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: make lint-commits
|
||||||
lint:
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [lint-frontend, lint-backend, lint-commits]
|
||||||
|
steps:
|
||||||
|
- run: echo "done"
|
||||||
|
|
||||||
|
# tests
|
||||||
|
test-frontend:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: 1.16
|
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '16'
|
||||||
- run: npm i -g commitlint
|
- run: make test-frontend
|
||||||
- run: make lint
|
test-backend:
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.18.3
|
||||||
- uses: actions/setup-node@v2
|
- run: make test-backend
|
||||||
with:
|
test:
|
||||||
node-version: '14'
|
runs-on: ubuntu-latest
|
||||||
- run: make test
|
needs: [test-frontend, test-backend]
|
||||||
|
steps:
|
||||||
|
- run: echo "done"
|
||||||
|
|
||||||
|
# release
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
@@ -42,15 +76,15 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.18.3
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '16'
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
- name: Build fronetend
|
- name: Build frontend
|
||||||
run: make build-frontend
|
run: make build-frontend
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
@@ -63,4 +97,4 @@ jobs:
|
|||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --rm-dist
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,10 +1,11 @@
|
|||||||
*.db
|
*.db
|
||||||
*.lock
|
|
||||||
*.bak
|
*.bak
|
||||||
_old
|
_old
|
||||||
rice-box.go
|
rice-box.go
|
||||||
.idea/
|
.idea/
|
||||||
filebrowser
|
/filebrowser
|
||||||
|
/filebrowser.exe
|
||||||
|
/dist
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ linters-settings:
|
|||||||
funlen:
|
funlen:
|
||||||
lines: 100
|
lines: 100
|
||||||
statements: 50
|
statements: 50
|
||||||
|
gci:
|
||||||
|
local-prefixes: github.com/filebrowser/filebrowser
|
||||||
goconst:
|
goconst:
|
||||||
min-len: 2
|
min-len: 2
|
||||||
min-occurrences: 2
|
min-occurrences: 2
|
||||||
@@ -26,8 +28,6 @@ linters-settings:
|
|||||||
min-complexity: 15
|
min-complexity: 15
|
||||||
goimports:
|
goimports:
|
||||||
local-prefixes: github.com/filebrowser/filebrowser
|
local-prefixes: github.com/filebrowser/filebrowser
|
||||||
golint:
|
|
||||||
min-confidence: 0
|
|
||||||
gomnd:
|
gomnd:
|
||||||
settings:
|
settings:
|
||||||
mnd:
|
mnd:
|
||||||
@@ -58,26 +58,25 @@ linters:
|
|||||||
- dogsled
|
- dogsled
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
|
- exportloopref
|
||||||
|
- exhaustive
|
||||||
- funlen
|
- funlen
|
||||||
- gochecknoinits
|
- gochecknoinits
|
||||||
- goconst
|
- goconst
|
||||||
- gocritic
|
- gocritic
|
||||||
- gocyclo
|
- gocyclo
|
||||||
- goimports
|
- goimports
|
||||||
- golint
|
|
||||||
- gomnd
|
- gomnd
|
||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
- gosec
|
- gosec
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- interfacer
|
|
||||||
- lll
|
- lll
|
||||||
- misspell
|
- misspell
|
||||||
- nakedret
|
- nakedret
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- scopelint
|
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- structcheck
|
- structcheck
|
||||||
- stylecheck
|
- stylecheck
|
||||||
@@ -89,19 +88,6 @@ linters:
|
|||||||
- whitespace
|
- whitespace
|
||||||
- prealloc
|
- prealloc
|
||||||
|
|
||||||
# don't enable:
|
|
||||||
# - asciicheck
|
|
||||||
# - exhaustive (TODO: enable after next release; current release at time of writing is v1.27)
|
|
||||||
# - gochecknoglobals
|
|
||||||
# - gocognit
|
|
||||||
# - godot
|
|
||||||
# - godox
|
|
||||||
# - goerr113
|
|
||||||
# - maligned
|
|
||||||
# - nestif
|
|
||||||
# - testpackage
|
|
||||||
# - wsl
|
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- path: cmd/.*.go
|
- path: cmd/.*.go
|
||||||
@@ -118,8 +104,12 @@ issues:
|
|||||||
- text: "Auther"
|
- text: "Auther"
|
||||||
linters:
|
linters:
|
||||||
- misspell
|
- misspell
|
||||||
|
- text: "strconv.Parse"
|
||||||
|
linters:
|
||||||
|
- gomnd
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
go: '1.18'
|
||||||
skip-dirs:
|
skip-dirs:
|
||||||
- frontend/
|
- frontend/
|
||||||
skip-files:
|
skip-files:
|
||||||
|
|||||||
110
.goreleaser.yml
110
.goreleaser.yml
@@ -41,7 +41,7 @@ archives:
|
|||||||
dockers:
|
dockers:
|
||||||
-
|
-
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
use_buildx: true
|
use: buildx
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--pull"
|
- "--pull"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
@@ -56,10 +56,10 @@ dockers:
|
|||||||
- "filebrowser/filebrowser:{{ .Tag }}-amd64"
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}-amd64"
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64"
|
||||||
extra_files:
|
extra_files:
|
||||||
- .docker.json
|
- docker_config.json
|
||||||
-
|
-
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
use_buildx: true
|
use: buildx
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--pull"
|
- "--pull"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
@@ -74,10 +74,10 @@ dockers:
|
|||||||
- "filebrowser/filebrowser:{{ .Tag }}-arm64"
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}-arm64"
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64"
|
||||||
extra_files:
|
extra_files:
|
||||||
- .docker.json
|
- docker_config.json
|
||||||
-
|
-
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
use_buildx: true
|
use: buildx
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--pull"
|
- "--pull"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
@@ -93,10 +93,10 @@ dockers:
|
|||||||
- "filebrowser/filebrowser:{{ .Tag }}-armv6"
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}-armv6"
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6"
|
||||||
extra_files:
|
extra_files:
|
||||||
- .docker.json
|
- docker_config.json
|
||||||
-
|
-
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
use_buildx: true
|
use: buildx
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--pull"
|
- "--pull"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
@@ -112,7 +112,82 @@ dockers:
|
|||||||
- "filebrowser/filebrowser:{{ .Tag }}-armv7"
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}-armv7"
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7"
|
||||||
extra_files:
|
extra_files:
|
||||||
- .docker.json
|
- docker_config.json
|
||||||
|
## s6 based docker images
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/amd64"
|
||||||
|
goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6.aarch64
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm64"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6.armhf
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm/v6"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: '6'
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6.armhf
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm/v7"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: '7'
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
docker_manifests:
|
docker_manifests:
|
||||||
- name_template: "filebrowser/filebrowser:latest"
|
- name_template: "filebrowser/filebrowser:latest"
|
||||||
image_templates:
|
image_templates:
|
||||||
@@ -132,6 +207,25 @@ docker_manifests:
|
|||||||
- "filebrowser/filebrowser:v{{ .Major }}-arm64"
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}-armv6"
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}-armv7"
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7"
|
||||||
|
## s6 image manifests
|
||||||
|
- name_template: "filebrowser/filebrowser:s6"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7-s6"
|
||||||
|
- name_template: "filebrowser/filebrowser:{{ .Tag }}-s6"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7-s6"
|
||||||
|
- name_template: "filebrowser/filebrowser:v{{ .Major }}-s6"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7-s6"
|
||||||
brews:
|
brews:
|
||||||
- name: filebrowser
|
- name: filebrowser
|
||||||
tap:
|
tap:
|
||||||
|
|||||||
271
CHANGELOG.md
271
CHANGELOG.md
@@ -2,6 +2,277 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [2.22.2](https://github.com/filebrowser/filebrowser/compare/v2.22.1...v2.22.2) (2022-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* display disk capacity in a correct format ([#2013](https://github.com/filebrowser/filebrowser/issues/2013)) ([dec3d62](https://github.com/filebrowser/filebrowser/commit/dec3d629d42de567aa708154ebc4e03b5223608c))
|
||||||
|
* don't calculate usage for files ([#1973](https://github.com/filebrowser/filebrowser/issues/1973)) ([577c0ef](https://github.com/filebrowser/filebrowser/commit/577c0efa9cff13628d5e3bac710ef568a00949e0)), closes [#1972](https://github.com/filebrowser/filebrowser/issues/1972) [#1967](https://github.com/filebrowser/filebrowser/issues/1967)
|
||||||
|
* preview url building fix ([#1976](https://github.com/filebrowser/filebrowser/issues/1976)) ([dcf0bc6](https://github.com/filebrowser/filebrowser/commit/dcf0bc65bfcfc7df3804d7392598a92019468cf7))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **backend:** upgrade golangci-lint to 1.46.2 ([#1991](https://github.com/filebrowser/filebrowser/issues/1991)) ([8118afd](https://github.com/filebrowser/filebrowser/commit/8118afd0ac0d25f4503c98879369764c35e7408e))
|
||||||
|
|
||||||
|
### [2.22.1](https://github.com/filebrowser/filebrowser/compare/v2.22.0...v2.22.1) (2022-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use correct basepath prefix for preview urls ([#1971](https://github.com/filebrowser/filebrowser/issues/1971)) ([1e7d3b2](https://github.com/filebrowser/filebrowser/commit/1e7d3b25c283c556d98c65f1c2f46db4e4178995))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **backend:** bump go version to 1.8.3 ([b16982d](https://github.com/filebrowser/filebrowser/commit/b16982df0f7da9eedb678455298b42ac55c86666))
|
||||||
|
|
||||||
|
## [2.22.0](https://github.com/filebrowser/filebrowser/compare/v2.21.1...v2.22.0) (2022-06-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add branding to the window title ([#1850](https://github.com/filebrowser/filebrowser/issues/1850)) ([f8dfbf7](https://github.com/filebrowser/filebrowser/commit/f8dfbf7eeecf3ee99ce906276777676f44e81e34))
|
||||||
|
* add disk usage information to the sidebar ([d1d8e3e](https://github.com/filebrowser/filebrowser/commit/d1d8e3e3405381b01317fe07ae729d70219415a7))
|
||||||
|
* automatically focus username field on login page ([596c732](https://github.com/filebrowser/filebrowser/commit/596c73288f5b53bd7e79ab8046136dc75ff078b9))
|
||||||
|
* invalid symlink icon ([b14b911](https://github.com/filebrowser/filebrowser/commit/b14b9114f837cacf9f7788e88c503142a81585be))
|
||||||
|
* page title localization ([8a43413](https://github.com/filebrowser/filebrowser/commit/8a43413f888440dc11b11c509abff45f706033d8))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow CSP inline styling ([5da9d74](https://github.com/filebrowser/filebrowser/commit/5da9d74da62c69c431361bcaf0c07dc1da237ea8))
|
||||||
|
* disable autocapitalize of login input (closes [#1910](https://github.com/filebrowser/filebrowser/issues/1910)) ([aed3af5](https://github.com/filebrowser/filebrowser/commit/aed3af58384697dc3de30f1450b837b0b74e4fa6))
|
||||||
|
* drag-and-drop folder upload ([e677c78](https://github.com/filebrowser/filebrowser/commit/e677c78471f09f8d2c21d63d7388e908924aa6d9))
|
||||||
|
* expired token error ([c3bd118](https://github.com/filebrowser/filebrowser/commit/c3bd1188aa396cbf00c593d259a9da0eddeeea3b))
|
||||||
|
* folder info on upload list ([d1d7b23](https://github.com/filebrowser/filebrowser/commit/d1d7b23da6cc0c9a2f2f3e17021ec4f13ea557dd))
|
||||||
|
* network error object message ([fc209f6](https://github.com/filebrowser/filebrowser/commit/fc209f64deff7a2793980d11ee738f7140c444cf))
|
||||||
|
* set correct scope when user home creation is enabled ([02730bb](https://github.com/filebrowser/filebrowser/commit/02730bb9bfa3bfbfa251bb4736fc4c08d33609ab))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **backend:** bump dependency versions ([7c9a75e](https://github.com/filebrowser/filebrowser/commit/7c9a75e72588f92d58fb58d32cdac352bce73b20))
|
||||||
|
* **deps:** bump async from 2.6.3 to 2.6.4 in /frontend ([#1933](https://github.com/filebrowser/filebrowser/issues/1933)) ([e5fa96b](https://github.com/filebrowser/filebrowser/commit/e5fa96b666eac2e46a02bde832488baca5f2cd6d))
|
||||||
|
* **deps:** bump eventsource from 1.1.0 to 1.1.1 in /frontend ([dd50369](https://github.com/filebrowser/filebrowser/commit/dd503695a1a8119a631643414d3a9070890f3f3c))
|
||||||
|
* **deps:** bump minimist from 1.2.5 to 1.2.6 in /frontend ([#1889](https://github.com/filebrowser/filebrowser/issues/1889)) ([a74c72d](https://github.com/filebrowser/filebrowser/commit/a74c72db451207e1275988f3d208fa6d6f0468a9))
|
||||||
|
* **deps:** bump minimist from 1.2.5 to 1.2.6 in /tools ([#1891](https://github.com/filebrowser/filebrowser/issues/1891)) ([f5b1e10](https://github.com/filebrowser/filebrowser/commit/f5b1e106183fb2192063a72fd195fc8c181ba8f9))
|
||||||
|
* **deps:** bump moment from 2.29.1 to 2.29.2 in /frontend ([#1900](https://github.com/filebrowser/filebrowser/issues/1900)) ([040584c](https://github.com/filebrowser/filebrowser/commit/040584c86563d869c7a05887ef1f781bce653033))
|
||||||
|
* **deps:** bump url-parse from 1.5.7 to 1.5.10 in /frontend ([#1841](https://github.com/filebrowser/filebrowser/issues/1841)) ([b2ad3f7](https://github.com/filebrowser/filebrowser/commit/b2ad3f73686a2abaa4fc62963fba6f83c9da9b5e))
|
||||||
|
* **frontend:** bump node version from 14 to 16 ([ac3ead8](https://github.com/filebrowser/filebrowser/commit/ac3ead8dcef9c64c6be8b5cbbceee143b2cc77a8))
|
||||||
|
* upgrade go version to 1.18.1 ([6bd34c7](https://github.com/filebrowser/filebrowser/commit/6bd34c76324780c1edd8625d5b22f5a84990852b))
|
||||||
|
|
||||||
|
### [2.21.1](https://github.com/filebrowser/filebrowser/compare/v2.21.0...v2.21.1) (2022-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* display user scope for admin users ([#1834](https://github.com/filebrowser/filebrowser/issues/1834)) ([6366cf0](https://github.com/filebrowser/filebrowser/commit/6366cf0b181f13eac38f69f1760d6f6f0586a5d1))
|
||||||
|
|
||||||
|
## [2.21.0](https://github.com/filebrowser/filebrowser/compare/v2.20.1...v2.21.0) (2022-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add colorized file type icons ([2948589](https://github.com/filebrowser/filebrowser/commit/2948589fcde6d1dca7f3ea52a621d8213fa3300c))
|
||||||
|
* add gallery view mode ([8888b9f](https://github.com/filebrowser/filebrowser/commit/8888b9f44640394df9e3583db4392472d7027a4b))
|
||||||
|
* add Ukrainian translation / update Russian translation ([#1753](https://github.com/filebrowser/filebrowser/issues/1753)) ([665e458](https://github.com/filebrowser/filebrowser/commit/665e45889cd333f1e3500e4bf38d15d229c9fe2a))
|
||||||
|
* add upload file list with progress ([#1825](https://github.com/filebrowser/filebrowser/issues/1825)) ([cf85404](https://github.com/filebrowser/filebrowser/commit/cf85404dd25cd7fdd73aa32878b4dc5f85ee3e96))
|
||||||
|
* smaller column width to fit 2 columns in landscape mobiles ([7870e89](https://github.com/filebrowser/filebrowser/commit/7870e89bc04f1494f2705795476b5f1c9d621e38))
|
||||||
|
* use real image path to calculate cache key ([c198723](https://github.com/filebrowser/filebrowser/commit/c1987237d05adcce77c614e5247a181ae5cdfacd))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* correctly handle non-ascii passwords for shared resources ([c782f21](https://github.com/filebrowser/filebrowser/commit/c782f21b0fa4511a15e7015117d075eaf5ea332c))
|
||||||
|
* don't expose scope for non-admin users ([0942fc7](https://github.com/filebrowser/filebrowser/commit/0942fc7042fd949cce91855169d0bcf16eb75771))
|
||||||
|
* open all the pdf files correctly ([#1742](https://github.com/filebrowser/filebrowser/issues/1742)) ([949f0f2](https://github.com/filebrowser/filebrowser/commit/949f0f277f6004904b3edfa716a8365ec93fa0fa))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **deps:** bump browserslist from 4.16.3 to 4.19.1 in /frontend ([8089007](https://github.com/filebrowser/filebrowser/commit/80890075e802e2a4217edbb01d6417122d702f5e))
|
||||||
|
* **deps:** bump dns-packet from 1.3.1 to 1.3.4 in /frontend ([a73d7f1](https://github.com/filebrowser/filebrowser/commit/a73d7f14b787935c6ebe525dba64b65f8ed733e2))
|
||||||
|
* **deps:** bump follow-redirects from 1.13.3 to 1.14.8 in /frontend ([f1f7f17](https://github.com/filebrowser/filebrowser/commit/f1f7f17ade8d40fc6cfb22c79960bce299876b56))
|
||||||
|
* **deps:** bump hosted-git-info from 2.8.8 to 2.8.9 in /frontend ([e7659ea](https://github.com/filebrowser/filebrowser/commit/e7659ea36bdf780ce17005f7170a2fef02a2d5e5))
|
||||||
|
* **deps:** bump path-parse from 1.0.6 to 1.0.7 in /frontend ([c014966](https://github.com/filebrowser/filebrowser/commit/c01496624a7ebfc8a7c256bd919a400367281cbb))
|
||||||
|
* **deps:** bump postcss from 7.0.35 to 7.0.39 in /frontend ([9182d33](https://github.com/filebrowser/filebrowser/commit/9182d33e1cc375473fb18989a92d20252884f096))
|
||||||
|
* **deps:** bump ssri from 6.0.1 to 6.0.2 in /frontend ([3717186](https://github.com/filebrowser/filebrowser/commit/371718634b11f32e68165f31c51b6b1139c829ec))
|
||||||
|
* **deps:** bump tar from 6.1.0 to 6.1.11 in /frontend ([010d16f](https://github.com/filebrowser/filebrowser/commit/010d16fc1d8f0200e5662943aef17ee89c5877b7))
|
||||||
|
* **deps:** bump url-parse from 1.5.1 to 1.5.4 in /frontend ([8906408](https://github.com/filebrowser/filebrowser/commit/8906408a8f0ed86d1e11ea90fc573b36815c9c0d))
|
||||||
|
* **deps:** bump url-parse from 1.5.4 to 1.5.7 in /frontend ([228ebea](https://github.com/filebrowser/filebrowser/commit/228ebea66cc871b33459406590a80ef906298e7d))
|
||||||
|
* **deps:** bump ws from 6.2.1 to 6.2.2 in /frontend ([73c8073](https://github.com/filebrowser/filebrowser/commit/73c80732d934bc8802a6d7c7a559cad37df405f0))
|
||||||
|
|
||||||
|
### [2.20.1](https://github.com/filebrowser/filebrowser/compare/v2.20.0...v2.20.1) (2021-12-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* revert to using the default alpine based docker image ([46d8046](https://github.com/filebrowser/filebrowser/commit/46d80464d2a67927b06a11b83fb137ad364a90ed))
|
||||||
|
|
||||||
|
## [2.20.0](https://github.com/filebrowser/filebrowser/compare/v2.19.0...v2.20.0) (2021-12-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* detect multiple subtitle languages ([#1723](https://github.com/filebrowser/filebrowser/issues/1723)) ([c2e03bb](https://github.com/filebrowser/filebrowser/commit/c2e03bbfab97fc6716bcdd59158e9d5129bf0ea7))
|
||||||
|
* use linuxserver based docker image ([b8f35ce](https://github.com/filebrowser/filebrowser/commit/b8f35ce9322c2b0dbf954cfd3ff584bc9f742fdd))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* set correct default database path in the config ([988d3e5](https://github.com/filebrowser/filebrowser/commit/988d3e5bdd224509ddc2f08444560e3087e9c67d))
|
||||||
|
* upgrade vulnerable versions of the library ([6eb3ab0](https://github.com/filebrowser/filebrowser/commit/6eb3ab063509a015ad630ab704ae3791461d0982))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* refactor makefile ([f81857a](https://github.com/filebrowser/filebrowser/commit/f81857acce25936a700945db5ef4af545eaeb1cf))
|
||||||
|
* remove deprecated goreleaser use_buildx param ([4d1b9dd](https://github.com/filebrowser/filebrowser/commit/4d1b9dd2112002a93bb26cece07dcfd81c31dc2c))
|
||||||
|
|
||||||
|
## [2.19.0](https://github.com/filebrowser/filebrowser/compare/v2.18.0...v2.19.0) (2021-11-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* prefetch previous and next images in preview. ([#1627](https://github.com/filebrowser/filebrowser/issues/1627)) ([7401d16](https://github.com/filebrowser/filebrowser/commit/7401d16e457bb232fd7dd7ef427e8960d465705c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* empty file listing on share ([e082397](https://github.com/filebrowser/filebrowser/commit/e08239781f61e7bb25d9b8c5c6cce90f34621a76))
|
||||||
|
* relative font sizes ([c29698d](https://github.com/filebrowser/filebrowser/commit/c29698dffac769077ab7c7869569a902979ee3d7))
|
||||||
|
|
||||||
|
## [2.18.0](https://github.com/filebrowser/filebrowser/compare/v2.17.2...v2.18.0) (2021-10-31)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability to select file modified time format ([#1536](https://github.com/filebrowser/filebrowser/issues/1536)) ([0426629](https://github.com/filebrowser/filebrowser/commit/0426629a59c712849570d3e29956948ae7725a4a))
|
||||||
|
* add manifest theme color param ([#1542](https://github.com/filebrowser/filebrowser/issues/1542)) ([0358e42](https://github.com/filebrowser/filebrowser/commit/0358e42d2c206732fffa77714f5a66f4fe50a69d))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* back button behaviour in preview ([#1573](https://github.com/filebrowser/filebrowser/issues/1573)) ([deabc80](https://github.com/filebrowser/filebrowser/commit/deabc80fd7670983039dfcd29531b45002ca5d9e))
|
||||||
|
* fix sidebar navigation on mobile devices ([#1618](https://github.com/filebrowser/filebrowser/issues/1618)) ([f09bf3e](https://github.com/filebrowser/filebrowser/commit/f09bf3e1d076b27d29ba8a91cf448a99993bc444))
|
||||||
|
* search box is misaligned when the browser preferred font size is other than 16px ([#1613](https://github.com/filebrowser/filebrowser/issues/1613)) ([6f345be](https://github.com/filebrowser/filebrowser/commit/6f345be3e47ba57ecc1eb9a62587ab949078c125))
|
||||||
|
* security issue in command runner (closes [#1621](https://github.com/filebrowser/filebrowser/issues/1621)) ([74b7cd8](https://github.com/filebrowser/filebrowser/commit/74b7cd8e81840537a8206317344f118093153e8d))
|
||||||
|
* set correct editor height regardless of preferred font size ([#1614](https://github.com/filebrowser/filebrowser/issues/1614)) ([ddd4ffa](https://github.com/filebrowser/filebrowser/commit/ddd4ffa4caa6b292a3a644ecd897aba1237c7503))
|
||||||
|
* zoom pics when dlclick at first time ([#1561](https://github.com/filebrowser/filebrowser/issues/1561)) ([b6a51be](https://github.com/filebrowser/filebrowser/commit/b6a51bed516814944f8aa41440652242d57824c5))
|
||||||
|
|
||||||
|
### [2.17.2](https://github.com/filebrowser/filebrowser/compare/v2.17.1...v2.17.2) (2021-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bug with inlineLink not creating url properly ([#1515](https://github.com/filebrowser/filebrowser/issues/1515)) ([43a4609](https://github.com/filebrowser/filebrowser/commit/43a460993c3f0d158b876db4b20caa7963e9f361))
|
||||||
|
|
||||||
|
### [2.17.1](https://github.com/filebrowser/filebrowser/compare/v2.17.0...v2.17.1) (2021-08-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* internal server error if --disable-preview-resize flag is set (closes [#1510](https://github.com/filebrowser/filebrowser/issues/1510)) ([4c3099a](https://github.com/filebrowser/filebrowser/commit/4c3099a086c206dcb3bc70ee8c8da02eee61c30b))
|
||||||
|
|
||||||
|
## [2.17.0](https://github.com/filebrowser/filebrowser/compare/v2.16.1...v2.17.0) (2021-08-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* open file option on preview ([76add9e](https://github.com/filebrowser/filebrowser/commit/76add9e5274b0373c6b983e3b20e387a14ea6c9e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 401 error in share view open file button ([#1495](https://github.com/filebrowser/filebrowser/issues/1495)) ([25c8788](https://github.com/filebrowser/filebrowser/commit/25c87883908babde073390a2e2320a8e5880a87c))
|
||||||
|
* escape quote on index template ([23d646c](https://github.com/filebrowser/filebrowser/commit/23d646c456876d06cf48e71c1e57b69de99511f0)), closes [#1501](https://github.com/filebrowser/filebrowser/issues/1501)
|
||||||
|
* file caching directive ([c63cc5a](https://github.com/filebrowser/filebrowser/commit/c63cc5a2d25909cc4e2f2e7235f276ec66c32bf2))
|
||||||
|
|
||||||
|
### [2.16.1](https://github.com/filebrowser/filebrowser/compare/v2.16.0...v2.16.1) (2021-08-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check symlink target type (closes [#1488](https://github.com/filebrowser/filebrowser/issues/1488)) ([76b466f](https://github.com/filebrowser/filebrowser/commit/76b466f6492e74cf13e66a33e7e5f597ac92b240))
|
||||||
|
|
||||||
|
## [2.16.0](https://github.com/filebrowser/filebrowser/compare/v2.15.0...v2.16.0) (2021-07-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* browser cache directives ([190cb99](https://github.com/filebrowser/filebrowser/commit/190cb99a79a0d438eca2da13539f8c6449ad73ac))
|
||||||
|
* display error messages on settings ([6032038](https://github.com/filebrowser/filebrowser/commit/603203848a8b2221158088b6d849609db4c0c46c))
|
||||||
|
* file name on page title ([16a34de](https://github.com/filebrowser/filebrowser/commit/16a34defc02554a77c6ac47b9e17e69d098a09fe))
|
||||||
|
* gzip encoding for static js files ([aa172b8](https://github.com/filebrowser/filebrowser/commit/aa172b8bb5f17d5f5cb9666bfb5ee650d8091fb5))
|
||||||
|
* loading spinner on views navigation ([976eb55](https://github.com/filebrowser/filebrowser/commit/976eb5583dae474125fd7ddec5dc19b6c291f98f))
|
||||||
|
* message for connection error ([5e6f14b](https://github.com/filebrowser/filebrowser/commit/5e6f14b5dcb9c5efdf526f1346e09c2d0b2f6974))
|
||||||
|
* mod time title on file info ([7d1e030](https://github.com/filebrowser/filebrowser/commit/7d1e03075d2c27148f60813defa0f68403d1d3c2))
|
||||||
|
* open file option on share ([1c25f6e](https://github.com/filebrowser/filebrowser/commit/1c25f6ee69bd71eed82af7020006d0e27537a967))
|
||||||
|
* show more button on share ([ba8c09f](https://github.com/filebrowser/filebrowser/commit/ba8c09f454feeadf4a1e97547a34151a81b389d5))
|
||||||
|
* support for IE11 browser ([7ec24d9](https://github.com/filebrowser/filebrowser/commit/7ec24d9d7794fa37825f64ca2d1575f568fb1362))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* break resource create/update handlers on error (closes [#1464](https://github.com/filebrowser/filebrowser/issues/1464)) ([5072bbb](https://github.com/filebrowser/filebrowser/commit/5072bbb2cbf5b29d041629faa8367f15e4d145a2))
|
||||||
|
* copying files with special characters ([20ebbf6](https://github.com/filebrowser/filebrowser/commit/20ebbf6611b734371426fb1b9cb5e388be90bf7e))
|
||||||
|
* delete image cache when moving ([8973c45](https://github.com/filebrowser/filebrowser/commit/8973c4598ff817647f1f1ad6ee36480054cd2776))
|
||||||
|
* don't remove files on unsuccessful updates (closes [#1456](https://github.com/filebrowser/filebrowser/issues/1456)) ([6b19ab6](https://github.com/filebrowser/filebrowser/commit/6b19ab6613b12be7f075299cd98f4b41d43827c7))
|
||||||
|
* failure on broken symlink deletion ([8650d2f](https://github.com/filebrowser/filebrowser/commit/8650d2ffe7a29cbafa800efcecbf6a61598a9f0c))
|
||||||
|
* inconsistent double click on listing item ([ba7e71a](https://github.com/filebrowser/filebrowser/commit/ba7e71a7c3b0cc71012e5adf94b1c642e554972e))
|
||||||
|
* no items displayed on file listing ([18889ad](https://github.com/filebrowser/filebrowser/commit/18889ad725f7f7e5a7e3f7abcf156487556dbeaf))
|
||||||
|
* omit file content ([209f9fa](https://github.com/filebrowser/filebrowser/commit/209f9fa77f751054512355f2b74b9b7258465d0b))
|
||||||
|
* short commit sha and typo fix in Makefile ([#1411](https://github.com/filebrowser/filebrowser/issues/1411)) ([46ee595](https://github.com/filebrowser/filebrowser/commit/46ee59538914dc2859f0da6b32e2d062d0a01b10))
|
||||||
|
|
||||||
|
## [2.15.0](https://github.com/filebrowser/filebrowser/compare/v2.14.1...v2.15.0) (2021-04-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add EXIF thumbnail support for JPEG files ([#1234](https://github.com/filebrowser/filebrowser/issues/1234)) ([7dd5b34](https://github.com/filebrowser/filebrowser/commit/7dd5b34d425dfbc2782152310483cbecf85c800a))
|
||||||
|
* dynamic autoplay on previewer ([a76e01d](https://github.com/filebrowser/filebrowser/commit/a76e01d2b78a785f3665a8b3532c7cc566bfabce))
|
||||||
|
* dynamic item count on file listing ([6c8ee96](https://github.com/filebrowser/filebrowser/commit/6c8ee96e6a21fae5d4608bdc7a5c5a161d7dafd3))
|
||||||
|
* dynamic zoom limit on previewer ([e410272](https://github.com/filebrowser/filebrowser/commit/e410272e6be6a0b660efe8d4eee6c6e9dd834cc5))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* buttons without permission on header ([1516d99](https://github.com/filebrowser/filebrowser/commit/1516d9932bf9926ac8b4cb3e738a5f51e80d5b1d))
|
||||||
|
* check modify permission on file overwrite ([59f9964](https://github.com/filebrowser/filebrowser/commit/59f9964e80c8233775f27be33a4c16a31bfe848a))
|
||||||
|
* empty archive name on directory download ([2697093](https://github.com/filebrowser/filebrowser/commit/2697093ac151f74eea3022951d128acfe04d1dcf))
|
||||||
|
* empty text file on editor ([e9baf0c](https://github.com/filebrowser/filebrowser/commit/e9baf0c4b688fab291cdc842ec464c7a7a816499))
|
||||||
|
* error causes panic on upload ([e1a6f59](https://github.com/filebrowser/filebrowser/commit/e1a6f593e1824e7fa4345a61dff5b1bb8cd22d05))
|
||||||
|
* hidden editor header on Safari ([b521dec](https://github.com/filebrowser/filebrowser/commit/b521dec8f9b14dd92248c429e902ebc639046389))
|
||||||
|
* image quality switch on previewer ([c0d85f3](https://github.com/filebrowser/filebrowser/commit/c0d85f3d85926c8790757bf142140d19455ae8ca))
|
||||||
|
* list item interactions on share ([87f1881](https://github.com/filebrowser/filebrowser/commit/87f1881b429877a740ea84a8e783ad4103248289))
|
||||||
|
* missing bold variation for Roboto font ([98d79b8](https://github.com/filebrowser/filebrowser/commit/98d79b8ed955df5691a306d709b4ab60d91da408))
|
||||||
|
* mouse wheel zoom on previewer ([fcb115f](https://github.com/filebrowser/filebrowser/commit/fcb115f42d33db2be7a4d428ec53d65d6050320b))
|
||||||
|
* no header button animations on file listing ([fe80730](https://github.com/filebrowser/filebrowser/commit/fe80730bb135b38e4d9de470c75cbe10b1aec201))
|
||||||
|
|
||||||
|
### [2.14.1](https://github.com/filebrowser/filebrowser/compare/v2.14.0...v2.14.1) (2021-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* display public routes with header proxy auth ([da54bd6](https://github.com/filebrowser/filebrowser/commit/da54bd6c214d7ee39b71d710ddfe6dd25fc4e5d6))
|
||||||
|
|
||||||
|
## [2.14.0](https://github.com/filebrowser/filebrowser/compare/v2.13.0...v2.14.0) (2021-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add health check handler ([a721dc1](https://github.com/filebrowser/filebrowser/commit/a721dc1f314732e60d331a1a7da97d06e0e8b613))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* hide dotfile error on share ([5f4a031](https://github.com/filebrowser/filebrowser/commit/5f4a0317ab5685fe4a558df74e604c12e04a1c10))
|
||||||
|
* prefix handling on http router ([93a35ad](https://github.com/filebrowser/filebrowser/commit/93a35ad2516accdcb9735db509550979d01de2c3))
|
||||||
|
* qr code url on share ([22f4be8](https://github.com/filebrowser/filebrowser/commit/22f4be8f54162b7cf494177705ffb8b09117bd01))
|
||||||
|
* text file detection on editor ([eeadc53](https://github.com/filebrowser/filebrowser/commit/eeadc532fe6057969b3c1a4726f236851b154cfa))
|
||||||
|
|
||||||
## [2.13.0](https://github.com/filebrowser/filebrowser/compare/v2.12.1...v2.13.0) (2021-03-14)
|
## [2.13.0](https://github.com/filebrowser/filebrowser/compare/v2.12.1...v2.13.0) (2021-03-14)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
10
Dockerfile
10
Dockerfile
@@ -1,11 +1,15 @@
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk --update add ca-certificates
|
RUN apk --update add ca-certificates \
|
||||||
RUN apk --update add mailcap
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
VOLUME /srv
|
VOLUME /srv
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
COPY .docker.json /.filebrowser.json
|
COPY docker_config.json /.filebrowser.json
|
||||||
COPY filebrowser /filebrowser
|
COPY filebrowser /filebrowser
|
||||||
|
|
||||||
ENTRYPOINT [ "/filebrowser" ]
|
ENTRYPOINT [ "/filebrowser" ]
|
||||||
16
Dockerfile.s6
Normal file
16
Dockerfile.s6
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM ghcr.io/linuxserver/baseimage-alpine:3.14
|
||||||
|
|
||||||
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
|
# copy local files
|
||||||
|
COPY docker/root/ /
|
||||||
|
COPY filebrowser /usr/bin/filebrowser
|
||||||
|
|
||||||
|
# ports and volumes
|
||||||
|
VOLUME /srv /config /database
|
||||||
|
EXPOSE 80
|
||||||
16
Dockerfile.s6.aarch64
Normal file
16
Dockerfile.s6.aarch64
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM ghcr.io/linuxserver/baseimage-alpine:arm64v8-3.14
|
||||||
|
|
||||||
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
|
# copy local files
|
||||||
|
COPY docker/root/ /
|
||||||
|
COPY filebrowser /usr/bin/filebrowser
|
||||||
|
|
||||||
|
# ports and volumes
|
||||||
|
VOLUME /srv /config /database
|
||||||
|
EXPOSE 80
|
||||||
16
Dockerfile.s6.armhf
Normal file
16
Dockerfile.s6.armhf
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM ghcr.io/linuxserver/baseimage-alpine:arm32v7-3.14
|
||||||
|
|
||||||
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
|
# copy local files
|
||||||
|
COPY docker/root/ /
|
||||||
|
COPY filebrowser /usr/bin/filebrowser
|
||||||
|
|
||||||
|
# ports and volumes
|
||||||
|
VOLUME /srv /config /database
|
||||||
|
EXPOSE 80
|
||||||
104
Makefile
104
Makefile
@@ -1,94 +1,68 @@
|
|||||||
SHELL := /bin/bash
|
include common.mk
|
||||||
BASE_PATH := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
include tools.mk
|
||||||
VERSION ?= $(shell git describe --tags --always --match=v* 2> /dev/null || \
|
|
||||||
cat $(CURDIR)/.version 2> /dev/null || echo v0)
|
|
||||||
VERSION_HASH = $(shell git rev-parse HEAD)
|
|
||||||
|
|
||||||
BIN = $(BASE_PATH)/bin
|
LDFLAGS += -X "$(MODULE)/version.Version=$(VERSION)" -X "$(MODULE)/version.CommitSHA=$(VERSION_HASH)"
|
||||||
PATH := $(BIN):$(PATH)
|
|
||||||
export PATH
|
|
||||||
|
|
||||||
# printing
|
## Build:
|
||||||
V = 0
|
|
||||||
Q = $(if $(filter 1,$V),,@)
|
|
||||||
M = $(shell printf "\033[34;1m▶\033[0m")
|
|
||||||
|
|
||||||
GO = GOGC=off go
|
|
||||||
# go module
|
|
||||||
MODULE = $(shell env GO111MODULE=on $(GO) list -m)
|
|
||||||
|
|
||||||
DATE ?= $(shell date +%FT%T%z)
|
|
||||||
VERSION ?= $(shell git describe --tags --always --match=v* 2> /dev/null || \
|
|
||||||
cat $(CURDIR)/.version 2> /dev/null || echo v0)
|
|
||||||
VERSION_HASH = $(shell git rev-parse HEAD)
|
|
||||||
BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
|
|
||||||
|
|
||||||
LDFLAGS += -X "$(MODULE)/varsion.Version=$(VERSION)" -X "$(MODULE)/varsion.CommitSHA=$(VERSION_HASH)"
|
|
||||||
|
|
||||||
# tools
|
|
||||||
$(BIN):
|
|
||||||
@mkdir -p $@
|
|
||||||
$(BIN)/%: | $(BIN) ; $(info $(M) installing $(PACKAGE)…)
|
|
||||||
$Q env GOBIN=$(BIN) $(GO) install $(PACKAGE)
|
|
||||||
|
|
||||||
GOLANGCI_LINT = $(BIN)/golangci-lint
|
|
||||||
$(BIN)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint@v1.37.1
|
|
||||||
|
|
||||||
GOIMPORTS = $(BIN)/goimports
|
|
||||||
$(BIN)/goimports: PACKAGE=golang.org/x/tools/cmd/goimports@v0.1.0
|
|
||||||
|
|
||||||
## build: Build
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: | build-frontend build-backend ; $(info $(M) building…)
|
build: | build-frontend build-backend ## Build binary
|
||||||
|
|
||||||
## build-frontend: Build frontend
|
|
||||||
.PHONY: build-frontend
|
.PHONY: build-frontend
|
||||||
build-frontend: | ; $(info $(M) building frontend…)
|
build-frontend: ## Build frontend
|
||||||
$Q cd frontend && npm ci && npm run build
|
$Q cd frontend && npm ci && npm run build
|
||||||
|
|
||||||
## build-backend: Build backend
|
|
||||||
.PHONY: build-backend
|
.PHONY: build-backend
|
||||||
build-backend: | ; $(info $(M) building backend…)
|
build-backend: ## Build backend
|
||||||
$Q $(GO) build -ldflags '$(LDFLAGS)' -o filebrowser
|
$Q $(go) build -ldflags '$(LDFLAGS)' -o .
|
||||||
|
|
||||||
## test: Run all tests
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: | test-frontend test-backend ; $(info $(M) running tests…)
|
test: | test-frontend test-backend ## Run all tests
|
||||||
|
|
||||||
## test-frontend: Run frontend tests
|
|
||||||
.PHONY: test-frontend
|
.PHONY: test-frontend
|
||||||
test-frontend: | ; $(info $(M) running frontend tests…)
|
test-frontend: ## Run frontend tests
|
||||||
|
|
||||||
## test-backend: Run backend tests
|
|
||||||
.PHONY: test-backend
|
.PHONY: test-backend
|
||||||
test-backend: | $(RICE) ; $(info $(M) running backend tests…)
|
test-backend: ## Run backend tests
|
||||||
$Q $(GO) test -v ./...
|
$Q $(go) test -v ./...
|
||||||
|
|
||||||
## lint: Lint
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: lint-frontend lint-backend lint-commits | ; $(info $(M) running all linters…)
|
lint: lint-frontend lint-backend lint-commits ## Run all linters
|
||||||
|
|
||||||
## lint-frontend: Lint frontend
|
|
||||||
.PHONY: lint-frontend
|
.PHONY: lint-frontend
|
||||||
lint-frontend: | ; $(info $(M) running frontend linters…)
|
lint-frontend: ## Run frontend linters
|
||||||
$Q cd frontend && npm ci && npm run lint
|
$Q cd frontend && npm ci && npm run lint
|
||||||
|
|
||||||
## lint-backend: Lint backend
|
|
||||||
.PHONY: lint-backend
|
.PHONY: lint-backend
|
||||||
lint-backend: | $(GOLANGCI_LINT) ; $(info $(M) running backend linters…)
|
lint-backend: | $(golangci-lint) ## Run backend linters
|
||||||
$Q $(GOLANGCI_LINT) run
|
$Q $(golangci-lint) run -v
|
||||||
|
|
||||||
## lint-commits: Lint commits
|
|
||||||
.PHONY: lint-commits
|
.PHONY: lint-commits
|
||||||
lint-commits: | ; $(info $(M) running commitlint…)
|
lint-commits: $(commitlint) ## Run commit linters
|
||||||
$Q ./scripts/commitlint.sh
|
$Q ./scripts/commitlint.sh
|
||||||
|
|
||||||
## bump-version: Bump app version
|
fmt: $(goimports) ## Format source files
|
||||||
|
$Q $(goimports) -local $(MODULE) -w $$(find . -type f -name '*.go' -not -path "./vendor/*")
|
||||||
|
|
||||||
|
clean: clean-tools ## Clean
|
||||||
|
|
||||||
|
## Release:
|
||||||
|
|
||||||
.PHONY: bump-version
|
.PHONY: bump-version
|
||||||
bump-version: | ; $(info $(M) creating a new release…)
|
bump-version: $(standard-version) ## Bump app version
|
||||||
$Q ./scripts/bump_version.sh
|
$Q ./scripts/bump_version.sh
|
||||||
|
|
||||||
## help: Show this help
|
## Help:
|
||||||
.PHONY: help
|
help: ## Show this help
|
||||||
help:
|
@echo ''
|
||||||
@sed -n 's/^## //p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /' | sort
|
@echo 'Usage:'
|
||||||
|
@echo ' ${YELLOW}make${RESET} ${GREEN}<target> [options]${RESET}'
|
||||||
|
@echo ''
|
||||||
|
@echo 'Options:'
|
||||||
|
@$(call global_option, "V [0|1]", "enable verbose mode (default:0)")
|
||||||
|
@echo ''
|
||||||
|
@echo 'Targets:'
|
||||||
|
@awk 'BEGIN {FS = ":.*?## "} { \
|
||||||
|
if (/^[a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \
|
||||||
|
else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \
|
||||||
|
}' $(MAKEFILE_LIST)
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
[](https://github.com/filebrowser/filebrowser/releases/latest)
|
[](https://github.com/filebrowser/filebrowser/releases/latest)
|
||||||
[](http://webchat.freenode.net/?channels=%23filebrowser)
|
[](http://webchat.freenode.net/?channels=%23filebrowser)
|
||||||
|
|
||||||
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
|
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|||||||
26
SECURITY.md
Normal file
26
SECURITY.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 2.x | :white_check_mark: |
|
||||||
|
| < 2.0 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Vulnerabilities should be reported to filebrowser@googlegroups.com - which is a private, maintainer-only group. Maintainers will attempt to respond to/confirm reports within 2-3 days, but if you believe your report to be "critical" to user safety and security, please note as such in the subject. We have tens of thousands of users using our software, and take security vulnerabilities seriously.
|
||||||
|
|
||||||
|
When reporting an issue, where possible, please provide at least:
|
||||||
|
|
||||||
|
* The commit version the issue was identified at
|
||||||
|
* A proof of concept (plaintext; no binaries)
|
||||||
|
* Steps to reproduce
|
||||||
|
* Your recommended remediation(s), if any.
|
||||||
|
|
||||||
|
The FileBrowser team is a volunteer-only effort, and may reach back out for clarification.
|
||||||
|
|
||||||
|
> Note: Please do not open public issues for security issues, as GitHub does not provide facility for private issues, and deleting the issue makes it hard to triage/respond back to the reporter.
|
||||||
@@ -41,6 +41,7 @@ func addConfigFlags(flags *pflag.FlagSet) {
|
|||||||
flags.String("recaptcha.secret", "", "ReCaptcha secret")
|
flags.String("recaptcha.secret", "", "ReCaptcha secret")
|
||||||
|
|
||||||
flags.String("branding.name", "", "replace 'File Browser' by this name")
|
flags.String("branding.name", "", "replace 'File Browser' by this name")
|
||||||
|
flags.String("branding.color", "", "set the theme color")
|
||||||
flags.String("branding.files", "", "path to directory with images and custom styles")
|
flags.String("branding.files", "", "path to directory with images and custom styles")
|
||||||
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
|
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
|
||||||
}
|
}
|
||||||
@@ -121,7 +122,7 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) {
|
func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
|
||||||
|
|
||||||
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
||||||
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
||||||
@@ -131,6 +132,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
|
|||||||
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
|
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
|
||||||
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
|
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
|
||||||
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal)
|
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal)
|
||||||
|
fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color)
|
||||||
fmt.Fprintln(w, "\nServer:")
|
fmt.Fprintln(w, "\nServer:")
|
||||||
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
|
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
|
||||||
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
|
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ you want to change. Other options will remain unchanged.`,
|
|||||||
set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name))
|
set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name))
|
||||||
case "branding.name":
|
case "branding.name":
|
||||||
set.Branding.Name = mustGetString(flags, flag.Name)
|
set.Branding.Name = mustGetString(flags, flag.Name)
|
||||||
|
case "branding.color":
|
||||||
|
set.Branding.Color = mustGetString(flags, flag.Name)
|
||||||
case "branding.disableExternal":
|
case "branding.disableExternal":
|
||||||
set.Branding.DisableExternal = mustGetBool(flags, flag.Name)
|
set.Branding.DisableExternal = mustGetBool(flags, flag.Name)
|
||||||
case "branding.files":
|
case "branding.files":
|
||||||
|
|||||||
22
cmd/root.go
22
cmd/root.go
@@ -3,8 +3,8 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -61,10 +61,10 @@ func addServerFlags(flags *pflag.FlagSet) {
|
|||||||
flags.StringP("key", "k", "", "tls key")
|
flags.StringP("key", "k", "", "tls key")
|
||||||
flags.StringP("root", "r", ".", "root to prepend to relative paths")
|
flags.StringP("root", "r", ".", "root to prepend to relative paths")
|
||||||
flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)")
|
flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)")
|
||||||
flags.Uint32("socket-perm", 0666, "unix socket file permissions")
|
flags.Uint32("socket-perm", 0666, "unix socket file permissions") //nolint:gomnd
|
||||||
flags.StringP("baseurl", "b", "", "base url")
|
flags.StringP("baseurl", "b", "", "base url")
|
||||||
flags.String("cache-dir", "", "file cache directory (disabled if empty)")
|
flags.String("cache-dir", "", "file cache directory (disabled if empty)")
|
||||||
flags.Int("img-processors", 4, "image processors count")
|
flags.Int("img-processors", 4, "image processors count") //nolint:gomnd
|
||||||
flags.Bool("disable-thumbnails", false, "disable image thumbnails")
|
flags.Bool("disable-thumbnails", false, "disable image thumbnails")
|
||||||
flags.Bool("disable-preview-resize", false, "disable resize of image previews")
|
flags.Bool("disable-preview-resize", false, "disable resize of image previews")
|
||||||
flags.Bool("disable-exec", false, "disables Command Runner feature")
|
flags.Bool("disable-exec", false, "disables Command Runner feature")
|
||||||
@@ -128,7 +128,7 @@ user created with the credentials from options "username" and "password".`,
|
|||||||
cacheDir, err := cmd.Flags().GetString("cache-dir")
|
cacheDir, err := cmd.Flags().GetString("cache-dir")
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
if cacheDir != "" {
|
if cacheDir != "" {
|
||||||
if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet
|
if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet,gomnd
|
||||||
log.Fatalf("can't make directory %s: %s", cacheDir, err)
|
log.Fatalf("can't make directory %s: %s", cacheDir, err)
|
||||||
}
|
}
|
||||||
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
||||||
@@ -299,7 +299,7 @@ func setupLog(logMethod string) {
|
|||||||
case "stderr":
|
case "stderr":
|
||||||
log.SetOutput(os.Stderr)
|
log.SetOutput(os.Stderr)
|
||||||
case "":
|
case "":
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(io.Discard)
|
||||||
default:
|
default:
|
||||||
log.SetOutput(&lumberjack.Logger{
|
log.SetOutput(&lumberjack.Logger{
|
||||||
Filename: logMethod,
|
Filename: logMethod,
|
||||||
@@ -312,9 +312,10 @@ func setupLog(logMethod string) {
|
|||||||
|
|
||||||
func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
||||||
set := &settings.Settings{
|
set := &settings.Settings{
|
||||||
Key: generateKey(),
|
Key: generateKey(),
|
||||||
Signup: false,
|
Signup: false,
|
||||||
CreateUserDir: false,
|
CreateUserDir: false,
|
||||||
|
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
|
||||||
Defaults: settings.UserDefaults{
|
Defaults: settings.UserDefaults{
|
||||||
Scope: ".",
|
Scope: ".",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
@@ -330,6 +331,11 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
|||||||
Download: true,
|
Download: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
AuthMethod: "",
|
||||||
|
Branding: settings.Branding{},
|
||||||
|
Commands: nil,
|
||||||
|
Shell: nil,
|
||||||
|
Rules: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ You can also specify an optional parameter (index_end) so
|
|||||||
you can remove all commands from 'index' to 'index_end',
|
you can remove all commands from 'index' to 'index_end',
|
||||||
including 'index_end'.`,
|
including 'index_end'.`,
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := cobra.RangeArgs(1, 2)(cmd, args); err != nil {
|
if err := cobra.RangeArgs(1, 2)(cmd, args); err != nil { //nolint:gomnd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ var usersCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printUsers(usrs []*users.User) {
|
func printUsers(usrs []*users.User) {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
|
||||||
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
|
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
|
||||||
|
|
||||||
for _, u := range usrs {
|
for _, u := range usrs {
|
||||||
@@ -53,7 +53,7 @@ func printUsers(usrs []*users.User) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseUsernameOrID(arg string) (username string, id uint) {
|
func parseUsernameOrID(arg string) (username string, id uint) {
|
||||||
id64, err := strconv.ParseUint(arg, 10, 0)
|
id64, err := strconv.ParseUint(arg, 10, 64) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return arg, 0
|
return arg, 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/asdine/storm"
|
"github.com/asdine/storm/v3"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
@@ -72,7 +72,7 @@ func dbExists(path string) (bool, error) {
|
|||||||
d := filepath.Dir(path)
|
d := filepath.Dir(path)
|
||||||
_, err = os.Stat(d)
|
_, err = os.Stat(d)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
if err := os.MkdirAll(d, 0700); err != nil { //nolint:govet
|
if err := os.MkdirAll(d, 0700); err != nil { //nolint:govet,gomnd
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
28
common.mk
Normal file
28
common.mk
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
DATE ?= $(shell date +%FT%T%z)
|
||||||
|
BASE_PATH := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
VERSION ?= $(shell git describe --tags --always --match=v* 2> /dev/null || \
|
||||||
|
cat $(CURDIR)/.version 2> /dev/null || echo v0)
|
||||||
|
VERSION_HASH = $(shell git rev-parse HEAD)
|
||||||
|
BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
|
go = GOGC=off go
|
||||||
|
MODULE = $(shell env GO111MODULE=on go list -m)
|
||||||
|
|
||||||
|
# printing
|
||||||
|
# $Q (quiet) is used in the targets as a replacer for @.
|
||||||
|
# This macro helps to print the command for debugging by setting V to 1. Example `make test-unit V=1`
|
||||||
|
V = 0
|
||||||
|
Q = $(if $(filter 1,$V),,@)
|
||||||
|
# $M is a macro to print a colored ▶ character. Example `$(info $(M) running coverage tests…)` will print "▶ running coverage tests…"
|
||||||
|
M = $(shell printf "\033[34;1m▶\033[0m")
|
||||||
|
|
||||||
|
GREEN := $(shell tput -Txterm setaf 2)
|
||||||
|
YELLOW := $(shell tput -Txterm setaf 3)
|
||||||
|
WHITE := $(shell tput -Txterm setaf 7)
|
||||||
|
CYAN := $(shell tput -Txterm setaf 6)
|
||||||
|
RESET := $(shell tput -Txterm sgr0)
|
||||||
|
|
||||||
|
define global_option
|
||||||
|
printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n" $(1) $(2)
|
||||||
|
endef
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -37,11 +37,11 @@ func (f *FileCache) Store(ctx context.Context, key string, value []byte) error {
|
|||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
fileName := f.getFileName(key)
|
fileName := f.getFileName(key)
|
||||||
if err := f.fs.MkdirAll(filepath.Dir(fileName), 0700); err != nil {
|
if err := f.fs.MkdirAll(filepath.Dir(fileName), 0700); err != nil { //nolint:gomnd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := afero.WriteFile(f.fs, fileName, value, 0700); err != nil {
|
if err := afero.WriteFile(f.fs, fileName, value, 0700); err != nil { //nolint:gomnd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ func (f *FileCache) Load(ctx context.Context, key string) (value []byte, exist b
|
|||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
value, err = ioutil.ReadAll(r)
|
value, err = io.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|||||||
8
docker/root/defaults/settings.json
Normal file
8
docker/root/defaults/settings.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"port": 80,
|
||||||
|
"baseURL": "",
|
||||||
|
"address": "",
|
||||||
|
"log": "stdout",
|
||||||
|
"database": "/database/filebrowser.db",
|
||||||
|
"root": "/srv"
|
||||||
|
}
|
||||||
15
docker/root/etc/cont-init.d/20-config
Normal file
15
docker/root/etc/cont-init.d/20-config
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/with-contenv bash
|
||||||
|
|
||||||
|
# make folders
|
||||||
|
mkdir -p /database
|
||||||
|
|
||||||
|
# copy config
|
||||||
|
if [ ! -f "/config/settings.json" ]; then
|
||||||
|
cp -a /defaults/settings.json /config/settings.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
# permissions
|
||||||
|
chown abc:abc \
|
||||||
|
/config/settings.json \
|
||||||
|
/database \
|
||||||
|
/srv
|
||||||
3
docker/root/etc/services.d/filebrowser/run
Normal file
3
docker/root/etc/services.d/filebrowser/run
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/with-contenv bash
|
||||||
|
|
||||||
|
exec s6-setuidgid abc filebrowser -c /config/settings.json -d /database/filebrowser.db;
|
||||||
145
files/file.go
145
files/file.go
@@ -34,6 +34,7 @@ type FileInfo struct {
|
|||||||
ModTime time.Time `json:"modified"`
|
ModTime time.Time `json:"modified"`
|
||||||
Mode os.FileMode `json:"mode"`
|
Mode os.FileMode `json:"mode"`
|
||||||
IsDir bool `json:"isDir"`
|
IsDir bool `json:"isDir"`
|
||||||
|
IsSymlink bool `json:"isSymlink"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Subtitles []string `json:"subtitles,omitempty"`
|
Subtitles []string `json:"subtitles,omitempty"`
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
@@ -50,6 +51,7 @@ type FileOptions struct {
|
|||||||
ReadHeader bool
|
ReadHeader bool
|
||||||
Token string
|
Token string
|
||||||
Checker rules.Checker
|
Checker rules.Checker
|
||||||
|
Content bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileInfo creates a File object from a path and a given user. This File
|
// NewFileInfo creates a File object from a path and a given user. This File
|
||||||
@@ -60,12 +62,73 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
|
|||||||
return nil, os.ErrPermission
|
return nil, os.ErrPermission
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := opts.Fs.Stat(opts.Path)
|
file, err := stat(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
file := &FileInfo{
|
if opts.Expand {
|
||||||
|
if file.IsDir {
|
||||||
|
if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:govet
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.detectType(opts.Modify, opts.Content, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func stat(opts FileOptions) (*FileInfo, error) {
|
||||||
|
var file *FileInfo
|
||||||
|
|
||||||
|
if lstaterFs, ok := opts.Fs.(afero.Lstater); ok {
|
||||||
|
info, _, err := lstaterFs.LstatIfPossible(opts.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
file = &FileInfo{
|
||||||
|
Fs: opts.Fs,
|
||||||
|
Path: opts.Path,
|
||||||
|
Name: info.Name(),
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
Mode: info.Mode(),
|
||||||
|
IsDir: info.IsDir(),
|
||||||
|
IsSymlink: IsSymlink(info.Mode()),
|
||||||
|
Size: info.Size(),
|
||||||
|
Extension: filepath.Ext(info.Name()),
|
||||||
|
Token: opts.Token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// regular file
|
||||||
|
if file != nil && !file.IsSymlink {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fs doesn't support afero.Lstater interface or the file is a symlink
|
||||||
|
info, err := opts.Fs.Stat(opts.Path)
|
||||||
|
if err != nil {
|
||||||
|
// can't follow symlink
|
||||||
|
if file != nil && file.IsSymlink {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set correct file size in case of symlink
|
||||||
|
if file != nil && file.IsSymlink {
|
||||||
|
file.Size = info.Size()
|
||||||
|
file.IsDir = info.IsDir()
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file = &FileInfo{
|
||||||
Fs: opts.Fs,
|
Fs: opts.Fs,
|
||||||
Path: opts.Path,
|
Path: opts.Path,
|
||||||
Name: info.Name(),
|
Name: info.Name(),
|
||||||
@@ -77,21 +140,7 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
|
|||||||
Token: opts.Token,
|
Token: opts.Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Expand {
|
return file, nil
|
||||||
if file.IsDir {
|
|
||||||
if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:govet
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return file, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = file.detectType(opts.Modify, true, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return file, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checksum checksums a given File for a given User, using a specific
|
// Checksum checksums a given File for a given User, using a specific
|
||||||
@@ -136,6 +185,19 @@ func (i *FileInfo) Checksum(algo string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *FileInfo) RealPath() string {
|
||||||
|
if realPathFs, ok := i.Fs.(interface {
|
||||||
|
RealPath(name string) (fPath string, err error)
|
||||||
|
}); ok {
|
||||||
|
realPath, err := realPathFs.RealPath(i.Path)
|
||||||
|
if err == nil {
|
||||||
|
return realPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.Path
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:goconst
|
//nolint:goconst
|
||||||
//TODO: use constants
|
//TODO: use constants
|
||||||
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||||
@@ -148,12 +210,15 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
|||||||
// of files couldn't be opened: we'd have immediately
|
// of files couldn't be opened: we'd have immediately
|
||||||
// a 500 even though it doesn't matter. So we just log it.
|
// a 500 even though it doesn't matter. So we just log it.
|
||||||
|
|
||||||
var buffer []byte
|
|
||||||
|
|
||||||
mimetype := mime.TypeByExtension(i.Extension)
|
mimetype := mime.TypeByExtension(i.Extension)
|
||||||
if mimetype == "" && readHeader {
|
|
||||||
|
var buffer []byte
|
||||||
|
if readHeader {
|
||||||
buffer = i.readFirstBytes()
|
buffer = i.readFirstBytes()
|
||||||
mimetype = http.DetectContentType(buffer)
|
|
||||||
|
if mimetype == "" {
|
||||||
|
mimetype = http.DetectContentType(buffer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -167,7 +232,10 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
|||||||
case strings.HasPrefix(mimetype, "image"):
|
case strings.HasPrefix(mimetype, "image"):
|
||||||
i.Type = "image"
|
i.Type = "image"
|
||||||
return nil
|
return nil
|
||||||
case (strings.HasPrefix(mimetype, "text") || (len(buffer) > 0 && !isBinary(buffer))) && i.Size <= 10*1024*1024: // 10 MB
|
case strings.HasSuffix(mimetype, "pdf"):
|
||||||
|
i.Type = "pdf"
|
||||||
|
return nil
|
||||||
|
case (strings.HasPrefix(mimetype, "text") || !isBinary(buffer)) && i.Size <= 10*1024*1024: // 10 MB
|
||||||
i.Type = "text"
|
i.Type = "text"
|
||||||
|
|
||||||
if !modify {
|
if !modify {
|
||||||
@@ -200,7 +268,7 @@ func (i *FileInfo) readFirstBytes() []byte {
|
|||||||
}
|
}
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
|
|
||||||
buffer := make([]byte, 512)
|
buffer := make([]byte, 512) //nolint:gomnd
|
||||||
n, err := reader.Read(buffer)
|
n, err := reader.Read(buffer)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
@@ -219,11 +287,17 @@ func (i *FileInfo) detectSubtitles() {
|
|||||||
i.Subtitles = []string{}
|
i.Subtitles = []string{}
|
||||||
ext := filepath.Ext(i.Path)
|
ext := filepath.Ext(i.Path)
|
||||||
|
|
||||||
// TODO: detect multiple languages. Base.Lang.vtt
|
// detect multiple languages. Base*.vtt
|
||||||
|
// TODO: give subtitles descriptive names (lang) and track attributes
|
||||||
fPath := strings.TrimSuffix(i.Path, ext) + ".vtt"
|
parentDir := strings.TrimRight(i.Path, i.Name)
|
||||||
if _, err := i.Fs.Stat(fPath); err == nil {
|
dir, err := afero.ReadDir(i.Fs, parentDir)
|
||||||
i.Subtitles = append(i.Subtitles, fPath)
|
if err == nil {
|
||||||
|
base := strings.TrimSuffix(i.Name, ext)
|
||||||
|
for _, f := range dir {
|
||||||
|
if !f.IsDir() && strings.HasPrefix(f.Name(), base) && strings.HasSuffix(f.Name(), ".vtt") {
|
||||||
|
i.Subtitles = append(i.Subtitles, path.Join(parentDir, f.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,12 +322,16 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSymlink, isInvalidLink := false, false
|
||||||
if IsSymlink(f.Mode()) {
|
if IsSymlink(f.Mode()) {
|
||||||
|
isSymlink = true
|
||||||
// It's a symbolic link. We try to follow it. If it doesn't work,
|
// It's a symbolic link. We try to follow it. If it doesn't work,
|
||||||
// we stay with the link information instead of the target's.
|
// we stay with the link information instead of the target's.
|
||||||
info, err := i.Fs.Stat(fPath)
|
info, err := i.Fs.Stat(fPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
f = info
|
f = info
|
||||||
|
} else {
|
||||||
|
isInvalidLink = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +342,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
|||||||
ModTime: f.ModTime(),
|
ModTime: f.ModTime(),
|
||||||
Mode: f.Mode(),
|
Mode: f.Mode(),
|
||||||
IsDir: f.IsDir(),
|
IsDir: f.IsDir(),
|
||||||
|
IsSymlink: isSymlink,
|
||||||
Extension: filepath.Ext(name),
|
Extension: filepath.Ext(name),
|
||||||
Path: fPath,
|
Path: fPath,
|
||||||
}
|
}
|
||||||
@@ -273,9 +352,13 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
|||||||
} else {
|
} else {
|
||||||
listing.NumFiles++
|
listing.NumFiles++
|
||||||
|
|
||||||
err := file.detectType(true, false, readHeader)
|
if isInvalidLink {
|
||||||
if err != nil {
|
file.Type = "invalid_link"
|
||||||
return err
|
} else {
|
||||||
|
err := file.detectType(true, false, readHeader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ func CopyFile(fs afero.Fs, source, dest string) error {
|
|||||||
|
|
||||||
// Makes the directory needed to create the dst
|
// Makes the directory needed to create the dst
|
||||||
// file.
|
// file.
|
||||||
err = fs.MkdirAll(filepath.Dir(dest), 0666)
|
err = fs.MkdirAll(filepath.Dir(dest), 0666) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the destination file.
|
// Create the destination file.
|
||||||
dst, err := fs.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
dst, err := fs.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//go:build !dev
|
||||||
|
// +build !dev
|
||||||
|
|
||||||
package frontend
|
package frontend
|
||||||
|
|
||||||
import "embed"
|
import "embed"
|
||||||
|
|||||||
15
frontend/assets_dev.go
Normal file
15
frontend/assets_dev.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build dev
|
||||||
|
// +build dev
|
||||||
|
|
||||||
|
package frontend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var assets fs.FS = os.DirFS("frontend")
|
||||||
|
|
||||||
|
func Assets() fs.FS {
|
||||||
|
return assets
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: ["@vue/app"],
|
||||||
'@vue/app'
|
};
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
25867
frontend/package-lock.json
generated
25867
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,35 +5,46 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --no-clean",
|
"build": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --no-clean",
|
||||||
"watch": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --watch --no-clean",
|
"lint": "npx vue-cli-service lint --no-fix --max-warnings=0",
|
||||||
"lint": "vue-cli-service lint --fix"
|
"fix": "npx vue-cli-service lint",
|
||||||
|
"watch": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --watch --no-clean"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ace-builds": "^1.4.7",
|
"ace-builds": "^1.4.7",
|
||||||
"clipboard": "^2.0.4",
|
"clipboard": "^2.0.4",
|
||||||
|
"core-js": "^3.9.1",
|
||||||
|
"css-vars-ponyfill": "^2.4.3",
|
||||||
"js-base64": "^2.5.1",
|
"js-base64": "^2.5.1",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"material-design-icons": "^3.0.1",
|
"material-icons": "^1.10.5",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.29.2",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"noty": "^3.2.0-beta",
|
"noty": "^3.2.0-beta",
|
||||||
|
"pretty-bytes": "^6.0.0",
|
||||||
"qrcode.vue": "^1.7.0",
|
"qrcode.vue": "^1.7.0",
|
||||||
"utif": "^3.1.0",
|
"utif": "^3.1.0",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
|
"vue-async-computed": "^3.9.0",
|
||||||
"vue-i18n": "^8.15.3",
|
"vue-i18n": "^8.15.3",
|
||||||
"vue-lazyload": "^1.3.3",
|
"vue-lazyload": "^1.3.3",
|
||||||
"vue-router": "^3.1.3",
|
"vue-router": "^3.1.3",
|
||||||
|
"vue-simple-progress": "^1.1.1",
|
||||||
"vuex": "^3.1.2",
|
"vuex": "^3.1.2",
|
||||||
"vuex-router-sync": "^5.0.0"
|
"vuex-router-sync": "^5.0.0",
|
||||||
|
"whatwg-fetch": "^3.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.1.2",
|
"@vue/cli-plugin-babel": "^4.1.2",
|
||||||
"@vue/cli-plugin-eslint": "^4.1.1",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"@vue/cli-service": "^4.1.2",
|
"@vue/cli-service": "^4.1.2",
|
||||||
"babel-eslint": "^10.0.3",
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"compression-webpack-plugin": "^6.0.3",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^6.7.2",
|
||||||
"eslint-plugin-vue": "^6.1.2",
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"prettier": "^2.2.1",
|
||||||
"vue-template-compiler": "^2.6.10"
|
"vue-template-compiler": "^2.6.10"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
@@ -43,7 +54,8 @@
|
|||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:vue/essential",
|
"plugin:vue/essential",
|
||||||
"eslint:recommended"
|
"eslint:recommended",
|
||||||
|
"@vue/prettier"
|
||||||
],
|
],
|
||||||
"rules": {},
|
"rules": {},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
@@ -58,6 +70,6 @@
|
|||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
"last 2 versions",
|
"last 2 versions",
|
||||||
"not ie <= 8"
|
"not ie < 11"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<!-- Add to home screen for Android and modern mobile browsers -->
|
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||||
<link rel="manifest" id="manifestPlaceholder" crossorigin="use-credentials">
|
<link rel="manifest" id="manifestPlaceholder" crossorigin="use-credentials">
|
||||||
<meta name="theme-color" content="#2979ff">
|
<meta name="theme-color" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
||||||
|
|
||||||
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
@@ -26,11 +26,11 @@
|
|||||||
|
|
||||||
<!-- Add to home screen for Windows -->
|
<!-- Add to home screen for Windows -->
|
||||||
<meta name="msapplication-TileImage" content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png">
|
<meta name="msapplication-TileImage" content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png">
|
||||||
<meta name="msapplication-TileColor" content="#2979ff">
|
<meta name="msapplication-TileColor" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
||||||
|
|
||||||
<!-- Inject Some Variables and generate the manifest json -->
|
<!-- Inject Some Variables and generate the manifest json -->
|
||||||
<script>
|
<script>
|
||||||
window.FileBrowser = JSON.parse(`[{[ .Json ]}]`);
|
window.FileBrowser = JSON.parse('[{[ .Json ]}]');
|
||||||
|
|
||||||
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
||||||
var dynamicManifest = {
|
var dynamicManifest = {
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"start_url": window.location.origin + window.FileBrowser.BaseURL,
|
"start_url": window.location.origin + window.FileBrowser.BaseURL,
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#ffffff",
|
"background_color": "#ffffff",
|
||||||
"theme_color": "#455a64"
|
"theme_color": window.FileBrowser.Color || "#455a64"
|
||||||
}
|
}
|
||||||
|
|
||||||
const stringManifest = JSON.stringify(dynamicManifest);
|
const stringManifest = JSON.stringify(dynamicManifest);
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
#loading .spinner {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner > div {
|
#loading .spinner > div {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
@@ -97,12 +97,12 @@
|
|||||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner .bounce1 {
|
#loading .spinner .bounce1 {
|
||||||
-webkit-animation-delay: -0.32s;
|
-webkit-animation-delay: -0.32s;
|
||||||
animation-delay: -0.32s;
|
animation-delay: -0.32s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner .bounce2 {
|
#loading .spinner .bounce2 {
|
||||||
-webkit-animation-delay: -0.16s;
|
-webkit-animation-delay: -0.16s;
|
||||||
animation-delay: -0.16s;
|
animation-delay: -0.16s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ body {
|
|||||||
#loading {
|
#loading {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
}
|
}
|
||||||
#loading .spinner div, #previewer .loading .spinner div {
|
#loading .spinner div, main .spinner div {
|
||||||
background: var(--icon);
|
background: var(--icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,19 +5,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
__webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: "app",
|
||||||
mounted () {
|
mounted() {
|
||||||
const loading = document.getElementById('loading')
|
const loading = document.getElementById("loading");
|
||||||
loading.classList.add('done')
|
loading.classList.add("done");
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
loading.parentNode.removeChild(loading)
|
loading.parentNode.removeChild(loading);
|
||||||
}, 200)
|
}, 200);
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import './css/styles.css';
|
@import "./css/styles.css";
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { removePrefix } from './utils'
|
import { removePrefix } from "./utils";
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from "@/utils/constants";
|
||||||
import store from '@/store'
|
import store from "@/store";
|
||||||
|
|
||||||
const ssl = (window.location.protocol === 'https:')
|
const ssl = window.location.protocol === "https:";
|
||||||
const protocol = (ssl ? 'wss:' : 'ws:')
|
const protocol = ssl ? "wss:" : "ws:";
|
||||||
|
|
||||||
export default function command(url, command, onmessage, onclose) {
|
export default function command(url, command, onmessage, onclose) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`
|
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`;
|
||||||
|
|
||||||
let conn = new window.WebSocket(url)
|
let conn = new window.WebSocket(url);
|
||||||
conn.onopen = () => conn.send(command)
|
conn.onopen = () => conn.send(command);
|
||||||
conn.onmessage = onmessage
|
conn.onmessage = onmessage;
|
||||||
conn.onclose = onclose
|
conn.onclose = onclose;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,147 +1,186 @@
|
|||||||
import { fetchURL, removePrefix } from './utils'
|
import { createURL, fetchURL, removePrefix } from "./utils";
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from "@/utils/constants";
|
||||||
import store from '@/store'
|
import store from "@/store";
|
||||||
|
|
||||||
export async function fetch (url) {
|
export async function fetch(url) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
|
|
||||||
const res = await fetchURL(`/api/resources${url}`, {})
|
const res = await fetchURL(`/api/resources${url}`, {});
|
||||||
|
|
||||||
if (res.status === 200) {
|
let data = await res.json();
|
||||||
let data = await res.json()
|
data.url = `/files${url}`;
|
||||||
data.url = `/files${url}`
|
|
||||||
|
|
||||||
if (data.isDir) {
|
if (data.isDir) {
|
||||||
if (!data.url.endsWith('/')) data.url += '/'
|
if (!data.url.endsWith("/")) data.url += "/";
|
||||||
data.items = data.items.map((item, index) => {
|
data.items = data.items.map((item, index) => {
|
||||||
item.index = index
|
item.index = index;
|
||||||
item.url = `${data.url}${encodeURIComponent(item.name)}`
|
item.url = `${data.url}${encodeURIComponent(item.name)}`;
|
||||||
|
|
||||||
if (item.isDir) {
|
if (item.isDir) {
|
||||||
item.url += '/'
|
item.url += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
return item
|
return item;
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
} else {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resourceAction (url, method, content) {
|
async function resourceAction(url, method, content) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
|
|
||||||
let opts = { method }
|
let opts = { method };
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
opts.body = content
|
opts.body = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetchURL(`/api/resources${url}`, opts)
|
const res = await fetchURL(`/api/resources${url}`, opts);
|
||||||
|
|
||||||
if (res.status !== 200) {
|
return res;
|
||||||
throw new Error(await res.text())
|
|
||||||
} else {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove (url) {
|
export async function remove(url) {
|
||||||
return resourceAction(url, 'DELETE')
|
return resourceAction(url, "DELETE");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function put (url, content = '') {
|
export async function put(url, content = "") {
|
||||||
return resourceAction(url, 'PUT', content)
|
return resourceAction(url, "PUT", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function download (format, ...files) {
|
export function download(format, ...files) {
|
||||||
let url = `${baseURL}/api/raw`
|
let url = `${baseURL}/api/raw`;
|
||||||
|
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
url += removePrefix(files[0]) + '?'
|
url += removePrefix(files[0]) + "?";
|
||||||
} else {
|
} else {
|
||||||
let arg = ''
|
let arg = "";
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
arg += removePrefix(file) + ','
|
arg += removePrefix(file) + ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
arg = arg.substring(0, arg.length - 1)
|
arg = arg.substring(0, arg.length - 1);
|
||||||
arg = encodeURIComponent(arg)
|
arg = encodeURIComponent(arg);
|
||||||
url += `/?files=${arg}&`
|
url += `/?files=${arg}&`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format) {
|
if (format) {
|
||||||
url += `algo=${format}&`
|
url += `algo=${format}&`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.state.jwt){
|
if (store.state.jwt) {
|
||||||
url += `auth=${store.state.jwt}&`
|
url += `auth=${store.state.jwt}&`;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.open(url)
|
window.open(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function post (url, content = '', overwrite = false, onupload) {
|
export async function post(url, content = "", overwrite = false, onupload) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
|
|
||||||
let bufferContent
|
let bufferContent;
|
||||||
if (content instanceof Blob && !['http:', 'https:'].includes(window.location.protocol)) {
|
if (
|
||||||
bufferContent = await new Response(content).arrayBuffer()
|
content instanceof Blob &&
|
||||||
|
!["http:", "https:"].includes(window.location.protocol)
|
||||||
|
) {
|
||||||
|
bufferContent = await new Response(content).arrayBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let request = new XMLHttpRequest()
|
let request = new XMLHttpRequest();
|
||||||
request.open('POST', `${baseURL}/api/resources${url}?override=${overwrite}`, true)
|
request.open(
|
||||||
request.setRequestHeader('X-Auth', store.state.jwt)
|
"POST",
|
||||||
|
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
request.setRequestHeader("X-Auth", store.state.jwt);
|
||||||
|
|
||||||
if (typeof onupload === 'function') {
|
if (typeof onupload === "function") {
|
||||||
request.upload.onprogress = onupload
|
request.upload.onprogress = onupload;
|
||||||
}
|
}
|
||||||
|
|
||||||
request.onload = () => {
|
request.onload = () => {
|
||||||
if (request.status === 200) {
|
if (request.status === 200) {
|
||||||
resolve(request.responseText)
|
resolve(request.responseText);
|
||||||
} else if (request.status === 409) {
|
} else if (request.status === 409) {
|
||||||
reject(request.status)
|
reject(request.status);
|
||||||
} else {
|
} else {
|
||||||
reject(request.responseText)
|
reject(request.responseText);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
request.onerror = (error) => {
|
request.onerror = () => {
|
||||||
reject(error)
|
reject(new Error("001 Connection aborted"));
|
||||||
}
|
};
|
||||||
|
|
||||||
request.send(bufferContent || content)
|
request.send(bufferContent || content);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveCopy (items, copy = false, overwrite = false, rename = false) {
|
function moveCopy(items, copy = false, overwrite = false, rename = false) {
|
||||||
let promises = []
|
let promises = [];
|
||||||
|
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
const from = item.from
|
const from = item.from;
|
||||||
const to = encodeURIComponent(removePrefix(item.to))
|
const to = encodeURIComponent(removePrefix(item.to));
|
||||||
const url = `${from}?action=${copy ? 'copy' : 'rename'}&destination=${to}&override=${overwrite}&rename=${rename}`
|
const url = `${from}?action=${
|
||||||
promises.push(resourceAction(url, 'PATCH'))
|
copy ? "copy" : "rename"
|
||||||
|
}&destination=${to}&override=${overwrite}&rename=${rename}`;
|
||||||
|
promises.push(resourceAction(url, "PATCH"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function move (items, overwrite = false, rename = false) {
|
export function move(items, overwrite = false, rename = false) {
|
||||||
return moveCopy(items, false, overwrite, rename)
|
return moveCopy(items, false, overwrite, rename);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copy (items, overwrite = false, rename = false) {
|
export function copy(items, overwrite = false, rename = false) {
|
||||||
return moveCopy(items, true, overwrite, rename)
|
return moveCopy(items, true, overwrite, rename);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checksum (url, algo) {
|
export async function checksum(url, algo) {
|
||||||
const data = await resourceAction(`${url}?checksum=${algo}`, 'GET')
|
const data = await resourceAction(`${url}?checksum=${algo}`, "GET");
|
||||||
return (await data.json()).checksums[algo]
|
return (await data.json()).checksums[algo];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDownloadURL(file, inline) {
|
||||||
|
const params = {
|
||||||
|
...(inline && { inline: "true" }),
|
||||||
|
};
|
||||||
|
|
||||||
|
return createURL("api/raw" + file.path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPreviewURL(file, size) {
|
||||||
|
const params = {
|
||||||
|
inline: "true",
|
||||||
|
key: Date.parse(file.modified),
|
||||||
|
};
|
||||||
|
|
||||||
|
return createURL("api/preview/" + size + file.path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubtitlesURL(file) {
|
||||||
|
const params = {
|
||||||
|
inline: "true",
|
||||||
|
};
|
||||||
|
|
||||||
|
const subtitles = [];
|
||||||
|
for (const sub of file.subtitles) {
|
||||||
|
subtitles.push(createURL("api/raw" + sub, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtitles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function usage(url) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
const res = await fetchURL(`/api/usage${url}`, {});
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
import * as files from './files'
|
import * as files from "./files";
|
||||||
import * as share from './share'
|
import * as share from "./share";
|
||||||
import * as users from './users'
|
import * as users from "./users";
|
||||||
import * as settings from './settings'
|
import * as settings from "./settings";
|
||||||
import * as pub from './pub'
|
import * as pub from "./pub";
|
||||||
import search from './search'
|
import search from "./search";
|
||||||
import commands from './commands'
|
import commands from "./commands";
|
||||||
|
|
||||||
export {
|
export { files, share, users, settings, pub, commands, search };
|
||||||
files,
|
|
||||||
share,
|
|
||||||
users,
|
|
||||||
settings,
|
|
||||||
pub,
|
|
||||||
commands,
|
|
||||||
search
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,61 +1,70 @@
|
|||||||
import { fetchURL, removePrefix } from './utils'
|
import { fetchURL, removePrefix, createURL } from "./utils";
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from "@/utils/constants";
|
||||||
|
|
||||||
export async function fetch (url, password = "") {
|
export async function fetch(url, password = "") {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
|
|
||||||
const res = await fetchURL(`/api/public/share${url}`, {
|
const res = await fetchURL(
|
||||||
headers: {'X-SHARE-PASSWORD': password},
|
`/api/public/share${url}`,
|
||||||
})
|
{
|
||||||
|
headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) },
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (res.status === 200) {
|
let data = await res.json();
|
||||||
let data = await res.json()
|
data.url = `/share${url}`;
|
||||||
data.url = `/share${url}`
|
|
||||||
|
|
||||||
if (data.isDir) {
|
if (data.isDir) {
|
||||||
if (!data.url.endsWith('/')) data.url += '/'
|
if (!data.url.endsWith("/")) data.url += "/";
|
||||||
data.items = data.items.map((item, index) => {
|
data.items = data.items.map((item, index) => {
|
||||||
item.index = index
|
item.index = index;
|
||||||
item.url = `${data.url}${encodeURIComponent(item.name)}`
|
item.url = `${data.url}${encodeURIComponent(item.name)}`;
|
||||||
|
|
||||||
if (item.isDir) {
|
if (item.isDir) {
|
||||||
item.url += '/'
|
item.url += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
return item
|
return item;
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
} else {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function download(format, hash, token, ...files) {
|
export function download(format, hash, token, ...files) {
|
||||||
let url = `${baseURL}/api/public/dl/${hash}`
|
let url = `${baseURL}/api/public/dl/${hash}`;
|
||||||
|
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
url += encodeURIComponent(files[0]) + '?'
|
url += encodeURIComponent(files[0]) + "?";
|
||||||
} else {
|
} else {
|
||||||
let arg = ''
|
let arg = "";
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
arg += encodeURIComponent(file) + ','
|
arg += encodeURIComponent(file) + ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
arg = arg.substring(0, arg.length - 1)
|
arg = arg.substring(0, arg.length - 1);
|
||||||
arg = encodeURIComponent(arg)
|
arg = encodeURIComponent(arg);
|
||||||
url += `/?files=${arg}&`
|
url += `/?files=${arg}&`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format) {
|
if (format) {
|
||||||
url += `algo=${format}&`
|
url += `algo=${format}&`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
url += `token=${token}&`
|
url += `token=${token}&`;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.open(url)
|
window.open(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDownloadURL(share, inline = false) {
|
||||||
|
const params = {
|
||||||
|
...(inline && { inline: "true" }),
|
||||||
|
...(share.token && { token: share.token }),
|
||||||
|
};
|
||||||
|
|
||||||
|
return createURL("api/public/dl/" + share.hash + share.path, params, false);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
import { fetchURL, removePrefix } from './utils'
|
import { fetchURL, removePrefix } from "./utils";
|
||||||
import url from '../utils/url'
|
import url from "../utils/url";
|
||||||
|
|
||||||
export default async function search (base, query) {
|
export default async function search(base, query) {
|
||||||
base = removePrefix(base)
|
base = removePrefix(base);
|
||||||
query = encodeURIComponent(query)
|
query = encodeURIComponent(query);
|
||||||
|
|
||||||
if (!base.endsWith('/')) {
|
if (!base.endsWith("/")) {
|
||||||
base += '/'
|
base += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await fetchURL(`/api/search${base}?query=${query}`, {})
|
let res = await fetchURL(`/api/search${base}?query=${query}`, {});
|
||||||
|
|
||||||
if (res.status === 200) {
|
let data = await res.json();
|
||||||
let data = await res.json()
|
|
||||||
|
|
||||||
data = data.map((item) => {
|
data = data.map((item) => {
|
||||||
item.url = `/files${base}` + url.encodePath(item.path)
|
item.url = `/files${base}` + url.encodePath(item.path);
|
||||||
|
|
||||||
if (item.dir) {
|
if (item.dir) {
|
||||||
item.url += '/'
|
item.url += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
return item
|
return item;
|
||||||
})
|
});
|
||||||
|
|
||||||
return data
|
return data;
|
||||||
} else {
|
}
|
||||||
throw Error(res.status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import { fetchURL, fetchJSON } from './utils'
|
import { fetchURL, fetchJSON } from "./utils";
|
||||||
|
|
||||||
export function get () {
|
export function get() {
|
||||||
return fetchJSON(`/api/settings`, {})
|
return fetchJSON(`/api/settings`, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update (settings) {
|
export async function update(settings) {
|
||||||
const res = await fetchURL(`/api/settings`, {
|
await fetchURL(`/api/settings`, {
|
||||||
method: 'PUT',
|
method: "PUT",
|
||||||
body: JSON.stringify(settings)
|
body: JSON.stringify(settings),
|
||||||
})
|
});
|
||||||
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
import { fetchURL, fetchJSON, removePrefix } from './utils'
|
import { fetchURL, fetchJSON, removePrefix, createURL } from "./utils";
|
||||||
|
|
||||||
export async function list() {
|
export async function list() {
|
||||||
return fetchJSON('/api/shares')
|
return fetchJSON("/api/shares");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get(url) {
|
export async function get(url) {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
return fetchJSON(`/api/share${url}`)
|
return fetchJSON(`/api/share${url}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove(hash) {
|
export async function remove(hash) {
|
||||||
const res = await fetchURL(`/api/share/${hash}`, {
|
await fetchURL(`/api/share/${hash}`, {
|
||||||
method: 'DELETE'
|
method: "DELETE",
|
||||||
})
|
});
|
||||||
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(url, password = '', expires = '', unit = 'hours') {
|
export async function create(url, password = "", expires = "", unit = "hours") {
|
||||||
url = removePrefix(url)
|
url = removePrefix(url);
|
||||||
url = `/api/share${url}`
|
url = `/api/share${url}`;
|
||||||
if (expires !== '') {
|
if (expires !== "") {
|
||||||
url += `?expires=${expires}&unit=${unit}`
|
url += `?expires=${expires}&unit=${unit}`;
|
||||||
}
|
}
|
||||||
let body = '{}';
|
let body = "{}";
|
||||||
if (password != '' || expires !== '' || unit !== 'hours') {
|
if (password != "" || expires !== "" || unit !== "hours") {
|
||||||
body = JSON.stringify({password: password, expires: expires, unit: unit})
|
body = JSON.stringify({ password: password, expires: expires, unit: unit });
|
||||||
}
|
}
|
||||||
return fetchJSON(url, {
|
return fetchJSON(url, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: body,
|
body: body,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShareURL(share) {
|
||||||
|
return createURL("share/" + share.hash, {}, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,41 @@
|
|||||||
import { fetchURL, fetchJSON } from './utils'
|
import { fetchURL, fetchJSON } from "./utils";
|
||||||
|
|
||||||
export async function getAll () {
|
export async function getAll() {
|
||||||
return fetchJSON(`/api/users`, {})
|
return fetchJSON(`/api/users`, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get (id) {
|
export async function get(id) {
|
||||||
return fetchJSON(`/api/users/${id}`, {})
|
return fetchJSON(`/api/users/${id}`, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create (user) {
|
export async function create(user) {
|
||||||
const res = await fetchURL(`/api/users`, {
|
const res = await fetchURL(`/api/users`, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
what: 'user',
|
what: "user",
|
||||||
which: [],
|
which: [],
|
||||||
data: user
|
data: user,
|
||||||
})
|
}),
|
||||||
})
|
});
|
||||||
|
|
||||||
if (res.status === 201) {
|
if (res.status === 201) {
|
||||||
return res.headers.get('Location')
|
return res.headers.get("Location");
|
||||||
} else {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update (user, which = ['all']) {
|
export async function update(user, which = ["all"]) {
|
||||||
const res = await fetchURL(`/api/users/${user.id}`, {
|
await fetchURL(`/api/users/${user.id}`, {
|
||||||
method: 'PUT',
|
method: "PUT",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
what: 'user',
|
what: "user",
|
||||||
which: which,
|
which: which,
|
||||||
data: user
|
data: user,
|
||||||
})
|
}),
|
||||||
})
|
});
|
||||||
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove (id) {
|
export async function remove(id) {
|
||||||
const res = await fetchURL(`/api/users/${id}`, {
|
await fetchURL(`/api/users/${id}`, {
|
||||||
method: 'DELETE'
|
method: "DELETE",
|
||||||
})
|
});
|
||||||
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error(res.status)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,81 @@
|
|||||||
import store from '@/store'
|
import store from "@/store";
|
||||||
import { renew } from '@/utils/auth'
|
import { renew, logout } from "@/utils/auth";
|
||||||
import { baseURL } from '@/utils/constants'
|
import { baseURL } from "@/utils/constants";
|
||||||
|
import { encodePath } from "@/utils/url";
|
||||||
|
|
||||||
export async function fetchURL (url, opts) {
|
export async function fetchURL(url, opts, auth = true) {
|
||||||
opts = opts || {}
|
opts = opts || {};
|
||||||
opts.headers = opts.headers || {}
|
opts.headers = opts.headers || {};
|
||||||
|
|
||||||
let { headers, ...rest } = opts
|
let { headers, ...rest } = opts;
|
||||||
|
|
||||||
const res = await fetch(`${baseURL}${url}`, {
|
let res;
|
||||||
headers: {
|
try {
|
||||||
'X-Auth': store.state.jwt,
|
res = await fetch(`${baseURL}${url}`, {
|
||||||
...headers
|
headers: {
|
||||||
},
|
"X-Auth": store.state.jwt,
|
||||||
...rest
|
...headers,
|
||||||
})
|
},
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
const error = new Error("000 No connection");
|
||||||
|
error.status = 0;
|
||||||
|
|
||||||
if (res.headers.get('X-Renew-Token') === 'true') {
|
throw error;
|
||||||
await renew(store.state.jwt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
if (auth && res.headers.get("X-Renew-Token") === "true") {
|
||||||
|
await renew(store.state.jwt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.status < 200 || res.status > 299) {
|
||||||
|
const error = new Error(await res.text());
|
||||||
|
error.status = res.status;
|
||||||
|
|
||||||
|
if (auth && res.status == 401) {
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchJSON (url, opts) {
|
export async function fetchJSON(url, opts) {
|
||||||
const res = await fetchURL(url, opts)
|
const res = await fetchURL(url, opts);
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
return res.json()
|
return res.json();
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.status)
|
throw new Error(res.status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removePrefix (url) {
|
export function removePrefix(url) {
|
||||||
url = url.split('/').splice(2).join('/')
|
url = url.split("/").splice(2).join("/");
|
||||||
|
|
||||||
if (url === '') url = '/'
|
if (url === "") url = "/";
|
||||||
if (url[0] !== '/') url = '/' + url
|
if (url[0] !== "/") url = "/" + url;
|
||||||
return url
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createURL(endpoint, params = {}, auth = true) {
|
||||||
|
let prefix = baseURL;
|
||||||
|
if (!prefix.endsWith("/")) {
|
||||||
|
prefix = prefix + "/";
|
||||||
|
}
|
||||||
|
const url = new URL(prefix + encodePath(endpoint), origin);
|
||||||
|
|
||||||
|
const searchParams = {
|
||||||
|
...(auth && { auth: store.state.jwt }),
|
||||||
|
...params,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const key in searchParams) {
|
||||||
|
url.searchParams.set(key, searchParams[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|||||||
BIN
frontend/src/assets/fonts/roboto/bold-cyrillic-ext.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-cyrillic-ext.woff2
Executable file
Binary file not shown.
BIN
frontend/src/assets/fonts/roboto/bold-cyrillic.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-cyrillic.woff2
Executable file
Binary file not shown.
BIN
frontend/src/assets/fonts/roboto/bold-greek-ext.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-greek-ext.woff2
Executable file
Binary file not shown.
BIN
frontend/src/assets/fonts/roboto/bold-greek.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-greek.woff2
Executable file
Binary file not shown.
BIN
frontend/src/assets/fonts/roboto/bold-latin-ext.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-latin-ext.woff2
Executable file
Binary file not shown.
BIN
frontend/src/assets/fonts/roboto/bold-latin.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-latin.woff2
Executable file
Binary file not shown.
BIN
frontend/src/assets/fonts/roboto/bold-vietnamese.woff2
Executable file
BIN
frontend/src/assets/fonts/roboto/bold-vietnamese.woff2
Executable file
Binary file not shown.
@@ -1,11 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="breadcrumbs">
|
<div class="breadcrumbs">
|
||||||
<component :is="element" :to="base || ''" :aria-label="$t('files.home')" :title="$t('files.home')">
|
<component
|
||||||
|
:is="element"
|
||||||
|
:to="base || ''"
|
||||||
|
:aria-label="$t('files.home')"
|
||||||
|
:title="$t('files.home')"
|
||||||
|
>
|
||||||
<i class="material-icons">home</i>
|
<i class="material-icons">home</i>
|
||||||
</component>
|
</component>
|
||||||
|
|
||||||
<span v-for="(link, index) in items" :key="index">
|
<span v-for="(link, index) in items" :key="index">
|
||||||
<span class="chevron"><i class="material-icons">keyboard_arrow_right</i></span>
|
<span class="chevron"
|
||||||
|
><i class="material-icons">keyboard_arrow_right</i></span
|
||||||
|
>
|
||||||
<component :is="element" :to="link.url">{{ link.name }}</component>
|
<component :is="element" :to="link.url">{{ link.name }}</component>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -13,55 +20,56 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'breadcrumbs',
|
name: "breadcrumbs",
|
||||||
props: [
|
props: ["base", "noLink"],
|
||||||
'base',
|
|
||||||
'noLink'
|
|
||||||
],
|
|
||||||
computed: {
|
computed: {
|
||||||
items () {
|
items() {
|
||||||
const relativePath = this.$route.path.replace(this.base, '')
|
const relativePath = this.$route.path.replace(this.base, "");
|
||||||
let parts = relativePath.split('/')
|
let parts = relativePath.split("/");
|
||||||
|
|
||||||
if (parts[0] === '') {
|
if (parts[0] === "") {
|
||||||
parts.shift()
|
parts.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parts[parts.length - 1] === '') {
|
if (parts[parts.length - 1] === "") {
|
||||||
parts.pop()
|
parts.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let breadcrumbs = []
|
let breadcrumbs = [];
|
||||||
|
|
||||||
for (let i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
breadcrumbs.push({ name: decodeURIComponent(parts[i]), url: this.base + '/' + parts[i] + '/' })
|
breadcrumbs.push({
|
||||||
} else {
|
name: decodeURIComponent(parts[i]),
|
||||||
breadcrumbs.push({ name: decodeURIComponent(parts[i]), url: breadcrumbs[i - 1].url + parts[i] + '/' })
|
url: this.base + "/" + parts[i] + "/",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
breadcrumbs.push({
|
||||||
|
name: decodeURIComponent(parts[i]),
|
||||||
|
url: breadcrumbs[i - 1].url + parts[i] + "/",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (breadcrumbs.length > 3) {
|
if (breadcrumbs.length > 3) {
|
||||||
while (breadcrumbs.length !== 4) {
|
while (breadcrumbs.length !== 4) {
|
||||||
breadcrumbs.shift()
|
breadcrumbs.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
breadcrumbs[0].name = '...'
|
breadcrumbs[0].name = "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
return breadcrumbs
|
return breadcrumbs;
|
||||||
},
|
},
|
||||||
element () {
|
element() {
|
||||||
if (this.noLink !== undefined) {
|
if (this.noLink !== undefined) {
|
||||||
return 'span'
|
return "span";
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'router-link'
|
return "router-link";
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="search" @click="open" v-bind:class="{ active , ongoing }">
|
<div id="search" @click="open" v-bind:class="{ active, ongoing }">
|
||||||
<div id="input">
|
<div id="input">
|
||||||
<button
|
<button
|
||||||
v-if="active"
|
v-if="active"
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
v-model.trim="value"
|
v-model.trim="value"
|
||||||
:aria-label="$t('search.search')"
|
:aria-label="$t('search.search')"
|
||||||
:placeholder="$t('search.search')"
|
:placeholder="$t('search.search')"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="result" ref="result">
|
<div id="result" ref="result">
|
||||||
@@ -30,25 +30,25 @@
|
|||||||
|
|
||||||
<template v-if="value.length === 0">
|
<template v-if="value.length === 0">
|
||||||
<div class="boxes">
|
<div class="boxes">
|
||||||
<h3>{{ $t('search.types') }}</h3>
|
<h3>{{ $t("search.types") }}</h3>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
v-for="(v,k) in boxes"
|
v-for="(v, k) in boxes"
|
||||||
:key="k"
|
:key="k"
|
||||||
role="button"
|
role="button"
|
||||||
@click="init('type:'+k)"
|
@click="init('type:' + k)"
|
||||||
:aria-label="$t('search.'+v.label)"
|
:aria-label="$t('search.' + v.label)"
|
||||||
>
|
>
|
||||||
<i class="material-icons">{{v.icon}}</i>
|
<i class="material-icons">{{ v.icon }}</i>
|
||||||
<p>{{ $t('search.'+v.label) }}</p>
|
<p>{{ $t("search." + v.label) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<ul v-show="results.length > 0">
|
<ul v-show="results.length > 0">
|
||||||
<li v-for="(s,k) in filteredResults" :key="k">
|
<li v-for="(s, k) in filteredResults" :key="k">
|
||||||
<router-link @click.native="close" :to="s.url">
|
<router-link @click.native="close" :to="s.url">
|
||||||
<i v-if="s.dir" class="material-icons">folder</i>
|
<i v-if="s.dir" class="material-icons">folder</i>
|
||||||
<i v-else class="material-icons">insert_drive_file</i>
|
<i v-else class="material-icons">insert_drive_file</i>
|
||||||
@@ -65,20 +65,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters, mapMutations } from "vuex"
|
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||||
import url from "@/utils/url"
|
import url from "@/utils/url";
|
||||||
import { search } from "@/api"
|
import { search } from "@/api";
|
||||||
|
|
||||||
var boxes = {
|
var boxes = {
|
||||||
image: { label: "images", icon: "insert_photo" },
|
image: { label: "images", icon: "insert_photo" },
|
||||||
audio: { label: "music", icon: "volume_up" },
|
audio: { label: "music", icon: "volume_up" },
|
||||||
video: { label: "video", icon: "movie" },
|
video: { label: "video", icon: "movie" },
|
||||||
pdf: { label: "pdf", icon: "picture_as_pdf" }
|
pdf: { label: "pdf", icon: "picture_as_pdf" },
|
||||||
}
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "search",
|
name: "search",
|
||||||
data: function() {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
value: "",
|
value: "",
|
||||||
active: false,
|
active: false,
|
||||||
@@ -86,111 +86,116 @@ export default {
|
|||||||
results: [],
|
results: [],
|
||||||
reload: false,
|
reload: false,
|
||||||
resultsCount: 50,
|
resultsCount: 50,
|
||||||
scrollable: null
|
scrollable: null,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
show (val, old) {
|
show(val, old) {
|
||||||
this.active = val === "search"
|
this.active = val === "search";
|
||||||
|
|
||||||
if (old === "search" && !this.active) {
|
if (old === "search" && !this.active) {
|
||||||
if (this.reload) {
|
if (this.reload) {
|
||||||
this.setReload(true)
|
this.setReload(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.style.overflow = "auto"
|
document.body.style.overflow = "auto";
|
||||||
this.reset()
|
this.reset();
|
||||||
this.value = ''
|
this.value = "";
|
||||||
this.active = false
|
this.active = false;
|
||||||
this.$refs.input.blur()
|
this.$refs.input.blur();
|
||||||
} else if (this.active) {
|
} else if (this.active) {
|
||||||
this.reload = false
|
this.reload = false;
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus();
|
||||||
document.body.style.overflow = "hidden"
|
document.body.style.overflow = "hidden";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
value () {
|
value() {
|
||||||
if (this.results.length) {
|
if (this.results.length) {
|
||||||
this.reset()
|
this.reset();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["user", "show"]),
|
...mapState(["user", "show"]),
|
||||||
...mapGetters(["isListing"]),
|
...mapGetters(["isListing"]),
|
||||||
boxes() {
|
boxes() {
|
||||||
return boxes
|
return boxes;
|
||||||
},
|
},
|
||||||
isEmpty() {
|
isEmpty() {
|
||||||
return this.results.length === 0
|
return this.results.length === 0;
|
||||||
},
|
},
|
||||||
text() {
|
text() {
|
||||||
if (this.ongoing) {
|
if (this.ongoing) {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.value === '' ? this.$t("search.typeToSearch") : this.$t("search.pressToSearch")
|
return this.value === ""
|
||||||
|
? this.$t("search.typeToSearch")
|
||||||
|
: this.$t("search.pressToSearch");
|
||||||
|
},
|
||||||
|
filteredResults() {
|
||||||
|
return this.results.slice(0, this.resultsCount);
|
||||||
},
|
},
|
||||||
filteredResults () {
|
|
||||||
return this.results.slice(0, this.resultsCount)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$refs.result.addEventListener('scroll', event => {
|
this.$refs.result.addEventListener("scroll", (event) => {
|
||||||
if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight - 100) {
|
if (
|
||||||
this.resultsCount += 50
|
event.target.offsetHeight + event.target.scrollTop >=
|
||||||
|
event.target.scrollHeight - 100
|
||||||
|
) {
|
||||||
|
this.resultsCount += 50;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(["showHover", "closeHovers", "setReload"]),
|
...mapMutations(["showHover", "closeHovers", "setReload"]),
|
||||||
open() {
|
open() {
|
||||||
this.showHover("search")
|
this.showHover("search");
|
||||||
},
|
},
|
||||||
close(event) {
|
close(event) {
|
||||||
event.stopPropagation()
|
event.stopPropagation();
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
this.closeHovers()
|
this.closeHovers();
|
||||||
},
|
},
|
||||||
keyup(event) {
|
keyup(event) {
|
||||||
if (event.keyCode === 27) {
|
if (event.keyCode === 27) {
|
||||||
this.close(event)
|
this.close(event);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.results.length = 0
|
this.results.length = 0;
|
||||||
},
|
},
|
||||||
init (string) {
|
init(string) {
|
||||||
this.value = `${string} `
|
this.value = `${string} `;
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus();
|
||||||
},
|
},
|
||||||
reset () {
|
reset() {
|
||||||
this.ongoing = false
|
this.ongoing = false;
|
||||||
this.resultsCount = 50
|
this.resultsCount = 50;
|
||||||
this.results = []
|
this.results = [];
|
||||||
},
|
},
|
||||||
async submit(event) {
|
async submit(event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.value === '') {
|
if (this.value === "") {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = this.$route.path
|
let path = this.$route.path;
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
path = url.removeLastDir(path) + "/"
|
path = url.removeLastDir(path) + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ongoing = true
|
this.ongoing = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.results = await search(path, this.value)
|
this.results = await search(path, this.value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$showError(error)
|
this.$showError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ongoing = false
|
this.ongoing = false;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div @click="focus" class="shell" ref="scrollable" :class="{ ['shell--hidden']: !showShell}">
|
<div
|
||||||
<div v-for="(c, index) in content" :key="index" class="shell__result" >
|
@click="focus"
|
||||||
<div class="shell__prompt"><i class="material-icons">chevron_right</i></div>
|
class="shell"
|
||||||
|
ref="scrollable"
|
||||||
|
:class="{ ['shell--hidden']: !showShell }"
|
||||||
|
>
|
||||||
|
<div v-for="(c, index) in content" :key="index" class="shell__result">
|
||||||
|
<div class="shell__prompt">
|
||||||
|
<i class="material-icons">chevron_right</i>
|
||||||
|
</div>
|
||||||
<pre class="shell__text">{{ c.text }}</pre>
|
<pre class="shell__text">{{ c.text }}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shell__result" :class="{ 'shell__result--hidden': !canInput }" >
|
<div class="shell__result" :class="{ 'shell__result--hidden': !canInput }">
|
||||||
<div class="shell__prompt"><i class="material-icons">chevron_right</i></div>
|
<div class="shell__prompt">
|
||||||
|
<i class="material-icons">chevron_right</i>
|
||||||
|
</div>
|
||||||
<pre
|
<pre
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
ref="input"
|
ref="input"
|
||||||
@@ -14,102 +23,103 @@
|
|||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
@keydown.prevent.38="historyUp"
|
@keydown.prevent.38="historyUp"
|
||||||
@keydown.prevent.40="historyDown"
|
@keydown.prevent.40="historyDown"
|
||||||
@keypress.prevent.enter="submit" />
|
@keypress.prevent.enter="submit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations, mapState, mapGetters } from 'vuex'
|
import { mapMutations, mapState, mapGetters } from "vuex";
|
||||||
import { commands } from '@/api'
|
import { commands } from "@/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'shell',
|
name: "shell",
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user', 'showShell' ]),
|
...mapState(["user", "showShell"]),
|
||||||
...mapGetters([ 'isFiles', 'isLogged' ]),
|
...mapGetters(["isFiles", "isLogged"]),
|
||||||
path: function () {
|
path: function () {
|
||||||
if (this.isFiles) {
|
if (this.isFiles) {
|
||||||
return this.$route.path
|
return this.$route.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ''
|
return "";
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
content: [],
|
content: [],
|
||||||
history: [],
|
history: [],
|
||||||
historyPos: 0,
|
historyPos: 0,
|
||||||
canInput: true
|
canInput: true,
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([ 'toggleShell' ]),
|
...mapMutations(["toggleShell"]),
|
||||||
scroll: function () {
|
scroll: function () {
|
||||||
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight
|
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight;
|
||||||
},
|
},
|
||||||
focus: function () {
|
focus: function () {
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus();
|
||||||
},
|
},
|
||||||
historyUp () {
|
historyUp() {
|
||||||
if (this.historyPos > 0) {
|
if (this.historyPos > 0) {
|
||||||
this.$refs.input.innerText = this.history[--this.historyPos]
|
this.$refs.input.innerText = this.history[--this.historyPos];
|
||||||
this.focus()
|
this.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
historyDown () {
|
historyDown() {
|
||||||
if (this.historyPos >= 0 && this.historyPos < this.history.length - 1) {
|
if (this.historyPos >= 0 && this.historyPos < this.history.length - 1) {
|
||||||
this.$refs.input.innerText = this.history[++this.historyPos]
|
this.$refs.input.innerText = this.history[++this.historyPos];
|
||||||
this.focus()
|
this.focus();
|
||||||
} else {
|
} else {
|
||||||
this.historyPos = this.history.length
|
this.historyPos = this.history.length;
|
||||||
this.$refs.input.innerText = ''
|
this.$refs.input.innerText = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
submit: function (event) {
|
submit: function (event) {
|
||||||
const cmd = event.target.innerText.trim()
|
const cmd = event.target.innerText.trim();
|
||||||
|
|
||||||
if (cmd === '') {
|
if (cmd === "") {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd === 'clear') {
|
if (cmd === "clear") {
|
||||||
this.content = []
|
this.content = [];
|
||||||
event.target.innerHTML = ''
|
event.target.innerHTML = "";
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd === 'exit') {
|
if (cmd === "exit") {
|
||||||
event.target.innerHTML = ''
|
event.target.innerHTML = "";
|
||||||
this.toggleShell()
|
this.toggleShell();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.canInput = false
|
this.canInput = false;
|
||||||
event.target.innerHTML = ''
|
event.target.innerHTML = "";
|
||||||
|
|
||||||
let results = {
|
let results = {
|
||||||
text: `${cmd}\n\n`
|
text: `${cmd}\n\n`,
|
||||||
}
|
};
|
||||||
|
|
||||||
this.history.push(cmd)
|
this.history.push(cmd);
|
||||||
this.historyPos = this.history.length
|
this.historyPos = this.history.length;
|
||||||
this.content.push(results)
|
this.content.push(results);
|
||||||
|
|
||||||
commands(
|
commands(
|
||||||
this.path,
|
this.path,
|
||||||
cmd,
|
cmd,
|
||||||
event => {
|
(event) => {
|
||||||
results.text += `${event.data}\n`
|
results.text += `${event.data}\n`;
|
||||||
this.scroll()
|
this.scroll();
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
results.text = results.text.trimEnd()
|
results.text = results.text.trimEnd();
|
||||||
this.canInput = true
|
this.canInput = true;
|
||||||
this.$refs.input.focus()
|
this.$refs.input.focus();
|
||||||
this.scroll()
|
this.scroll();
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,82 +1,183 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav :class="{active}">
|
<nav :class="{ active }">
|
||||||
<template v-if="isLogged">
|
<template v-if="isLogged">
|
||||||
<router-link class="action" to="/files/" :aria-label="$t('sidebar.myFiles')" :title="$t('sidebar.myFiles')">
|
<button
|
||||||
|
class="action"
|
||||||
|
@click="toRoot"
|
||||||
|
:aria-label="$t('sidebar.myFiles')"
|
||||||
|
:title="$t('sidebar.myFiles')"
|
||||||
|
>
|
||||||
<i class="material-icons">folder</i>
|
<i class="material-icons">folder</i>
|
||||||
<span>{{ $t('sidebar.myFiles') }}</span>
|
<span>{{ $t("sidebar.myFiles") }}</span>
|
||||||
</router-link>
|
</button>
|
||||||
|
|
||||||
<div v-if="user.perm.create">
|
<div v-if="user.perm.create">
|
||||||
<button @click="$store.commit('showHover', 'newDir')" class="action" :aria-label="$t('sidebar.newFolder')" :title="$t('sidebar.newFolder')">
|
<button
|
||||||
|
@click="$store.commit('showHover', 'newDir')"
|
||||||
|
class="action"
|
||||||
|
:aria-label="$t('sidebar.newFolder')"
|
||||||
|
:title="$t('sidebar.newFolder')"
|
||||||
|
>
|
||||||
<i class="material-icons">create_new_folder</i>
|
<i class="material-icons">create_new_folder</i>
|
||||||
<span>{{ $t('sidebar.newFolder') }}</span>
|
<span>{{ $t("sidebar.newFolder") }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="$store.commit('showHover', 'newFile')" class="action" :aria-label="$t('sidebar.newFile')" :title="$t('sidebar.newFile')">
|
<button
|
||||||
|
@click="$store.commit('showHover', 'newFile')"
|
||||||
|
class="action"
|
||||||
|
:aria-label="$t('sidebar.newFile')"
|
||||||
|
:title="$t('sidebar.newFile')"
|
||||||
|
>
|
||||||
<i class="material-icons">note_add</i>
|
<i class="material-icons">note_add</i>
|
||||||
<span>{{ $t('sidebar.newFile') }}</span>
|
<span>{{ $t("sidebar.newFile") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<router-link class="action" to="/settings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
|
<button
|
||||||
|
class="action"
|
||||||
|
@click="toSettings"
|
||||||
|
:aria-label="$t('sidebar.settings')"
|
||||||
|
:title="$t('sidebar.settings')"
|
||||||
|
>
|
||||||
<i class="material-icons">settings_applications</i>
|
<i class="material-icons">settings_applications</i>
|
||||||
<span>{{ $t('sidebar.settings') }}</span>
|
<span>{{ $t("sidebar.settings") }}</span>
|
||||||
</router-link>
|
</button>
|
||||||
|
|
||||||
<button v-if="authMethod == 'json'" @click="logout" class="action" id="logout" :aria-label="$t('sidebar.logout')" :title="$t('sidebar.logout')">
|
<button
|
||||||
|
v-if="authMethod == 'json'"
|
||||||
|
@click="logout"
|
||||||
|
class="action"
|
||||||
|
id="logout"
|
||||||
|
:aria-label="$t('sidebar.logout')"
|
||||||
|
:title="$t('sidebar.logout')"
|
||||||
|
>
|
||||||
<i class="material-icons">exit_to_app</i>
|
<i class="material-icons">exit_to_app</i>
|
||||||
<span>{{ $t('sidebar.logout') }}</span>
|
<span>{{ $t("sidebar.logout") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<router-link class="action" to="/login" :aria-label="$t('sidebar.login')" :title="$t('sidebar.login')">
|
<router-link
|
||||||
|
class="action"
|
||||||
|
to="/login"
|
||||||
|
:aria-label="$t('sidebar.login')"
|
||||||
|
:title="$t('sidebar.login')"
|
||||||
|
>
|
||||||
<i class="material-icons">exit_to_app</i>
|
<i class="material-icons">exit_to_app</i>
|
||||||
<span>{{ $t('sidebar.login') }}</span>
|
<span>{{ $t("sidebar.login") }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<router-link v-if="signup" class="action" to="/login" :aria-label="$t('sidebar.signup')" :title="$t('sidebar.signup')">
|
<router-link
|
||||||
|
v-if="signup"
|
||||||
|
class="action"
|
||||||
|
to="/login"
|
||||||
|
:aria-label="$t('sidebar.signup')"
|
||||||
|
:title="$t('sidebar.signup')"
|
||||||
|
>
|
||||||
<i class="material-icons">person_add</i>
|
<i class="material-icons">person_add</i>
|
||||||
<span>{{ $t('sidebar.signup') }}</span>
|
<span>{{ $t("sidebar.signup") }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="credits"
|
||||||
|
v-if="$router.currentRoute.path.includes('/files/')"
|
||||||
|
style="width: 90%; margin: 2em 2.5em 3em 2.5em"
|
||||||
|
>
|
||||||
|
<progress-bar :val="usage.usedPercentage" size="small"></progress-bar>
|
||||||
|
<br />
|
||||||
|
{{ usage.used }} of {{ usage.total }} used
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="credits">
|
<p class="credits">
|
||||||
<span>
|
<span>
|
||||||
<span v-if="disableExternal">File Browser</span>
|
<span v-if="disableExternal">File Browser</span>
|
||||||
<a v-else rel="noopener noreferrer" target="_blank" href="https://github.com/filebrowser/filebrowser">File Browser</a>
|
<a
|
||||||
|
v-else
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/filebrowser/filebrowser"
|
||||||
|
>File Browser</a
|
||||||
|
>
|
||||||
<span> {{ version }}</span>
|
<span> {{ version }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span><a @click="help">{{ $t('sidebar.help') }}</a></span>
|
<span>
|
||||||
|
<a @click="help">{{ $t("sidebar.help") }}</a>
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from "vuex";
|
||||||
import * as auth from '@/utils/auth'
|
import * as auth from "@/utils/auth";
|
||||||
import { version, signup, disableExternal, noAuth, authMethod } from '@/utils/constants'
|
import {
|
||||||
|
version,
|
||||||
|
signup,
|
||||||
|
disableExternal,
|
||||||
|
noAuth,
|
||||||
|
authMethod,
|
||||||
|
} from "@/utils/constants";
|
||||||
|
import { files as api } from "@/api";
|
||||||
|
import ProgressBar from "vue-simple-progress";
|
||||||
|
import prettyBytes from "pretty-bytes";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'sidebar',
|
name: "sidebar",
|
||||||
|
components: {
|
||||||
|
ProgressBar,
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'user' ]),
|
...mapState(["user"]),
|
||||||
...mapGetters([ 'isLogged' ]),
|
...mapGetters(["isLogged"]),
|
||||||
active () {
|
active() {
|
||||||
return this.$store.state.show === 'sidebar'
|
return this.$store.state.show === "sidebar";
|
||||||
},
|
},
|
||||||
signup: () => signup,
|
signup: () => signup,
|
||||||
version: () => version,
|
version: () => version,
|
||||||
disableExternal: () => disableExternal,
|
disableExternal: () => disableExternal,
|
||||||
noAuth: () => noAuth,
|
noAuth: () => noAuth,
|
||||||
authMethod: () => authMethod
|
authMethod: () => authMethod,
|
||||||
|
},
|
||||||
|
asyncComputed: {
|
||||||
|
usage: {
|
||||||
|
async get() {
|
||||||
|
let path = this.$route.path.endsWith("/")
|
||||||
|
? this.$route.path
|
||||||
|
: this.$route.path + "/";
|
||||||
|
let usageStats = { used: 0, total: 0, usedPercentage: 0 };
|
||||||
|
try {
|
||||||
|
let usage = await api.usage(path);
|
||||||
|
usageStats = {
|
||||||
|
used: prettyBytes(usage.used, { binary: true }),
|
||||||
|
total: prettyBytes(usage.total, { binary: true }),
|
||||||
|
usedPercentage: Math.round((usage.used / usage.total) * 100),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
this.$showError(error);
|
||||||
|
}
|
||||||
|
return usageStats;
|
||||||
|
},
|
||||||
|
default: { used: "0 B", total: "0 B", usedPercentage: 0 },
|
||||||
|
shouldUpdate() {
|
||||||
|
return this.$router.currentRoute.path.includes("/files/");
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
help () {
|
toRoot() {
|
||||||
this.$store.commit('showHover', 'help')
|
this.$router.push({ path: "/files/" }, () => {});
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
},
|
},
|
||||||
logout: auth.logout
|
toSettings() {
|
||||||
}
|
this.$router.push({ path: "/settings" }, () => {});
|
||||||
}
|
this.$store.commit("closeHovers");
|
||||||
|
},
|
||||||
|
help() {
|
||||||
|
this.$store.commit("showHover", "help");
|
||||||
|
},
|
||||||
|
logout: auth.logout,
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,40 +10,33 @@
|
|||||||
@mouseup="mouseUp"
|
@mouseup="mouseUp"
|
||||||
@wheel="wheelMove"
|
@wheel="wheelMove"
|
||||||
>
|
>
|
||||||
<img src="" class="image-ex-img image-ex-img-center" ref="imgex" @load="onLoad">
|
<img
|
||||||
|
src=""
|
||||||
|
class="image-ex-img image-ex-img-center"
|
||||||
|
ref="imgex"
|
||||||
|
@load="onLoad"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from "lodash.throttle";
|
||||||
import UTIF from 'utif'
|
import UTIF from "utif";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
src: String,
|
src: String,
|
||||||
moveDisabledTime: {
|
moveDisabledTime: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: () => 200
|
default: () => 200,
|
||||||
},
|
|
||||||
maxScale: {
|
|
||||||
type: Number,
|
|
||||||
default: () => 4
|
|
||||||
},
|
|
||||||
minScale: {
|
|
||||||
type: Number,
|
|
||||||
default: () => 0.25
|
|
||||||
},
|
},
|
||||||
classList: {
|
classList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
},
|
},
|
||||||
zoomStep: {
|
zoomStep: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: () => 0.25
|
default: () => 0.25,
|
||||||
},
|
},
|
||||||
autofill: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -51,210 +44,236 @@ export default {
|
|||||||
lastX: null,
|
lastX: null,
|
||||||
lastY: null,
|
lastY: null,
|
||||||
inDrag: false,
|
inDrag: false,
|
||||||
|
touches: 0,
|
||||||
lastTouchDistance: 0,
|
lastTouchDistance: 0,
|
||||||
moveDisabled: false,
|
moveDisabled: false,
|
||||||
disabledTimer: null,
|
disabledTimer: null,
|
||||||
imageLoaded: false,
|
imageLoaded: false,
|
||||||
position: {
|
position: {
|
||||||
center: { x: 0, y: 0 },
|
center: { x: 0, y: 0 },
|
||||||
relative: { x: 0, y: 0 }
|
relative: { x: 0, y: 0 },
|
||||||
}
|
},
|
||||||
}
|
maxScale: 4,
|
||||||
|
minScale: 0.25,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (!this.decodeUTIF()) {
|
if (!this.decodeUTIF()) {
|
||||||
this.$refs.imgex.src = this.src
|
this.$refs.imgex.src = this.src;
|
||||||
}
|
}
|
||||||
let container = this.$refs.container
|
let container = this.$refs.container;
|
||||||
this.classList.forEach(className => container.classList.add(className))
|
this.classList.forEach((className) => container.classList.add(className));
|
||||||
// set width and height if they are zero
|
// set width and height if they are zero
|
||||||
if (getComputedStyle(container).width === "0px") {
|
if (getComputedStyle(container).width === "0px") {
|
||||||
container.style.width = "100%"
|
container.style.width = "100%";
|
||||||
}
|
}
|
||||||
if (getComputedStyle(container).height === "0px") {
|
if (getComputedStyle(container).height === "0px") {
|
||||||
container.style.height = "100%"
|
container.style.height = "100%";
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', this.onResize)
|
window.addEventListener("resize", this.onResize);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
window.removeEventListener('resize', this.onResize)
|
window.removeEventListener("resize", this.onResize);
|
||||||
document.removeEventListener('mouseup', this.onMouseUp)
|
document.removeEventListener("mouseup", this.onMouseUp);
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
src: function () {
|
src: function () {
|
||||||
this.scale = 1
|
if (!this.decodeUTIF()) {
|
||||||
this.setZoom()
|
this.$refs.imgex.src = this.src;
|
||||||
this.setCenter()
|
}
|
||||||
}
|
|
||||||
|
this.scale = 1;
|
||||||
|
this.setZoom();
|
||||||
|
this.setCenter();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// Modified from UTIF.replaceIMG
|
// Modified from UTIF.replaceIMG
|
||||||
decodeUTIF() {
|
decodeUTIF() {
|
||||||
const sufs = ["tif", "tiff", "dng", "cr2", "nef"]
|
const sufs = ["tif", "tiff", "dng", "cr2", "nef"];
|
||||||
let suff = document.location.pathname.split(".").pop().toLowerCase()
|
let suff = document.location.pathname.split(".").pop().toLowerCase();
|
||||||
if (sufs.indexOf(suff) == -1) return false
|
if (sufs.indexOf(suff) == -1) return false;
|
||||||
let xhr = new XMLHttpRequest()
|
let xhr = new XMLHttpRequest();
|
||||||
UTIF._xhrs.push(xhr)
|
UTIF._xhrs.push(xhr);
|
||||||
UTIF._imgs.push(this.$refs.imgex)
|
UTIF._imgs.push(this.$refs.imgex);
|
||||||
xhr.open("GET", this.src)
|
xhr.open("GET", this.src);
|
||||||
xhr.responseType = "arraybuffer"
|
xhr.responseType = "arraybuffer";
|
||||||
xhr.onload = UTIF._imgLoaded
|
xhr.onload = UTIF._imgLoaded;
|
||||||
xhr.send()
|
xhr.send();
|
||||||
return true
|
return true;
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
let img = this.$refs.imgex
|
let img = this.$refs.imgex;
|
||||||
|
|
||||||
this.imageLoaded = true
|
this.imageLoaded = true;
|
||||||
|
|
||||||
if (img === undefined) {
|
if (img === undefined) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.classList.remove('image-ex-img-center')
|
img.classList.remove("image-ex-img-center");
|
||||||
this.setCenter()
|
this.setCenter();
|
||||||
img.classList.add('image-ex-img-ready')
|
img.classList.add("image-ex-img-ready");
|
||||||
|
|
||||||
document.addEventListener('mouseup', this.onMouseUp)
|
document.addEventListener("mouseup", this.onMouseUp);
|
||||||
|
|
||||||
|
let realSize = img.naturalWidth;
|
||||||
|
let displaySize = img.offsetWidth;
|
||||||
|
|
||||||
|
// Image is in portrait orientation
|
||||||
|
if (img.naturalHeight > img.naturalWidth) {
|
||||||
|
realSize = img.naturalHeight;
|
||||||
|
displaySize = img.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale needed to display the image on full size
|
||||||
|
const fullScale = realSize / displaySize;
|
||||||
|
|
||||||
|
// Full size plus additional zoom
|
||||||
|
this.maxScale = fullScale + 4;
|
||||||
},
|
},
|
||||||
onMouseUp() {
|
onMouseUp() {
|
||||||
this.inDrag = false
|
this.inDrag = false;
|
||||||
},
|
},
|
||||||
onResize: throttle(function() {
|
onResize: throttle(function () {
|
||||||
if (this.imageLoaded) {
|
if (this.imageLoaded) {
|
||||||
this.setCenter()
|
this.setCenter();
|
||||||
this.doMove(this.position.relative.x, this.position.relative.y)
|
this.doMove(this.position.relative.x, this.position.relative.y);
|
||||||
}
|
}
|
||||||
}, 100),
|
}, 100),
|
||||||
setCenter() {
|
setCenter() {
|
||||||
let container = this.$refs.container
|
let container = this.$refs.container;
|
||||||
let img = this.$refs.imgex
|
let img = this.$refs.imgex;
|
||||||
|
|
||||||
this.position.center.x = Math.floor((container.clientWidth - img.clientWidth) / 2)
|
this.position.center.x = Math.floor(
|
||||||
this.position.center.y = Math.floor((container.clientHeight - img.clientHeight) / 2)
|
(container.clientWidth - img.clientWidth) / 2
|
||||||
|
);
|
||||||
|
this.position.center.y = Math.floor(
|
||||||
|
(container.clientHeight - img.clientHeight) / 2
|
||||||
|
);
|
||||||
|
|
||||||
img.style.left = this.position.center.x + 'px'
|
img.style.left = this.position.center.x + "px";
|
||||||
img.style.top = this.position.center.y + 'px'
|
img.style.top = this.position.center.y + "px";
|
||||||
},
|
},
|
||||||
mousedownStart(event) {
|
mousedownStart(event) {
|
||||||
this.lastX = null
|
this.lastX = null;
|
||||||
this.lastY = null
|
this.lastY = null;
|
||||||
this.inDrag = true
|
this.inDrag = true;
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
mouseMove(event) {
|
mouseMove(event) {
|
||||||
if (!this.inDrag) return
|
if (!this.inDrag) return;
|
||||||
this.doMove(event.movementX, event.movementY)
|
this.doMove(event.movementX, event.movementY);
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
mouseUp(event) {
|
mouseUp(event) {
|
||||||
this.inDrag = false
|
this.inDrag = false;
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
touchStart(event) {
|
touchStart(event) {
|
||||||
this.lastX = null
|
this.lastX = null;
|
||||||
this.lastY = null
|
this.lastY = null;
|
||||||
this.lastTouchDistance = null
|
this.lastTouchDistance = null;
|
||||||
if (event.targetTouches.length < 2) {
|
if (event.targetTouches.length < 2) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.touches = 0
|
this.touches = 0;
|
||||||
}, 300)
|
}, 300);
|
||||||
this.touches++
|
this.touches++;
|
||||||
if (this.touches > 1) {
|
if (this.touches > 1) {
|
||||||
this.zoomAuto(event)
|
this.zoomAuto(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
zoomAuto(event) {
|
zoomAuto(event) {
|
||||||
switch (this.scale) {
|
switch (this.scale) {
|
||||||
case 1:
|
case 1:
|
||||||
this.scale = 2
|
this.scale = 2;
|
||||||
break
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this.scale = 4
|
this.scale = 4;
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
case 4:
|
case 4:
|
||||||
this.scale = 1
|
this.scale = 1;
|
||||||
this.setCenter()
|
this.setCenter();
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
this.setZoom()
|
this.setZoom();
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
touchMove(event) {
|
touchMove(event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
if (this.lastX === null) {
|
if (this.lastX === null) {
|
||||||
this.lastX = event.targetTouches[0].pageX
|
this.lastX = event.targetTouches[0].pageX;
|
||||||
this.lastY = event.targetTouches[0].pageY
|
this.lastY = event.targetTouches[0].pageY;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
let step = this.$refs.imgex.width / 5
|
let step = this.$refs.imgex.width / 5;
|
||||||
if (event.targetTouches.length === 2) {
|
if (event.targetTouches.length === 2) {
|
||||||
this.moveDisabled = true
|
this.moveDisabled = true;
|
||||||
clearTimeout(this.disabledTimer)
|
clearTimeout(this.disabledTimer);
|
||||||
this.disabledTimer = setTimeout(
|
this.disabledTimer = setTimeout(
|
||||||
() => (this.moveDisabled = false),
|
() => (this.moveDisabled = false),
|
||||||
this.moveDisabledTime
|
this.moveDisabledTime
|
||||||
)
|
);
|
||||||
|
|
||||||
let p1 = event.targetTouches[0]
|
let p1 = event.targetTouches[0];
|
||||||
let p2 = event.targetTouches[1]
|
let p2 = event.targetTouches[1];
|
||||||
let touchDistance = Math.sqrt(
|
let touchDistance = Math.sqrt(
|
||||||
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
|
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
|
||||||
)
|
);
|
||||||
if (!this.lastTouchDistance) {
|
if (!this.lastTouchDistance) {
|
||||||
this.lastTouchDistance = touchDistance
|
this.lastTouchDistance = touchDistance;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
this.scale += (touchDistance - this.lastTouchDistance) / step
|
this.scale += (touchDistance - this.lastTouchDistance) / step;
|
||||||
this.lastTouchDistance = touchDistance
|
this.lastTouchDistance = touchDistance;
|
||||||
this.setZoom()
|
this.setZoom();
|
||||||
} else if (event.targetTouches.length === 1) {
|
} else if (event.targetTouches.length === 1) {
|
||||||
if (this.moveDisabled) return
|
if (this.moveDisabled) return;
|
||||||
let x = event.targetTouches[0].pageX - this.lastX
|
let x = event.targetTouches[0].pageX - this.lastX;
|
||||||
let y = event.targetTouches[0].pageY - this.lastY
|
let y = event.targetTouches[0].pageY - this.lastY;
|
||||||
if (Math.abs(x) >= step && Math.abs(y) >= step) return
|
if (Math.abs(x) >= step && Math.abs(y) >= step) return;
|
||||||
this.lastX = event.targetTouches[0].pageX
|
this.lastX = event.targetTouches[0].pageX;
|
||||||
this.lastY = event.targetTouches[0].pageY
|
this.lastY = event.targetTouches[0].pageY;
|
||||||
this.doMove(x, y)
|
this.doMove(x, y);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doMove(x, y) {
|
doMove(x, y) {
|
||||||
let style = this.$refs.imgex.style
|
let style = this.$refs.imgex.style;
|
||||||
let posX = this.pxStringToNumber(style.left) + x
|
let posX = this.pxStringToNumber(style.left) + x;
|
||||||
let posY = this.pxStringToNumber(style.top) + y
|
let posY = this.pxStringToNumber(style.top) + y;
|
||||||
|
|
||||||
style.left = posX + 'px'
|
style.left = posX + "px";
|
||||||
style.top = posY + 'px'
|
style.top = posY + "px";
|
||||||
|
|
||||||
this.position.relative.x = Math.abs(this.position.center.x - posX)
|
this.position.relative.x = Math.abs(this.position.center.x - posX);
|
||||||
this.position.relative.y = Math.abs(this.position.center.y - posY)
|
this.position.relative.y = Math.abs(this.position.center.y - posY);
|
||||||
|
|
||||||
if (posX < this.position.center.x) {
|
if (posX < this.position.center.x) {
|
||||||
this.position.relative.x = this.position.relative.x * -1
|
this.position.relative.x = this.position.relative.x * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (posY < this.position.center.y) {
|
if (posY < this.position.center.y) {
|
||||||
this.position.relative.y = this.position.relative.y * -1
|
this.position.relative.y = this.position.relative.y * -1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
wheelMove(event) {
|
wheelMove(event) {
|
||||||
this.scale += (event.wheelDeltaY / 100) * this.zoomStep
|
this.scale += -Math.sign(event.deltaY) * this.zoomStep;
|
||||||
this.setZoom()
|
this.setZoom();
|
||||||
},
|
},
|
||||||
setZoom() {
|
setZoom() {
|
||||||
this.scale = this.scale < this.minScale ? this.minScale : this.scale
|
this.scale = this.scale < this.minScale ? this.minScale : this.scale;
|
||||||
this.scale = this.scale > this.maxScale ? this.maxScale : this.scale
|
this.scale = this.scale > this.maxScale ? this.maxScale : this.scale;
|
||||||
this.$refs.imgex.style.transform = `scale(${this.scale})`
|
this.$refs.imgex.style.transform = `scale(${this.scale})`;
|
||||||
},
|
},
|
||||||
pxStringToNumber(style) {
|
pxStringToNumber(style) {
|
||||||
return +style.replace("px", "")
|
return +style.replace("px", "");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.image-ex-container {
|
.image-ex-container {
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="item"
|
<div
|
||||||
role="button"
|
class="item"
|
||||||
tabindex="0"
|
role="button"
|
||||||
:draggable="isDraggable"
|
tabindex="0"
|
||||||
@dragstart="dragStart"
|
:draggable="isDraggable"
|
||||||
@dragover="dragOver"
|
@dragstart="dragStart"
|
||||||
@drop="drop"
|
@dragover="dragOver"
|
||||||
@click="itemClick"
|
@drop="drop"
|
||||||
@dblclick="dblclick"
|
@click="itemClick"
|
||||||
@touchstart="touchstart"
|
:data-dir="isDir"
|
||||||
:data-dir="isDir"
|
:data-type="type"
|
||||||
:aria-label="name"
|
:aria-label="name"
|
||||||
:aria-selected="isSelected">
|
:aria-selected="isSelected"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<img v-if="readOnly == undefined && type==='image' && isThumbsEnabled" v-lazy="thumbnailUrl">
|
<img
|
||||||
<i v-else class="material-icons">{{ icon }}</i>
|
v-if="readOnly == undefined && type === 'image' && isThumbsEnabled"
|
||||||
|
v-lazy="thumbnailUrl"
|
||||||
|
/>
|
||||||
|
<i v-else class="material-icons"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -31,203 +35,215 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { baseURL, enableThumbs } from '@/utils/constants'
|
import { enableThumbs } from "@/utils/constants";
|
||||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
import { mapMutations, mapGetters, mapState } from "vuex";
|
||||||
import filesize from 'filesize'
|
import filesize from "filesize";
|
||||||
import moment from 'moment'
|
import moment from "moment";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import * as upload from '@/utils/upload'
|
import * as upload from "@/utils/upload";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'item',
|
name: "item",
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
touches: 0
|
touches: 0,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index', 'readOnly'],
|
props: [
|
||||||
|
"name",
|
||||||
|
"isDir",
|
||||||
|
"url",
|
||||||
|
"type",
|
||||||
|
"size",
|
||||||
|
"modified",
|
||||||
|
"index",
|
||||||
|
"readOnly",
|
||||||
|
"path",
|
||||||
|
],
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['user', 'selected', 'req', 'jwt']),
|
...mapState(["user", "selected", "req", "jwt"]),
|
||||||
...mapGetters(['selectedCount']),
|
...mapGetters(["selectedCount"]),
|
||||||
singleClick () {
|
singleClick() {
|
||||||
return this.readOnly == undefined && this.user.singleClick
|
return this.readOnly == undefined && this.user.singleClick;
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected() {
|
||||||
return (this.selected.indexOf(this.index) !== -1)
|
return this.selected.indexOf(this.index) !== -1;
|
||||||
},
|
},
|
||||||
icon () {
|
isDraggable() {
|
||||||
if (this.isDir) return 'folder'
|
return this.readOnly == undefined && this.user.perm.rename;
|
||||||
if (this.type === 'image') return 'insert_photo'
|
|
||||||
if (this.type === 'audio') return 'volume_up'
|
|
||||||
if (this.type === 'video') return 'movie'
|
|
||||||
return 'insert_drive_file'
|
|
||||||
},
|
},
|
||||||
isDraggable () {
|
canDrop() {
|
||||||
return this.readOnly == undefined && this.user.perm.rename
|
if (!this.isDir || this.readOnly !== undefined) return false;
|
||||||
},
|
|
||||||
canDrop () {
|
|
||||||
if (!this.isDir || this.readOnly !== undefined) return false
|
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (let i of this.selected) {
|
||||||
if (this.req.items[i].url === this.url) {
|
if (this.req.items[i].url === this.url) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
},
|
},
|
||||||
thumbnailUrl () {
|
thumbnailUrl() {
|
||||||
const path = this.url.replace(/^\/files\//, '')
|
const file = {
|
||||||
|
path: this.path,
|
||||||
|
modified: this.modified,
|
||||||
|
};
|
||||||
|
|
||||||
// reload the image when the file is replaced
|
return api.getPreviewURL(file, "thumb");
|
||||||
const key = Date.parse(this.modified)
|
},
|
||||||
|
isThumbsEnabled() {
|
||||||
return `${baseURL}/api/preview/thumb/${path}?auth=${this.jwt}&inline=true&k=${key}`
|
return enableThumbs;
|
||||||
},
|
},
|
||||||
isThumbsEnabled () {
|
|
||||||
return enableThumbs
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['addSelected', 'removeSelected', 'resetSelected']),
|
...mapMutations(["addSelected", "removeSelected", "resetSelected"]),
|
||||||
humanSize: function () {
|
humanSize: function () {
|
||||||
return filesize(this.size)
|
return this.type == "invalid_link" ? "invalid link" : filesize(this.size);
|
||||||
},
|
},
|
||||||
humanTime: function () {
|
humanTime: function () {
|
||||||
return moment(this.modified).fromNow()
|
if (this.readOnly == undefined && this.user.dateFormat) {
|
||||||
|
return moment(this.modified).format("L LT");
|
||||||
|
}
|
||||||
|
return moment(this.modified).fromNow();
|
||||||
},
|
},
|
||||||
dragStart: function () {
|
dragStart: function () {
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isSelected) {
|
if (!this.isSelected) {
|
||||||
this.resetSelected()
|
this.resetSelected();
|
||||||
this.addSelected(this.index)
|
this.addSelected(this.index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dragOver: function (event) {
|
dragOver: function (event) {
|
||||||
if (!this.canDrop) return
|
if (!this.canDrop) return;
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
let el = event.target
|
let el = event.target;
|
||||||
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (!el.classList.contains('item')) {
|
if (!el.classList.contains("item")) {
|
||||||
el = el.parentElement
|
el = el.parentElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
el.style.opacity = 1
|
el.style.opacity = 1;
|
||||||
},
|
},
|
||||||
drop: async function (event) {
|
drop: async function (event) {
|
||||||
if (!this.canDrop) return
|
if (!this.canDrop) return;
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.selectedCount === 0) return
|
if (this.selectedCount === 0) return;
|
||||||
|
|
||||||
let el = event.target
|
let el = event.target;
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (el !== null && !el.classList.contains('item')) {
|
if (el !== null && !el.classList.contains("item")) {
|
||||||
el = el.parentElement
|
el = el.parentElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let items = []
|
let items = [];
|
||||||
|
|
||||||
for (let i of this.selected) {
|
for (let i of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[i].url,
|
from: this.req.items[i].url,
|
||||||
to: this.url + this.req.items[i].name,
|
to: this.url + encodeURIComponent(this.req.items[i].name),
|
||||||
name: this.req.items[i].name
|
name: this.req.items[i].name,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let base = el.querySelector('.name').innerHTML + '/'
|
// Get url from ListingItem instance
|
||||||
let path = this.$route.path + base
|
let path = el.__vue__.url;
|
||||||
let baseItems = (await api.fetch(path)).items
|
let baseItems = (await api.fetch(path)).items;
|
||||||
|
|
||||||
let action = (overwrite, rename) => {
|
let action = (overwrite, rename) => {
|
||||||
api.move(items, overwrite, rename).then(() => {
|
api
|
||||||
this.$store.commit('setReload', true)
|
.move(items, overwrite, rename)
|
||||||
}).catch(this.$showError)
|
.then(() => {
|
||||||
}
|
this.$store.commit("setReload", true);
|
||||||
|
})
|
||||||
|
.catch(this.$showError);
|
||||||
|
};
|
||||||
|
|
||||||
let conflict = upload.checkConflict(items, baseItems)
|
let conflict = upload.checkConflict(items, baseItems);
|
||||||
|
|
||||||
let overwrite = false
|
let overwrite = false;
|
||||||
let rename = false
|
let rename = false;
|
||||||
|
|
||||||
if (conflict) {
|
if (conflict) {
|
||||||
this.$store.commit('showHover', {
|
this.$store.commit("showHover", {
|
||||||
prompt: 'replace-rename',
|
prompt: "replace-rename",
|
||||||
confirm: (event, option) => {
|
confirm: (event, option) => {
|
||||||
overwrite = option == 'overwrite'
|
overwrite = option == "overwrite";
|
||||||
rename = option == 'rename'
|
rename = option == "rename";
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
action(overwrite, rename)
|
action(overwrite, rename);
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action(overwrite, rename)
|
action(overwrite, rename);
|
||||||
},
|
},
|
||||||
itemClick: function(event) {
|
itemClick: function (event) {
|
||||||
if (this.singleClick && !this.$store.state.multiple) this.open()
|
if (this.singleClick && !this.$store.state.multiple) this.open();
|
||||||
else this.click(event)
|
else this.click(event);
|
||||||
},
|
},
|
||||||
click: function (event) {
|
click: function (event) {
|
||||||
if (!this.singleClick && this.selectedCount !== 0) event.preventDefault()
|
if (!this.singleClick && this.selectedCount !== 0) event.preventDefault();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.touches = 0;
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
this.touches++;
|
||||||
|
if (this.touches > 1) {
|
||||||
|
this.open();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||||
this.removeSelected(this.index)
|
this.removeSelected(this.index);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.shiftKey && this.selected.length > 0) {
|
if (event.shiftKey && this.selected.length > 0) {
|
||||||
let fi = 0
|
let fi = 0;
|
||||||
let la = 0
|
let la = 0;
|
||||||
|
|
||||||
if (this.index > this.selected[0]) {
|
if (this.index > this.selected[0]) {
|
||||||
fi = this.selected[0] + 1
|
fi = this.selected[0] + 1;
|
||||||
la = this.index
|
la = this.index;
|
||||||
} else {
|
} else {
|
||||||
fi = this.index
|
fi = this.index;
|
||||||
la = this.selected[0] - 1
|
la = this.selected[0] - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; fi <= la; fi++) {
|
for (; fi <= la; fi++) {
|
||||||
if (this.$store.state.selected.indexOf(fi) == -1) {
|
if (this.$store.state.selected.indexOf(fi) == -1) {
|
||||||
this.addSelected(fi)
|
this.addSelected(fi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.singleClick && !event.ctrlKey && !event.metaKey && !this.$store.state.multiple) this.resetSelected()
|
if (
|
||||||
this.addSelected(this.index)
|
!this.singleClick &&
|
||||||
},
|
!event.ctrlKey &&
|
||||||
dblclick: function () {
|
!event.metaKey &&
|
||||||
if (!this.singleClick) this.open()
|
!this.$store.state.multiple
|
||||||
},
|
)
|
||||||
touchstart () {
|
this.resetSelected();
|
||||||
setTimeout(() => {
|
this.addSelected(this.index);
|
||||||
this.touches = 0
|
|
||||||
}, 300)
|
|
||||||
|
|
||||||
this.touches++
|
|
||||||
if (this.touches > 1) {
|
|
||||||
this.open()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
open: function () {
|
open: function () {
|
||||||
this.$router.push({path: this.url})
|
this.$router.push({ path: this.url });
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button @click="action" :aria-label="label" :title="label" class="action">
|
<button @click="action" :aria-label="label" :title="label" class="action">
|
||||||
<i class="material-icons">{{ icon }}</i>
|
<i class="material-icons">{{ icon }}</i>
|
||||||
<span>{{ label }}</span>
|
<span>{{ label }}</span>
|
||||||
<span v-if="counter > 0" class="counter">{{ counter }}</span>
|
<span v-if="counter > 0" class="counter">{{ counter }}</span>
|
||||||
@@ -8,25 +8,18 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'action',
|
name: "action",
|
||||||
props: [
|
props: ["icon", "label", "counter", "show"],
|
||||||
'icon',
|
|
||||||
'label',
|
|
||||||
'counter',
|
|
||||||
'show'
|
|
||||||
],
|
|
||||||
methods: {
|
methods: {
|
||||||
action: function () {
|
action: function () {
|
||||||
if (this.show) {
|
if (this.show) {
|
||||||
this.$store.commit('showHover', this.show)
|
this.$store.commit("showHover", this.show);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('action')
|
this.$emit("action");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<header>
|
<header>
|
||||||
<img v-if="showLogo !== undefined" :src="logoURL" />
|
<img v-if="showLogo !== undefined" :src="logoURL" />
|
||||||
<action v-if="showMenu !== undefined" class="menu-button" icon="menu" :label="$t('buttons.toggleSidebar')" @action="openSidebar()" />
|
<action
|
||||||
|
v-if="showMenu !== undefined"
|
||||||
|
class="menu-button"
|
||||||
|
icon="menu"
|
||||||
|
:label="$t('buttons.toggleSidebar')"
|
||||||
|
@action="openSidebar()"
|
||||||
|
/>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
@@ -9,39 +15,44 @@
|
|||||||
<slot name="actions" />
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<action v-if="this.$slots.actions" id="more" icon="more_vert" :label="$t('buttons.more')" @action="$store.commit('showHover', 'more')" />
|
<action
|
||||||
|
v-if="this.$slots.actions"
|
||||||
|
id="more"
|
||||||
|
icon="more_vert"
|
||||||
|
:label="$t('buttons.more')"
|
||||||
|
@action="$store.commit('showHover', 'more')"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="overlay" v-show="this.$store.state.show == 'more'" @click="$store.commit('closeHovers')" />
|
<div
|
||||||
|
class="overlay"
|
||||||
|
v-show="this.$store.state.show == 'more'"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { logoURL } from '@/utils/constants'
|
import { logoURL } from "@/utils/constants";
|
||||||
|
|
||||||
import Action from '@/components/header/Action'
|
import Action from "@/components/header/Action";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'header-bar',
|
name: "header-bar",
|
||||||
props: [
|
props: ["showLogo", "showMenu"],
|
||||||
'showLogo',
|
|
||||||
'showMenu',
|
|
||||||
],
|
|
||||||
components: {
|
components: {
|
||||||
Action
|
Action,
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
logoURL
|
logoURL,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openSidebar () {
|
openSidebar() {
|
||||||
this.$store.commit('showHover', 'sidebar')
|
this.$store.commit("showHover", "sidebar");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,108 +1,119 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.copy') }}</h2>
|
<h2>{{ $t("prompts.copy") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.copyMessage') }}</p>
|
<p>{{ $t("prompts.copyMessage") }}</p>
|
||||||
<file-list @update:selected="val => dest = val"></file-list>
|
<file-list @update:selected="(val) => (dest = val)"></file-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
@click="copy"
|
@click="copy"
|
||||||
:aria-label="$t('buttons.copy')"
|
:aria-label="$t('buttons.copy')"
|
||||||
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
|
:title="$t('buttons.copy')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.copy") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
import FileList from './FileList'
|
import FileList from "./FileList";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import buttons from '@/utils/buttons'
|
import buttons from "@/utils/buttons";
|
||||||
import * as upload from '@/utils/upload'
|
import * as upload from "@/utils/upload";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'copy',
|
name: "copy",
|
||||||
components: { FileList },
|
components: { FileList },
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
current: window.location.pathname,
|
current: window.location.pathname,
|
||||||
dest: null
|
dest: null,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: mapState(['req', 'selected']),
|
computed: mapState(["req", "selected"]),
|
||||||
methods: {
|
methods: {
|
||||||
copy: async function (event) {
|
copy: async function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
let items = []
|
let items = [];
|
||||||
|
|
||||||
// Create a new promise for each file.
|
// Create a new promise for each file.
|
||||||
for (let item of this.selected) {
|
for (let item of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[item].url,
|
from: this.req.items[item].url,
|
||||||
to: this.dest + encodeURIComponent(this.req.items[item].name),
|
to: this.dest + encodeURIComponent(this.req.items[item].name),
|
||||||
name: this.req.items[item].name
|
name: this.req.items[item].name,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = async (overwrite, rename) => {
|
let action = async (overwrite, rename) => {
|
||||||
buttons.loading('copy')
|
buttons.loading("copy");
|
||||||
|
|
||||||
await api.copy(items, overwrite, rename).then(() => {
|
await api
|
||||||
buttons.success('copy')
|
.copy(items, overwrite, rename)
|
||||||
|
.then(() => {
|
||||||
|
buttons.success("copy");
|
||||||
|
|
||||||
if (this.$route.path === this.dest) {
|
if (this.$route.path === this.dest) {
|
||||||
this.$store.commit('setReload', true)
|
this.$store.commit("setReload", true);
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.push({ path: this.dest })
|
this.$router.push({ path: this.dest });
|
||||||
}).catch((e) => {
|
})
|
||||||
buttons.done('copy')
|
.catch((e) => {
|
||||||
this.$showError(e)
|
buttons.done("copy");
|
||||||
})
|
this.$showError(e);
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (this.$route.path === this.dest) {
|
if (this.$route.path === this.dest) {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
action(false, true)
|
action(false, true);
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dstItems = (await api.fetch(this.dest)).items
|
let dstItems = (await api.fetch(this.dest)).items;
|
||||||
let conflict = upload.checkConflict(items, dstItems)
|
let conflict = upload.checkConflict(items, dstItems);
|
||||||
|
|
||||||
let overwrite = false
|
let overwrite = false;
|
||||||
let rename = false
|
let rename = false;
|
||||||
|
|
||||||
if (conflict) {
|
if (conflict) {
|
||||||
this.$store.commit('showHover', {
|
this.$store.commit("showHover", {
|
||||||
prompt: 'replace-rename',
|
prompt: "replace-rename",
|
||||||
confirm: (event, option) => {
|
confirm: (event, option) => {
|
||||||
overwrite = option == 'overwrite'
|
overwrite = option == "overwrite";
|
||||||
rename = option == 'rename'
|
rename = option == "rename";
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
action(overwrite, rename)
|
action(overwrite, rename);
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action(overwrite, rename)
|
action(overwrite, rename);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,68 +1,80 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p v-if="req.kind !== 'listing'">{{ $t('prompts.deleteMessageSingle') }}</p>
|
<p v-if="req.kind !== 'listing'">
|
||||||
<p v-else>{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
|
{{ $t("prompts.deleteMessageSingle") }}
|
||||||
|
</p>
|
||||||
|
<p v-else>
|
||||||
|
{{ $t("prompts.deleteMessageMultiple", { count: selectedCount }) }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button @click="$store.commit('closeHovers')"
|
<button
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
class="button button--flat button--grey"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button @click="submit"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="submit"
|
||||||
class="button button--flat button--red"
|
class="button button--flat button--red"
|
||||||
:aria-label="$t('buttons.delete')"
|
:aria-label="$t('buttons.delete')"
|
||||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
:title="$t('buttons.delete')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.delete") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapGetters, mapMutations, mapState} from 'vuex'
|
import { mapGetters, mapMutations, mapState } from "vuex";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import buttons from '@/utils/buttons'
|
import buttons from "@/utils/buttons";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'delete',
|
name: "delete",
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['isListing', 'selectedCount']),
|
...mapGetters(["isListing", "selectedCount"]),
|
||||||
...mapState(['req', 'selected', 'showConfirm'])
|
...mapState(["req", "selected", "showConfirm"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['closeHovers']),
|
...mapMutations(["closeHovers"]),
|
||||||
submit: async function () {
|
submit: async function () {
|
||||||
buttons.loading('delete')
|
buttons.loading("delete");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
await api.remove(this.$route.path)
|
await api.remove(this.$route.path);
|
||||||
buttons.success('delete')
|
buttons.success("delete");
|
||||||
|
|
||||||
this.showConfirm()
|
this.showConfirm();
|
||||||
this.closeHovers()
|
this.closeHovers();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeHovers()
|
this.closeHovers();
|
||||||
|
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let promises = []
|
let promises = [];
|
||||||
for (let index of this.selected) {
|
for (let index of this.selected) {
|
||||||
promises.push(api.remove(this.req.items[index].url))
|
promises.push(api.remove(this.req.items[index].url));
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises)
|
await Promise.all(promises);
|
||||||
buttons.success('delete')
|
buttons.success("delete");
|
||||||
this.$store.commit('setReload', true)
|
this.$store.commit("setReload", true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
buttons.done('delete')
|
buttons.done("delete");
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
if (this.isListing) this.$store.commit('setReload', true)
|
if (this.isListing) this.$store.commit("setReload", true);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,35 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating" id="download">
|
<div class="card floating" id="download">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.download') }}</h2>
|
<h2>{{ $t("prompts.download") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.downloadMessage') }}</p>
|
<p>{{ $t("prompts.downloadMessage") }}</p>
|
||||||
|
|
||||||
<button v-for="(ext, format) in formats" :key="format" class="button button--block" @click="showConfirm(format)" v-focus>{{ ext }}</button>
|
<button
|
||||||
|
v-for="(ext, format) in formats"
|
||||||
|
:key="format"
|
||||||
|
class="button button--block"
|
||||||
|
@click="showConfirm(format)"
|
||||||
|
v-focus
|
||||||
|
>
|
||||||
|
{{ ext }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'download',
|
name: "download",
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
formats: {
|
formats: {
|
||||||
zip: 'zip',
|
zip: "zip",
|
||||||
tar: 'tar',
|
tar: "tar",
|
||||||
targz: 'tar.gz',
|
targz: "tar.gz",
|
||||||
tarbz2: 'tar.bz2',
|
tarbz2: "tar.bz2",
|
||||||
tarxz: 'tar.xz',
|
tarxz: "tar.xz",
|
||||||
tarlz4: 'tar.lz4',
|
tarlz4: "tar.lz4",
|
||||||
tarsz: 'tar.sz'
|
tarsz: "tar.sz",
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: mapState(['showConfirm'])
|
computed: mapState(["showConfirm"]),
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,132 +1,138 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ul class="file-list">
|
<ul class="file-list">
|
||||||
<li @click="itemClick"
|
<li
|
||||||
|
@click="itemClick"
|
||||||
@touchstart="touchstart"
|
@touchstart="touchstart"
|
||||||
@dblclick="next"
|
@dblclick="next"
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
:aria-label="item.name"
|
:aria-label="item.name"
|
||||||
:aria-selected="selected == item.url"
|
:aria-selected="selected == item.url"
|
||||||
:key="item.name" v-for="item in items"
|
:key="item.name"
|
||||||
:data-url="item.url">{{ item.name }}</li>
|
v-for="item in items"
|
||||||
|
:data-url="item.url"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>{{ $t('prompts.currentlyNavigating') }} <code>{{ nav }}</code>.</p>
|
<p>
|
||||||
|
{{ $t("prompts.currentlyNavigating") }} <code>{{ nav }}</code
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
import url from '@/utils/url'
|
import url from "@/utils/url";
|
||||||
import { files } from '@/api'
|
import { files } from "@/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'file-list',
|
name: "file-list",
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
touches: {
|
touches: {
|
||||||
id: '',
|
id: "",
|
||||||
count: 0
|
count: 0,
|
||||||
},
|
},
|
||||||
selected: null,
|
selected: null,
|
||||||
current: window.location.pathname
|
current: window.location.pathname,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'req', 'user' ]),
|
...mapState(["req", "user"]),
|
||||||
nav () {
|
nav() {
|
||||||
return decodeURIComponent(this.current)
|
return decodeURIComponent(this.current);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
this.fillOptions(this.req)
|
this.fillOptions(this.req);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fillOptions (req) {
|
fillOptions(req) {
|
||||||
// Sets the current path and resets
|
// Sets the current path and resets
|
||||||
// the current items.
|
// the current items.
|
||||||
this.current = req.url
|
this.current = req.url;
|
||||||
this.items = []
|
this.items = [];
|
||||||
|
|
||||||
this.$emit('update:selected', this.current)
|
this.$emit("update:selected", this.current);
|
||||||
|
|
||||||
// If the path isn't the root path,
|
// If the path isn't the root path,
|
||||||
// show a button to navigate to the previous
|
// show a button to navigate to the previous
|
||||||
// directory.
|
// directory.
|
||||||
if (req.url !== '/files/') {
|
if (req.url !== "/files/") {
|
||||||
this.items.push({
|
this.items.push({
|
||||||
name: '..',
|
name: "..",
|
||||||
url: url.removeLastDir(req.url) + '/'
|
url: url.removeLastDir(req.url) + "/",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this folder is empty, finish here.
|
// If this folder is empty, finish here.
|
||||||
if (req.items === null) return
|
if (req.items === null) return;
|
||||||
|
|
||||||
// Otherwise we add every directory to the
|
// Otherwise we add every directory to the
|
||||||
// move options.
|
// move options.
|
||||||
for (let item of req.items) {
|
for (let item of req.items) {
|
||||||
if (!item.isDir) continue
|
if (!item.isDir) continue;
|
||||||
|
|
||||||
this.items.push({
|
this.items.push({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
url: item.url
|
url: item.url,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
next: function (event) {
|
next: function (event) {
|
||||||
// Retrieves the URL of the directory the user
|
// Retrieves the URL of the directory the user
|
||||||
// just clicked in and fill the options with its
|
// just clicked in and fill the options with its
|
||||||
// content.
|
// content.
|
||||||
let uri = event.currentTarget.dataset.url
|
let uri = event.currentTarget.dataset.url;
|
||||||
|
|
||||||
files.fetch(uri)
|
files.fetch(uri).then(this.fillOptions).catch(this.$showError);
|
||||||
.then(this.fillOptions)
|
|
||||||
.catch(this.$showError)
|
|
||||||
},
|
},
|
||||||
touchstart (event) {
|
touchstart(event) {
|
||||||
let url = event.currentTarget.dataset.url
|
let url = event.currentTarget.dataset.url;
|
||||||
|
|
||||||
// In 300 milliseconds, we shall reset the count.
|
// In 300 milliseconds, we shall reset the count.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.touches.count = 0
|
this.touches.count = 0;
|
||||||
}, 300)
|
}, 300);
|
||||||
|
|
||||||
// If the element the user is touching
|
// If the element the user is touching
|
||||||
// is different from the last one he touched,
|
// is different from the last one he touched,
|
||||||
// reset the count.
|
// reset the count.
|
||||||
if (this.touches.id !== url) {
|
if (this.touches.id !== url) {
|
||||||
this.touches.id = url
|
this.touches.id = url;
|
||||||
this.touches.count = 1
|
this.touches.count = 1;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.touches.count++
|
this.touches.count++;
|
||||||
|
|
||||||
// If there is more than one touch already,
|
// If there is more than one touch already,
|
||||||
// open the next screen.
|
// open the next screen.
|
||||||
if (this.touches.count > 1) {
|
if (this.touches.count > 1) {
|
||||||
this.next(event)
|
this.next(event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemClick: function (event) {
|
itemClick: function (event) {
|
||||||
if (this.user.singleClick) this.next(event)
|
if (this.user.singleClick) this.next(event);
|
||||||
else this.select(event)
|
else this.select(event);
|
||||||
},
|
},
|
||||||
select: function (event) {
|
select: function (event) {
|
||||||
// If the element is already selected, unselect it.
|
// If the element is already selected, unselect it.
|
||||||
if (this.selected === event.currentTarget.dataset.url) {
|
if (this.selected === event.currentTarget.dataset.url) {
|
||||||
this.selected = null
|
this.selected = null;
|
||||||
this.$emit('update:selected', this.current)
|
this.$emit("update:selected", this.current);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise select the element.
|
// Otherwise select the element.
|
||||||
this.selected = event.currentTarget.dataset.url
|
this.selected = event.currentTarget.dataset.url;
|
||||||
this.$emit('update:selected', this.selected)
|
this.$emit("update:selected", this.selected);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,34 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating help">
|
<div class="card floating help">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('help.help') }}</h2>
|
<h2>{{ $t("help.help") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>F1</strong> - {{ $t('help.f1') }}</li>
|
<li><strong>F1</strong> - {{ $t("help.f1") }}</li>
|
||||||
<li><strong>F2</strong> - {{ $t('help.f2') }}</li>
|
<li><strong>F2</strong> - {{ $t("help.f2") }}</li>
|
||||||
<li><strong>DEL</strong> - {{ $t('help.del') }}</li>
|
<li><strong>DEL</strong> - {{ $t("help.del") }}</li>
|
||||||
<li><strong>ESC</strong> - {{ $t('help.esc') }}</li>
|
<li><strong>ESC</strong> - {{ $t("help.esc") }}</li>
|
||||||
<li><strong>CTRL + S</strong> - {{ $t('help.ctrl.s') }}</li>
|
<li><strong>CTRL + S</strong> - {{ $t("help.ctrl.s") }}</li>
|
||||||
<li><strong>CTRL + F</strong> - {{ $t('help.ctrl.f') }}</li>
|
<li><strong>CTRL + F</strong> - {{ $t("help.ctrl.f") }}</li>
|
||||||
<li><strong>CTRL + Click</strong> - {{ $t('help.ctrl.click') }}</li>
|
<li><strong>CTRL + Click</strong> - {{ $t("help.ctrl.click") }}</li>
|
||||||
<li><strong>Click</strong> - {{ $t('help.click') }}</li>
|
<li><strong>Click</strong> - {{ $t("help.click") }}</li>
|
||||||
<li><strong>Double click</strong> - {{ $t('help.doubleClick') }}</li>
|
<li><strong>Double click</strong> - {{ $t("help.doubleClick") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button type="submit"
|
<button
|
||||||
|
type="submit"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
:aria-label="$t('buttons.ok')"
|
:aria-label="$t('buttons.ok')"
|
||||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
:title="$t('buttons.ok')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.ok") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default { name: 'help' }
|
export default { name: "help" };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,99 +1,152 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.fileInfo') }}</h2>
|
<h2>{{ $t("prompts.fileInfo") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p v-if="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
|
<p v-if="selected.length > 1">
|
||||||
|
{{ $t("prompts.filesSelected", { count: selected.length }) }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="break-word" v-if="selected.length < 2"><strong>{{ $t('prompts.displayName') }}</strong> {{ name }}</p>
|
<p class="break-word" v-if="selected.length < 2">
|
||||||
<p v-if="!dir || selected.length > 1"><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length"></span> {{ humanSize }}</p>
|
<strong>{{ $t("prompts.displayName") }}</strong> {{ name }}
|
||||||
<p v-if="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime }}</p>
|
</p>
|
||||||
|
<p v-if="!dir || selected.length > 1">
|
||||||
|
<strong>{{ $t("prompts.size") }}:</strong>
|
||||||
|
<span id="content_length"></span> {{ humanSize }}
|
||||||
|
</p>
|
||||||
|
<p v-if="selected.length < 2" :title="modTime">
|
||||||
|
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<template v-if="dir && selected.length === 0">
|
<template v-if="dir && selected.length === 0">
|
||||||
<p><strong>{{ $t('prompts.numberFiles') }}:</strong> {{ req.numFiles }}</p>
|
<p>
|
||||||
<p><strong>{{ $t('prompts.numberDirs') }}:</strong> {{ req.numDirs }}</p>
|
<strong>{{ $t("prompts.numberFiles") }}:</strong> {{ req.numFiles }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{{ $t("prompts.numberDirs") }}:</strong> {{ req.numDirs }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="!dir">
|
<template v-if="!dir">
|
||||||
<p><strong>MD5: </strong><code><a @click="checksum($event, 'md5')">{{ $t('prompts.show') }}</a></code></p>
|
<p>
|
||||||
<p><strong>SHA1: </strong><code><a @click="checksum($event, 'sha1')">{{ $t('prompts.show') }}</a></code></p>
|
<strong>MD5: </strong
|
||||||
<p><strong>SHA256: </strong><code><a @click="checksum($event, 'sha256')">{{ $t('prompts.show') }}</a></code></p>
|
><code
|
||||||
<p><strong>SHA512: </strong><code><a @click="checksum($event, 'sha512')">{{ $t('prompts.show') }}</a></code></p>
|
><a @click="checksum($event, 'md5')">{{
|
||||||
|
$t("prompts.show")
|
||||||
|
}}</a></code
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>SHA1: </strong
|
||||||
|
><code
|
||||||
|
><a @click="checksum($event, 'sha1')">{{
|
||||||
|
$t("prompts.show")
|
||||||
|
}}</a></code
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>SHA256: </strong
|
||||||
|
><code
|
||||||
|
><a @click="checksum($event, 'sha256')">{{
|
||||||
|
$t("prompts.show")
|
||||||
|
}}</a></code
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>SHA512: </strong
|
||||||
|
><code
|
||||||
|
><a @click="checksum($event, 'sha512')">{{
|
||||||
|
$t("prompts.show")
|
||||||
|
}}</a></code
|
||||||
|
>
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button type="submit"
|
<button
|
||||||
|
type="submit"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
:aria-label="$t('buttons.ok')"
|
:aria-label="$t('buttons.ok')"
|
||||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
:title="$t('buttons.ok')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.ok") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState, mapGetters} from 'vuex'
|
import { mapState, mapGetters } from "vuex";
|
||||||
import filesize from 'filesize'
|
import filesize from "filesize";
|
||||||
import moment from 'moment'
|
import moment from "moment";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'info',
|
name: "info",
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'selected']),
|
...mapState(["req", "selected"]),
|
||||||
...mapGetters(['selectedCount', 'isListing']),
|
...mapGetters(["selectedCount", "isListing"]),
|
||||||
humanSize: function () {
|
humanSize: function () {
|
||||||
if (this.selectedCount === 0 || !this.isListing) {
|
if (this.selectedCount === 0 || !this.isListing) {
|
||||||
return filesize(this.req.size)
|
return filesize(this.req.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sum = 0
|
let sum = 0;
|
||||||
|
|
||||||
for (let selected of this.selected) {
|
for (let selected of this.selected) {
|
||||||
sum += this.req.items[selected].size
|
sum += this.req.items[selected].size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesize(sum)
|
return filesize(sum);
|
||||||
},
|
},
|
||||||
humanTime: function () {
|
humanTime: function () {
|
||||||
if (this.selectedCount === 0) {
|
if (this.selectedCount === 0) {
|
||||||
return moment(this.req.modified).fromNow()
|
return moment(this.req.modified).fromNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
return moment(this.req.items[this.selected[0]].modified).fromNow()
|
return moment(this.req.items[this.selected[0]].modified).fromNow();
|
||||||
|
},
|
||||||
|
modTime: function () {
|
||||||
|
return new Date(Date.parse(this.req.modified)).toLocaleString();
|
||||||
},
|
},
|
||||||
name: function () {
|
name: function () {
|
||||||
return this.selectedCount === 0 ? this.req.name : this.req.items[this.selected[0]].name
|
return this.selectedCount === 0
|
||||||
|
? this.req.name
|
||||||
|
: this.req.items[this.selected[0]].name;
|
||||||
},
|
},
|
||||||
dir: function () {
|
dir: function () {
|
||||||
return this.selectedCount > 1 || (this.selectedCount === 0
|
return (
|
||||||
? this.req.isDir
|
this.selectedCount > 1 ||
|
||||||
: this.req.items[this.selected[0]].isDir)
|
(this.selectedCount === 0
|
||||||
}
|
? this.req.isDir
|
||||||
|
: this.req.items[this.selected[0]].isDir)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checksum: async function (event, algo) {
|
checksum: async function (event, algo) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
let link
|
let link;
|
||||||
|
|
||||||
if (this.selectedCount) {
|
if (this.selectedCount) {
|
||||||
link = this.req.items[this.selected[0]].url
|
link = this.req.items[this.selected[0]].url;
|
||||||
} else {
|
} else {
|
||||||
link = this.$route.path
|
link = this.$route.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hash = await api.checksum(link, algo)
|
const hash = await api.checksum(link, algo);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
event.target.innerHTML = hash
|
event.target.innerHTML = hash
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,93 +1,104 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.move') }}</h2>
|
<h2>{{ $t("prompts.move") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<file-list @update:selected="val => dest = val"></file-list>
|
<file-list @update:selected="(val) => (dest = val)"></file-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
@click="move"
|
@click="move"
|
||||||
:disabled="$route.path === dest"
|
:disabled="$route.path === dest"
|
||||||
:aria-label="$t('buttons.move')"
|
:aria-label="$t('buttons.move')"
|
||||||
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
|
:title="$t('buttons.move')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.move") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
import FileList from './FileList'
|
import FileList from "./FileList";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import buttons from '@/utils/buttons'
|
import buttons from "@/utils/buttons";
|
||||||
import * as upload from '@/utils/upload'
|
import * as upload from "@/utils/upload";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'move',
|
name: "move",
|
||||||
components: { FileList },
|
components: { FileList },
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
current: window.location.pathname,
|
current: window.location.pathname,
|
||||||
dest: null
|
dest: null,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: mapState(['req', 'selected']),
|
computed: mapState(["req", "selected"]),
|
||||||
methods: {
|
methods: {
|
||||||
move: async function (event) {
|
move: async function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
let items = []
|
let items = [];
|
||||||
|
|
||||||
for (let item of this.selected) {
|
for (let item of this.selected) {
|
||||||
items.push({
|
items.push({
|
||||||
from: this.req.items[item].url,
|
from: this.req.items[item].url,
|
||||||
to: this.dest + encodeURIComponent(this.req.items[item].name),
|
to: this.dest + encodeURIComponent(this.req.items[item].name),
|
||||||
name: this.req.items[item].name
|
name: this.req.items[item].name,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = async (overwrite, rename) => {
|
let action = async (overwrite, rename) => {
|
||||||
buttons.loading('move')
|
buttons.loading("move");
|
||||||
|
|
||||||
await api.move(items, overwrite, rename).then(() => {
|
await api
|
||||||
buttons.success('move')
|
.move(items, overwrite, rename)
|
||||||
this.$router.push({ path: this.dest })
|
.then(() => {
|
||||||
}).catch((e) => {
|
buttons.success("move");
|
||||||
buttons.done('move')
|
this.$router.push({ path: this.dest });
|
||||||
this.$showError(e)
|
})
|
||||||
})
|
.catch((e) => {
|
||||||
}
|
buttons.done("move");
|
||||||
|
this.$showError(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
let dstItems = (await api.fetch(this.dest)).items
|
let dstItems = (await api.fetch(this.dest)).items;
|
||||||
let conflict = upload.checkConflict(items, dstItems)
|
let conflict = upload.checkConflict(items, dstItems);
|
||||||
|
|
||||||
let overwrite = false
|
let overwrite = false;
|
||||||
let rename = false
|
let rename = false;
|
||||||
|
|
||||||
if (conflict) {
|
if (conflict) {
|
||||||
this.$store.commit('showHover', {
|
this.$store.commit("showHover", {
|
||||||
prompt: 'replace-rename',
|
prompt: "replace-rename",
|
||||||
confirm: (event, option) => {
|
confirm: (event, option) => {
|
||||||
overwrite = option == 'overwrite'
|
overwrite = option == "overwrite";
|
||||||
rename = option == 'rename'
|
rename = option == "rename";
|
||||||
|
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
action(overwrite, rename)
|
action(overwrite, rename);
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
action(overwrite, rename)
|
action(overwrite, rename);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.newDir') }}</h2>
|
<h2>{{ $t("prompts.newDir") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.newDirMessage') }}</p>
|
<p>{{ $t("prompts.newDirMessage") }}</p>
|
||||||
<input class="input input--block" type="text" @keyup.enter="submit" v-model.trim="name" v-focus>
|
<input
|
||||||
|
class="input input--block"
|
||||||
|
type="text"
|
||||||
|
@keyup.enter="submit"
|
||||||
|
v-model.trim="name"
|
||||||
|
v-focus
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
@@ -15,57 +21,60 @@
|
|||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')"
|
:title="$t('buttons.cancel')"
|
||||||
>{{ $t('buttons.cancel') }}</button>
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
:aria-label="$t('buttons.create')"
|
:aria-label="$t('buttons.create')"
|
||||||
:title="$t('buttons.create')"
|
:title="$t('buttons.create')"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
>{{ $t('buttons.create') }}</button>
|
>
|
||||||
|
{{ $t("buttons.create") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import url from '@/utils/url'
|
import url from "@/utils/url";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'new-dir',
|
name: "new-dir",
|
||||||
data: function() {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
name: ''
|
name: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([ 'isFiles', 'isListing' ])
|
...mapGetters(["isFiles", "isListing"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: async function(event) {
|
submit: async function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
if (this.new === '') return
|
if (this.new === "") return;
|
||||||
|
|
||||||
// Build the path of the new directory.
|
// Build the path of the new directory.
|
||||||
let uri = this.isFiles ? this.$route.path + '/' : '/'
|
let uri = this.isFiles ? this.$route.path + "/" : "/";
|
||||||
|
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
uri = url.removeLastDir(uri) + '/'
|
uri = url.removeLastDir(uri) + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
uri += encodeURIComponent(this.name) + '/'
|
uri += encodeURIComponent(this.name) + "/";
|
||||||
uri = uri.replace('//', '/')
|
uri = uri.replace("//", "/");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.post(uri)
|
await api.post(uri);
|
||||||
this.$router.push({ path: uri })
|
this.$router.push({ path: uri });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.newFile') }}</h2>
|
<h2>{{ $t("prompts.newFile") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.newFileMessage') }}</p>
|
<p>{{ $t("prompts.newFileMessage") }}</p>
|
||||||
<input class="input input--block" v-focus type="text" @keyup.enter="submit" v-model.trim="name">
|
<input
|
||||||
|
class="input input--block"
|
||||||
|
v-focus
|
||||||
|
type="text"
|
||||||
|
@keyup.enter="submit"
|
||||||
|
v-model.trim="name"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
@@ -15,57 +21,60 @@
|
|||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')"
|
:title="$t('buttons.cancel')"
|
||||||
>{{ $t('buttons.cancel') }}</button>
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
:aria-label="$t('buttons.create')"
|
:aria-label="$t('buttons.create')"
|
||||||
:title="$t('buttons.create')"
|
:title="$t('buttons.create')"
|
||||||
>{{ $t('buttons.create') }}</button>
|
>
|
||||||
|
{{ $t("buttons.create") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from "vuex";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
import url from '@/utils/url'
|
import url from "@/utils/url";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'new-file',
|
name: "new-file",
|
||||||
data: function() {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
name: ''
|
name: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([ 'isFiles', 'isListing' ])
|
...mapGetters(["isFiles", "isListing"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: async function(event) {
|
submit: async function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
if (this.new === '') return
|
if (this.new === "") return;
|
||||||
|
|
||||||
// Build the path of the new directory.
|
// Build the path of the new directory.
|
||||||
let uri = this.isFiles ? this.$route.path + '/' : '/'
|
let uri = this.isFiles ? this.$route.path + "/" : "/";
|
||||||
|
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
uri = url.removeLastDir(uri) + '/'
|
uri = url.removeLastDir(uri) + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
uri += encodeURIComponent(this.name)
|
uri += encodeURIComponent(this.name);
|
||||||
uri = uri.replace('//', '/')
|
uri = uri.replace("//", "/");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.post(uri)
|
await api.post(uri);
|
||||||
this.$router.push({ path: uri })
|
this.$router.push({ path: uri });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -6,25 +6,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Help from './Help'
|
import Help from "./Help";
|
||||||
import Info from './Info'
|
import Info from "./Info";
|
||||||
import Delete from './Delete'
|
import Delete from "./Delete";
|
||||||
import Rename from './Rename'
|
import Rename from "./Rename";
|
||||||
import Download from './Download'
|
import Download from "./Download";
|
||||||
import Move from './Move'
|
import Move from "./Move";
|
||||||
import Copy from './Copy'
|
import Copy from "./Copy";
|
||||||
import NewFile from './NewFile'
|
import NewFile from "./NewFile";
|
||||||
import NewDir from './NewDir'
|
import NewDir from "./NewDir";
|
||||||
import Replace from './Replace'
|
import Replace from "./Replace";
|
||||||
import ReplaceRename from './ReplaceRename'
|
import ReplaceRename from "./ReplaceRename";
|
||||||
import Share from './Share'
|
import Share from "./Share";
|
||||||
import Upload from './Upload'
|
import Upload from "./Upload";
|
||||||
import ShareDelete from './ShareDelete'
|
import ShareDelete from "./ShareDelete";
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
import buttons from '@/utils/buttons'
|
import buttons from "@/utils/buttons";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'prompts',
|
name: "prompts",
|
||||||
components: {
|
components: {
|
||||||
Info,
|
Info,
|
||||||
Delete,
|
Delete,
|
||||||
@@ -39,74 +39,81 @@ export default {
|
|||||||
Replace,
|
Replace,
|
||||||
ReplaceRename,
|
ReplaceRename,
|
||||||
Upload,
|
Upload,
|
||||||
ShareDelete
|
ShareDelete,
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
pluginData: {
|
pluginData: {
|
||||||
buttons,
|
buttons,
|
||||||
'store': this.$store,
|
store: this.$store,
|
||||||
'router': this.$router
|
router: this.$router,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
created () {
|
created() {
|
||||||
window.addEventListener('keydown', (event) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
if (this.show == null)
|
if (this.show == null) return;
|
||||||
return
|
|
||||||
|
|
||||||
let prompt = this.$refs.currentComponent;
|
let prompt = this.$refs.currentComponent;
|
||||||
|
|
||||||
|
// Esc!
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
}
|
||||||
|
|
||||||
// Enter
|
// Enter
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
switch (this.show) {
|
switch (this.show) {
|
||||||
case 'delete':
|
case "delete":
|
||||||
prompt.submit()
|
prompt.submit();
|
||||||
break;
|
break;
|
||||||
case 'copy':
|
case "copy":
|
||||||
prompt.copy(event)
|
prompt.copy(event);
|
||||||
break;
|
break;
|
||||||
case 'move':
|
case "move":
|
||||||
prompt.move(event)
|
prompt.move(event);
|
||||||
break;
|
break;
|
||||||
case 'replace':
|
case "replace":
|
||||||
prompt.showConfirm(event)
|
prompt.showConfirm(event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['show', 'plugins']),
|
...mapState(["show", "plugins"]),
|
||||||
currentComponent: function () {
|
currentComponent: function () {
|
||||||
const matched = [
|
const matched =
|
||||||
'info',
|
[
|
||||||
'help',
|
"info",
|
||||||
'delete',
|
"help",
|
||||||
'rename',
|
"delete",
|
||||||
'move',
|
"rename",
|
||||||
'copy',
|
"move",
|
||||||
'newFile',
|
"copy",
|
||||||
'newDir',
|
"newFile",
|
||||||
'download',
|
"newDir",
|
||||||
'replace',
|
"download",
|
||||||
'replace-rename',
|
"replace",
|
||||||
'share',
|
"replace-rename",
|
||||||
'upload',
|
"share",
|
||||||
'share-delete'
|
"upload",
|
||||||
].indexOf(this.show) >= 0;
|
"share-delete",
|
||||||
|
].indexOf(this.show) >= 0;
|
||||||
|
|
||||||
return matched && this.show || null;
|
return (matched && this.show) || null;
|
||||||
},
|
},
|
||||||
showOverlay: function () {
|
showOverlay: function () {
|
||||||
return (this.show !== null && this.show !== 'search' && this.show !== 'more')
|
return (
|
||||||
}
|
this.show !== null && this.show !== "search" && this.show !== "more"
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetPrompts () {
|
resetPrompts() {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,89 +1,107 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.rename') }}</h2>
|
<h2>{{ $t("prompts.rename") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
|
<p>
|
||||||
<input class="input input--block" v-focus type="text" @keyup.enter="submit" v-model.trim="name">
|
{{ $t("prompts.renameMessage") }} <code>{{ oldName() }}</code
|
||||||
|
>:
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
class="input input--block"
|
||||||
|
v-focus
|
||||||
|
type="text"
|
||||||
|
@keyup.enter="submit"
|
||||||
|
v-model.trim="name"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button @click="submit"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="submit"
|
||||||
class="button button--flat"
|
class="button button--flat"
|
||||||
type="submit"
|
type="submit"
|
||||||
:aria-label="$t('buttons.rename')"
|
:aria-label="$t('buttons.rename')"
|
||||||
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
|
:title="$t('buttons.rename')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.rename") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from "vuex";
|
||||||
import url from '@/utils/url'
|
import url from "@/utils/url";
|
||||||
import { files as api } from '@/api'
|
import { files as api } from "@/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'rename',
|
name: "rename",
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
name: ''
|
name: "",
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
created () {
|
created() {
|
||||||
this.name = this.oldName()
|
this.name = this.oldName();
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['req', 'selected', 'selectedCount']),
|
...mapState(["req", "selected", "selectedCount"]),
|
||||||
...mapGetters(['isListing'])
|
...mapGetters(["isListing"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cancel: function () {
|
cancel: function () {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
},
|
},
|
||||||
oldName: function () {
|
oldName: function () {
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
return this.req.name
|
return this.req.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
||||||
// This shouldn't happen.
|
// This shouldn't happen.
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.req.items[this.selected[0]].name
|
return this.req.items[this.selected[0]].name;
|
||||||
},
|
},
|
||||||
submit: async function () {
|
submit: async function () {
|
||||||
let oldLink = ''
|
let oldLink = "";
|
||||||
let newLink = ''
|
let newLink = "";
|
||||||
|
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
oldLink = this.req.url
|
oldLink = this.req.url;
|
||||||
} else {
|
} else {
|
||||||
oldLink = this.req.items[this.selected[0]].url
|
oldLink = this.req.items[this.selected[0]].url;
|
||||||
}
|
}
|
||||||
|
|
||||||
newLink = url.removeLastDir(oldLink) + '/' + encodeURIComponent(this.name)
|
newLink =
|
||||||
|
url.removeLastDir(oldLink) + "/" + encodeURIComponent(this.name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.move([{ from: oldLink, to: newLink }])
|
await api.move([{ from: oldLink, to: newLink }]);
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
this.$router.push({ path: newLink })
|
this.$router.push({ path: newLink });
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('setReload', true)
|
this.$store.commit("setReload", true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,31 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.replace') }}</h2>
|
<h2>{{ $t("prompts.replace") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.replaceMessage') }}</p>
|
<p>{{ $t("prompts.replaceMessage") }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat button--red"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat button--red"
|
||||||
@click="showConfirm"
|
@click="showConfirm"
|
||||||
:aria-label="$t('buttons.replace')"
|
:aria-label="$t('buttons.replace')"
|
||||||
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
|
:title="$t('buttons.replace')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.replace") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'replace',
|
name: "replace",
|
||||||
computed: mapState(['showConfirm'])
|
computed: mapState(["showConfirm"]),
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,35 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.replace') }}</h2>
|
<h2>{{ $t("prompts.replace") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.replaceMessage') }}</p>
|
<p>{{ $t("prompts.replaceMessage") }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat button--blue"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat button--blue"
|
||||||
@click="(event) => showConfirm(event, 'rename')"
|
@click="(event) => showConfirm(event, 'rename')"
|
||||||
:aria-label="$t('buttons.rename')"
|
:aria-label="$t('buttons.rename')"
|
||||||
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
|
:title="$t('buttons.rename')"
|
||||||
<button class="button button--flat button--red"
|
>
|
||||||
|
{{ $t("buttons.rename") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat button--red"
|
||||||
@click="(event) => showConfirm(event, 'overwrite')"
|
@click="(event) => showConfirm(event, 'overwrite')"
|
||||||
:aria-label="$t('buttons.replace')"
|
:aria-label="$t('buttons.replace')"
|
||||||
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
|
:title="$t('buttons.replace')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.replace") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'replace-rename',
|
name: "replace-rename",
|
||||||
computed: mapState(['showConfirm'])
|
computed: mapState(["showConfirm"]),
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating share__promt__card" id="share">
|
<div class="card floating share__promt__card" id="share">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('buttons.share') }}</h2>
|
<h2>{{ $t("buttons.share") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="listing">
|
<template v-if="listing">
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>{{ $t('settings.shareDuration') }}</th>
|
<th>{{ $t("settings.shareDuration") }}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -17,188 +17,218 @@
|
|||||||
<tr v-for="link in links" :key="link.hash">
|
<tr v-for="link in links" :key="link.hash">
|
||||||
<td>{{ link.hash }}</td>
|
<td>{{ link.hash }}</td>
|
||||||
<td>
|
<td>
|
||||||
<template v-if="link.expire !== 0">{{ humanTime(link.expire) }}</template>
|
<template v-if="link.expire !== 0">{{
|
||||||
<template v-else>{{ $t('permanent') }}</template>
|
humanTime(link.expire)
|
||||||
|
}}</template>
|
||||||
|
<template v-else>{{ $t("permanent") }}</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="small">
|
<td class="small">
|
||||||
<button class="action copy-clipboard"
|
<button
|
||||||
:data-clipboard-text="buildLink(link.hash)"
|
class="action copy-clipboard"
|
||||||
|
:data-clipboard-text="buildLink(link)"
|
||||||
:aria-label="$t('buttons.copyToClipboard')"
|
:aria-label="$t('buttons.copyToClipboard')"
|
||||||
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
|
:title="$t('buttons.copyToClipboard')"
|
||||||
|
>
|
||||||
|
<i class="material-icons">content_paste</i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td class="small">
|
<td class="small">
|
||||||
<button class="action"
|
<button
|
||||||
|
class="action"
|
||||||
@click="deleteLink($event, link)"
|
@click="deleteLink($event, link)"
|
||||||
:aria-label="$t('buttons.delete')"
|
:aria-label="$t('buttons.delete')"
|
||||||
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
|
:title="$t('buttons.delete')"
|
||||||
|
>
|
||||||
|
<i class="material-icons">delete</i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
:aria-label="$t('buttons.close')"
|
:aria-label="$t('buttons.close')"
|
||||||
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
|
:title="$t('buttons.close')"
|
||||||
<button class="button button--flat button--blue"
|
>
|
||||||
|
{{ $t("buttons.close") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat button--blue"
|
||||||
@click="() => switchListing()"
|
@click="() => switchListing()"
|
||||||
:aria-label="$t('buttons.new')"
|
:aria-label="$t('buttons.new')"
|
||||||
:title="$t('buttons.new')">{{ $t('buttons.new') }}</button>
|
:title="$t('buttons.new')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.new") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('settings.shareDuration') }}</p>
|
<p>{{ $t("settings.shareDuration") }}</p>
|
||||||
<div class="input-group input">
|
<div class="input-group input">
|
||||||
<input v-focus
|
<input
|
||||||
type="number"
|
v-focus
|
||||||
max="2147483647"
|
type="number"
|
||||||
min="1"
|
max="2147483647"
|
||||||
@keyup.enter="submit"
|
min="1"
|
||||||
v-model.trim="time">
|
@keyup.enter="submit"
|
||||||
<select class="right" v-model="unit" :aria-label="$t('time.unit')">
|
v-model.trim="time"
|
||||||
<option value="seconds">{{ $t('time.seconds') }}</option>
|
/>
|
||||||
<option value="minutes">{{ $t('time.minutes') }}</option>
|
<select class="right" v-model="unit" :aria-label="$t('time.unit')">
|
||||||
<option value="hours">{{ $t('time.hours') }}</option>
|
<option value="seconds">{{ $t("time.seconds") }}</option>
|
||||||
<option value="days">{{ $t('time.days') }}</option>
|
<option value="minutes">{{ $t("time.minutes") }}</option>
|
||||||
</select>
|
<option value="hours">{{ $t("time.hours") }}</option>
|
||||||
|
<option value="days">{{ $t("time.days") }}</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<p>{{ $t('prompts.optionalPassword') }}</p>
|
<p>{{ $t("prompts.optionalPassword") }}</p>
|
||||||
<input class="input input--block" type="password" v-model.trim="password">
|
<input
|
||||||
|
class="input input--block"
|
||||||
|
type="password"
|
||||||
|
v-model.trim="password"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button class="button button--flat button--grey"
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
@click="() => switchListing()"
|
@click="() => switchListing()"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button class="button button--flat button--blue"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat button--blue"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
:aria-label="$t('buttons.share')"
|
:aria-label="$t('buttons.share')"
|
||||||
:title="$t('buttons.share')">{{ $t('buttons.share') }}</button>
|
:title="$t('buttons.share')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.share") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from "vuex";
|
||||||
import { share as api } from '@/api'
|
import { share as api } from "@/api";
|
||||||
import { baseURL } from '@/utils/constants'
|
import moment from "moment";
|
||||||
import moment from 'moment'
|
import Clipboard from "clipboard";
|
||||||
import Clipboard from 'clipboard'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'share',
|
name: "share",
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
time: '',
|
time: "",
|
||||||
unit: 'hours',
|
unit: "hours",
|
||||||
links: [],
|
links: [],
|
||||||
clip: null,
|
clip: null,
|
||||||
password: '',
|
password: "",
|
||||||
listing: true
|
listing: true,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([ 'req', 'selected', 'selectedCount' ]),
|
...mapState(["req", "selected", "selectedCount"]),
|
||||||
...mapGetters([ 'isListing' ]),
|
...mapGetters(["isListing"]),
|
||||||
url () {
|
url() {
|
||||||
if (!this.isListing) {
|
if (!this.isListing) {
|
||||||
return this.$route.path
|
return this.$route.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
||||||
// This shouldn't happen.
|
// This shouldn't happen.
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.req.items[this.selected[0]].url
|
return this.req.items[this.selected[0]].url;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
async beforeMount () {
|
async beforeMount() {
|
||||||
try {
|
try {
|
||||||
const links = await api.get(this.url)
|
const links = await api.get(this.url);
|
||||||
this.links = links
|
this.links = links;
|
||||||
this.sort()
|
this.sort();
|
||||||
|
|
||||||
if (this.links.length == 0) {
|
if (this.links.length == 0) {
|
||||||
this.listing = false
|
this.listing = false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted() {
|
||||||
this.clip = new Clipboard('.copy-clipboard')
|
this.clip = new Clipboard(".copy-clipboard");
|
||||||
this.clip.on('success', () => {
|
this.clip.on("success", () => {
|
||||||
this.$showSuccess(this.$t('success.linkCopied'))
|
this.$showSuccess(this.$t("success.linkCopied"));
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy() {
|
||||||
this.clip.destroy()
|
this.clip.destroy();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: async function () {
|
submit: async function () {
|
||||||
let isPermanent = !this.time || this.time == 0
|
let isPermanent = !this.time || this.time == 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let res = null
|
let res = null;
|
||||||
|
|
||||||
if (isPermanent) {
|
if (isPermanent) {
|
||||||
res = await api.create(this.url, this.password)
|
res = await api.create(this.url, this.password);
|
||||||
} else {
|
} else {
|
||||||
res = await api.create(this.url, this.password, this.time, this.unit)
|
res = await api.create(this.url, this.password, this.time, this.unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.links.push(res)
|
this.links.push(res);
|
||||||
this.sort()
|
this.sort();
|
||||||
|
|
||||||
this.time = ''
|
this.time = "";
|
||||||
this.unit = 'hours'
|
this.unit = "hours";
|
||||||
this.password = ''
|
this.password = "";
|
||||||
|
|
||||||
this.listing = true
|
this.listing = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteLink: async function (event, link) {
|
deleteLink: async function (event, link) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
try {
|
try {
|
||||||
await api.remove(link.hash)
|
await api.remove(link.hash);
|
||||||
this.links = this.links.filter(item => item.hash !== link.hash)
|
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||||
|
|
||||||
if (this.links.length == 0) {
|
if (this.links.length == 0) {
|
||||||
this.listing = false
|
this.listing = false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e)
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
humanTime (time) {
|
humanTime(time) {
|
||||||
return moment(time * 1000).fromNow()
|
return moment(time * 1000).fromNow();
|
||||||
},
|
},
|
||||||
buildLink (hash) {
|
buildLink(share) {
|
||||||
return `${window.location.origin}${baseURL}/share/${hash}`
|
return api.getShareURL(share);
|
||||||
},
|
},
|
||||||
sort () {
|
sort() {
|
||||||
this.links = this.links.sort((a, b) => {
|
this.links = this.links.sort((a, b) => {
|
||||||
if (a.expire === 0) return -1
|
if (a.expire === 0) return -1;
|
||||||
if (b.expire === 0) return 1
|
if (b.expire === 0) return 1;
|
||||||
return new Date(a.expire) - new Date(b.expire)
|
return new Date(a.expire) - new Date(b.expire);
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
switchListing () {
|
switchListing() {
|
||||||
if (this.links.length == 0 && !this.listing) {
|
if (this.links.length == 0 && !this.listing) {
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit("closeHovers");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listing = !this.listing
|
this.listing = !this.listing;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.deleteMessageShare', {path: ''}) }}</p>
|
<p>{{ $t("prompts.deleteMessageShare", { path: "" }) }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button @click="$store.commit('closeHovers')"
|
<button
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
class="button button--flat button--grey"
|
class="button button--flat button--grey"
|
||||||
:aria-label="$t('buttons.cancel')"
|
:aria-label="$t('buttons.cancel')"
|
||||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
:title="$t('buttons.cancel')"
|
||||||
<button @click="submit"
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="submit"
|
||||||
class="button button--flat button--red"
|
class="button button--flat button--red"
|
||||||
:aria-label="$t('buttons.delete')"
|
:aria-label="$t('buttons.delete')"
|
||||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
:title="$t('buttons.delete')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.delete") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from 'vuex'
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'share-delete',
|
name: "share-delete",
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['showConfirm'])
|
...mapState(["showConfirm"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: function () {
|
submit: function () {
|
||||||
this.showConfirm()
|
this.showConfirm();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,39 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t('prompts.upload') }}</h2>
|
<h2>{{ $t("prompts.upload") }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t('prompts.uploadMessage') }}</p>
|
<p>{{ $t("prompts.uploadMessage") }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action full">
|
<div class="card-action full">
|
||||||
<div @click="uploadFile" class="action">
|
<div @click="uploadFile" class="action">
|
||||||
<i class="material-icons">insert_drive_file</i>
|
<i class="material-icons">insert_drive_file</i>
|
||||||
<div class="title">File</div>
|
<div class="title">{{ $t("buttons.file") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div @click="uploadFolder" class="action">
|
<div @click="uploadFolder" class="action">
|
||||||
<i class="material-icons">folder</i>
|
<i class="material-icons">folder</i>
|
||||||
<div class="title">Folder</div>
|
<div class="title">{{ $t("buttons.folder") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'upload',
|
name: "upload",
|
||||||
methods: {
|
methods: {
|
||||||
uploadFile: function () {
|
uploadFile: function () {
|
||||||
document.getElementById('upload-input').value = ''
|
document.getElementById("upload-input").value = "";
|
||||||
document.getElementById('upload-input').click()
|
document.getElementById("upload-input").click();
|
||||||
},
|
},
|
||||||
uploadFolder: function () {
|
uploadFolder: function () {
|
||||||
document.getElementById('upload-folder-input').value = ''
|
document.getElementById("upload-folder-input").value = "";
|
||||||
document.getElementById('upload-folder-input').click()
|
document.getElementById("upload-folder-input").click();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
63
frontend/src/components/prompts/UploadFiles.vue
Normal file
63
frontend/src/components/prompts/UploadFiles.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="filesInUploadCount > 0"
|
||||||
|
class="upload-files"
|
||||||
|
v-bind:class="{ closed: !open }"
|
||||||
|
>
|
||||||
|
<div class="card floating">
|
||||||
|
<div class="card-title">
|
||||||
|
<h2>{{ $t("prompts.uploadFiles", { files: filesInUploadCount }) }}</h2>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="action"
|
||||||
|
@click="toggle"
|
||||||
|
aria-label="Toggle file upload list"
|
||||||
|
title="Toggle file upload list"
|
||||||
|
>
|
||||||
|
<i class="material-icons">{{
|
||||||
|
open ? "keyboard_arrow_down" : "keyboard_arrow_up"
|
||||||
|
}}</i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-content file-icons">
|
||||||
|
<div
|
||||||
|
class="file"
|
||||||
|
v-for="file in filesInUpload"
|
||||||
|
:key="file.id"
|
||||||
|
:data-dir="file.isDir"
|
||||||
|
:data-type="file.type"
|
||||||
|
:aria-label="file.name"
|
||||||
|
>
|
||||||
|
<div class="file-name">
|
||||||
|
<i class="material-icons"></i> {{ file.name }}
|
||||||
|
</div>
|
||||||
|
<div class="file-progress">
|
||||||
|
<div v-bind:style="{ width: file.progress + '%' }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "uploadFiles",
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["filesInUpload", "filesInUploadCount"]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggle: function () {
|
||||||
|
this.open = !this.open;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ $t('settings.userCommands') }}</h3>
|
<h3>{{ $t("settings.userCommands") }}</h3>
|
||||||
<p class="small">{{ $t('settings.userCommandsHelp') }} <i>git svn hg</i>.</p>
|
<p class="small">
|
||||||
<input class="input input--block" type="text" v-model.trim="raw">
|
{{ $t("settings.userCommandsHelp") }} <i>git svn hg</i>.
|
||||||
|
</p>
|
||||||
|
<input class="input input--block" type="text" v-model.trim="raw" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'permissions',
|
name: "permissions",
|
||||||
props: ['commands'],
|
props: ["commands"],
|
||||||
computed: {
|
computed: {
|
||||||
raw: {
|
raw: {
|
||||||
get () {
|
get() {
|
||||||
return this.commands.join(' ')
|
return this.commands.join(" ");
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
if (value !== '') {
|
if (value !== "") {
|
||||||
this.$emit('update:commands', value.split(' '))
|
this.$emit("update:commands", value.split(" "));
|
||||||
} else {
|
} else {
|
||||||
this.$emit('update:commands', [])
|
this.$emit("update:commands", []);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,46 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-on:change="change" :value="locale">
|
<select v-on:change="change" :value="locale">
|
||||||
<option v-for="(language, value) in locales" :key="value" :value="value">{{ $t('languages.' + language) }}</option>
|
<option v-for="(language, value) in locales" :key="value" :value="value">
|
||||||
|
{{ $t("languages." + language) }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'languages',
|
name: "languages",
|
||||||
props: [ 'locale' ],
|
props: ["locale"],
|
||||||
data() {
|
data() {
|
||||||
let dataObj = {
|
let dataObj = {
|
||||||
locales: {
|
locales: {
|
||||||
ar: 'ar',
|
ar: "ar",
|
||||||
de: 'de',
|
de: "de",
|
||||||
en: 'en',
|
en: "en",
|
||||||
es: 'es',
|
es: "es",
|
||||||
fr: 'fr',
|
fr: "fr",
|
||||||
is: 'is',
|
is: "is",
|
||||||
it: 'it',
|
it: "it",
|
||||||
ja: 'ja',
|
ja: "ja",
|
||||||
ko: 'ko',
|
ko: "ko",
|
||||||
'nl-be': 'nlBE',
|
"nl-be": "nlBE",
|
||||||
pl: 'pl',
|
pl: "pl",
|
||||||
'pt-br': 'ptBR',
|
"pt-br": "ptBR",
|
||||||
pt: 'pt',
|
pt: "pt",
|
||||||
ro: 'ro',
|
ro: "ro",
|
||||||
ru: 'ru',
|
ru: "ru",
|
||||||
'sv-se': 'svSE',
|
sk: "sk",
|
||||||
'zh-cn': 'zhCN',
|
"sv-se": "svSE",
|
||||||
'zh-tw': 'zhTW'
|
tr: "tr",
|
||||||
}
|
ua: "ua",
|
||||||
|
"zh-cn": "zhCN",
|
||||||
|
"zh-tw": "zhTW",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperty(dataObj, "locales", { configurable: false, writable: false });
|
Object.defineProperty(dataObj, "locales", {
|
||||||
|
configurable: false,
|
||||||
|
writable: false,
|
||||||
|
});
|
||||||
|
|
||||||
return dataObj;
|
return dataObj;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change (event) {
|
change(event) {
|
||||||
this.$emit('update:locale', event.target.value)
|
this.$emit("update:locale", event.target.value);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,41 +1,65 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ $t('settings.permissions') }}</h3>
|
<h3>{{ $t("settings.permissions") }}</h3>
|
||||||
<p class="small">{{ $t('settings.permissionsHelp') }}</p>
|
<p class="small">{{ $t("settings.permissionsHelp") }}</p>
|
||||||
|
|
||||||
<p><input type="checkbox" v-model="admin"> {{ $t('settings.administrator') }}</p>
|
<p>
|
||||||
|
<input type="checkbox" v-model="admin" />
|
||||||
|
{{ $t("settings.administrator") }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.create"> {{ $t('settings.perm.create') }}</p>
|
<p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.delete"> {{ $t('settings.perm.delete') }}</p>
|
<input type="checkbox" :disabled="admin" v-model="perm.create" />
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.download"> {{ $t('settings.perm.download') }}</p>
|
{{ $t("settings.perm.create") }}
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.modify"> {{ $t('settings.perm.modify') }}</p>
|
</p>
|
||||||
<p v-if="isExecEnabled"><input type="checkbox" :disabled="admin" v-model="perm.execute"> {{ $t('settings.perm.execute') }}</p>
|
<p>
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.rename"> {{ $t('settings.perm.rename') }}</p>
|
<input type="checkbox" :disabled="admin" v-model="perm.delete" />
|
||||||
<p><input type="checkbox" :disabled="admin" v-model="perm.share"> {{ $t('settings.perm.share') }}</p>
|
{{ $t("settings.perm.delete") }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="checkbox" :disabled="admin" v-model="perm.download" />
|
||||||
|
{{ $t("settings.perm.download") }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="checkbox" :disabled="admin" v-model="perm.modify" />
|
||||||
|
{{ $t("settings.perm.modify") }}
|
||||||
|
</p>
|
||||||
|
<p v-if="isExecEnabled">
|
||||||
|
<input type="checkbox" :disabled="admin" v-model="perm.execute" />
|
||||||
|
{{ $t("settings.perm.execute") }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="checkbox" :disabled="admin" v-model="perm.rename" />
|
||||||
|
{{ $t("settings.perm.rename") }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="checkbox" :disabled="admin" v-model="perm.share" />
|
||||||
|
{{ $t("settings.perm.share") }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { enableExec } from '@/utils/constants'
|
import { enableExec } from "@/utils/constants";
|
||||||
export default {
|
export default {
|
||||||
name: 'permissions',
|
name: "permissions",
|
||||||
props: ['perm'],
|
props: ["perm"],
|
||||||
computed: {
|
computed: {
|
||||||
admin: {
|
admin: {
|
||||||
get () {
|
get() {
|
||||||
return this.perm.admin
|
return this.perm.admin;
|
||||||
},
|
},
|
||||||
set (value) {
|
set(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
for (const key in this.perm) {
|
for (const key in this.perm) {
|
||||||
this.perm[key] = true
|
this.perm[key] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.perm.admin = value
|
this.perm.admin = value;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
isExecEnabled: () => enableExec
|
isExecEnabled: () => enableExec,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,57 +1,63 @@
|
|||||||
<template>
|
<template>
|
||||||
<form class="rules small">
|
<form class="rules small">
|
||||||
<div v-for="(rule, index) in rules" :key="index">
|
<div v-for="(rule, index) in rules" :key="index">
|
||||||
<input type="checkbox" v-model="rule.regex"><label>Regex</label>
|
<input type="checkbox" v-model="rule.regex" /><label>Regex</label>
|
||||||
<input type="checkbox" v-model="rule.allow"><label>Allow</label>
|
<input type="checkbox" v-model="rule.allow" /><label>Allow</label>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@keypress.enter.prevent
|
@keypress.enter.prevent
|
||||||
type="text"
|
type="text"
|
||||||
v-if="rule.regex"
|
v-if="rule.regex"
|
||||||
v-model="rule.regexp.raw"
|
v-model="rule.regexp.raw"
|
||||||
:placeholder="$t('settings.insertRegex')" />
|
:placeholder="$t('settings.insertRegex')"
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
@keypress.enter.prevent
|
@keypress.enter.prevent
|
||||||
type="text"
|
type="text"
|
||||||
v-else
|
v-else
|
||||||
v-model="rule.path"
|
v-model="rule.path"
|
||||||
:placeholder="$t('settings.insertPath')" />
|
:placeholder="$t('settings.insertPath')"
|
||||||
|
/>
|
||||||
|
|
||||||
<button class="button button--red" @click="remove($event, index)">-</button>
|
<button class="button button--red" @click="remove($event, index)">
|
||||||
|
-
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="button" @click="create" default="false">{{ $t('buttons.new') }}</button>
|
<button class="button" @click="create" default="false">
|
||||||
|
{{ $t("buttons.new") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'rules-textarea',
|
name: "rules-textarea",
|
||||||
props: ['rules'],
|
props: ["rules"],
|
||||||
methods: {
|
methods: {
|
||||||
remove (event, index) {
|
remove(event, index) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
let rules = [ ...this.rules ]
|
let rules = [...this.rules];
|
||||||
rules.splice(index, 1)
|
rules.splice(index, 1);
|
||||||
this.$emit('update:rules', [ ...rules ])
|
this.$emit("update:rules", [...rules]);
|
||||||
},
|
},
|
||||||
create (event) {
|
create(event) {
|
||||||
event.preventDefault()
|
event.preventDefault();
|
||||||
|
|
||||||
this.$emit('update:rules', [
|
this.$emit("update:rules", [
|
||||||
...this.rules,
|
...this.rules,
|
||||||
{
|
{
|
||||||
allow: true,
|
allow: true,
|
||||||
path: '',
|
path: "",
|
||||||
regex: false,
|
regex: false,
|
||||||
regexp: {
|
regexp: {
|
||||||
raw: ''
|
raw: "",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
])
|
]);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<select v-on:change="change" :value="theme">
|
<select v-on:change="change" :value="theme">
|
||||||
<option value="">{{ $t('settings.themes.light') }}</option>
|
<option value="">{{ $t("settings.themes.light") }}</option>
|
||||||
<option value="dark">{{ $t('settings.themes.dark') }}</option>
|
<option value="dark">{{ $t("settings.themes.dark") }}</option>
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'themes',
|
name: "themes",
|
||||||
props: [ 'theme' ],
|
props: ["theme"],
|
||||||
methods: {
|
methods: {
|
||||||
change (event) {
|
change(event) {
|
||||||
this.$emit('update:theme', event.target.value)
|
this.$emit("update:theme", event.target.value);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,67 +1,119 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<p v-if="!isDefault">
|
<p v-if="!isDefault">
|
||||||
<label for="username">{{ $t('settings.username') }}</label>
|
<label for="username">{{ $t("settings.username") }}</label>
|
||||||
<input class="input input--block" type="text" v-model="user.username" id="username">
|
<input
|
||||||
|
class="input input--block"
|
||||||
|
type="text"
|
||||||
|
v-model="user.username"
|
||||||
|
id="username"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p v-if="!isDefault">
|
<p v-if="!isDefault">
|
||||||
<label for="password">{{ $t('settings.password') }}</label>
|
<label for="password">{{ $t("settings.password") }}</label>
|
||||||
<input class="input input--block" type="password" :placeholder="passwordPlaceholder" v-model="user.password" id="password">
|
<input
|
||||||
|
class="input input--block"
|
||||||
|
type="password"
|
||||||
|
:placeholder="passwordPlaceholder"
|
||||||
|
v-model="user.password"
|
||||||
|
id="password"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="scope">{{ $t('settings.scope') }}</label>
|
<label for="scope">{{ $t("settings.scope") }}</label>
|
||||||
<input class="input input--block" type="text" v-model="user.scope" id="scope">
|
<input
|
||||||
|
:disabled="createUserDirData"
|
||||||
|
:placeholder="scopePlaceholder"
|
||||||
|
class="input input--block"
|
||||||
|
type="text"
|
||||||
|
v-model="user.scope"
|
||||||
|
id="scope"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p class="small" v-if="displayHomeDirectoryCheckbox">
|
||||||
|
<input type="checkbox" v-model="createUserDirData" />
|
||||||
|
{{ $t("settings.createUserHomeDirectory") }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="locale">{{ $t('settings.language') }}</label>
|
<label for="locale">{{ $t("settings.language") }}</label>
|
||||||
<languages class="input input--block" id="locale" :locale.sync="user.locale"></languages>
|
<languages
|
||||||
|
class="input input--block"
|
||||||
|
id="locale"
|
||||||
|
:locale.sync="user.locale"
|
||||||
|
></languages>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p v-if="!isDefault">
|
<p v-if="!isDefault">
|
||||||
<input type="checkbox" :disabled="user.perm.admin" v-model="user.lockPassword"> {{ $t('settings.lockPassword') }}
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:disabled="user.perm.admin"
|
||||||
|
v-model="user.lockPassword"
|
||||||
|
/>
|
||||||
|
{{ $t("settings.lockPassword") }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<permissions :perm.sync="user.perm" />
|
<permissions :perm.sync="user.perm" />
|
||||||
<commands v-if="isExecEnabled" :commands.sync="user.commands" />
|
<commands v-if="isExecEnabled" :commands.sync="user.commands" />
|
||||||
|
|
||||||
<div v-if="!isDefault">
|
<div v-if="!isDefault">
|
||||||
<h3>{{ $t('settings.rules') }}</h3>
|
<h3>{{ $t("settings.rules") }}</h3>
|
||||||
<p class="small">{{ $t('settings.rulesHelp') }}</p>
|
<p class="small">{{ $t("settings.rulesHelp") }}</p>
|
||||||
<rules :rules.sync="user.rules" />
|
<rules :rules.sync="user.rules" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Languages from './Languages'
|
import Languages from "./Languages";
|
||||||
import Rules from './Rules'
|
import Rules from "./Rules";
|
||||||
import Permissions from './Permissions'
|
import Permissions from "./Permissions";
|
||||||
import Commands from './Commands'
|
import Commands from "./Commands";
|
||||||
import { enableExec } from '@/utils/constants'
|
import { enableExec } from "@/utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'user',
|
name: "user",
|
||||||
|
data: () => {
|
||||||
|
return {
|
||||||
|
createUserDirData: false,
|
||||||
|
originalUserScope: "/",
|
||||||
|
};
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Permissions,
|
Permissions,
|
||||||
Languages,
|
Languages,
|
||||||
Rules,
|
Rules,
|
||||||
Commands
|
Commands,
|
||||||
|
},
|
||||||
|
props: ["user", "createUserDir", "isNew", "isDefault"],
|
||||||
|
created() {
|
||||||
|
this.originalUserScope = this.user.scope;
|
||||||
|
this.createUserDirData = this.createUserDir;
|
||||||
},
|
},
|
||||||
props: [ 'user', 'isNew', 'isDefault' ],
|
|
||||||
computed: {
|
computed: {
|
||||||
passwordPlaceholder () {
|
passwordPlaceholder() {
|
||||||
return this.isNew ? '' : this.$t('settings.avoidChanges')
|
return this.isNew ? "" : this.$t("settings.avoidChanges");
|
||||||
},
|
},
|
||||||
isExecEnabled: () => enableExec
|
scopePlaceholder() {
|
||||||
|
return this.createUserDir
|
||||||
|
? this.$t("settings.userScopeGenerationPlaceholder")
|
||||||
|
: "";
|
||||||
|
},
|
||||||
|
displayHomeDirectoryCheckbox() {
|
||||||
|
return this.isNew && this.createUserDir;
|
||||||
|
},
|
||||||
|
isExecEnabled: () => enableExec,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'user.perm.admin': function () {
|
"user.perm.admin": function () {
|
||||||
if (!this.user.perm.admin) return
|
if (!this.user.perm.admin) return;
|
||||||
this.user.lockPassword = false
|
this.user.lockPassword = false;
|
||||||
}
|
},
|
||||||
}
|
createUserDirData() {
|
||||||
}
|
this.user.scope = this.createUserDirData ? "" : this.originalUserScope;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.share__box__info {
|
.share__box__info {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 18em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.share__box__element {
|
.share__box__element {
|
||||||
@@ -43,6 +43,15 @@
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.share__box__element .button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share__box__element .button i {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.share__box__items {
|
.share__box__items {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
flex: 10 0 25em;
|
flex: 10 0 25em;
|
||||||
|
|||||||
@@ -4,4 +4,11 @@
|
|||||||
--red: #F44336;
|
--red: #F44336;
|
||||||
--dark-red: #D32F2F;
|
--dark-red: #D32F2F;
|
||||||
--moon-grey: #f2f2f2;
|
--moon-grey: #f2f2f2;
|
||||||
|
|
||||||
|
--icon-red: #da4453;
|
||||||
|
--icon-orange: #f47750;
|
||||||
|
--icon-yellow: #fdbc4b;
|
||||||
|
--icon-green: #2ecc71;
|
||||||
|
--icon-blue: #1d99f3;
|
||||||
|
--icon-violet: #9b59b6;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
padding-top: 4em;
|
padding-top: 4em;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
@@ -13,7 +13,7 @@ body {
|
|||||||
*:hover,
|
*:hover,
|
||||||
*:active,
|
*:active,
|
||||||
*:focus {
|
*:focus {
|
||||||
outline: 0
|
outline: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -44,7 +44,7 @@ i.spin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
transition: .2s ease padding;
|
transition: 0.2s ease padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app.multiple {
|
#app.multiple {
|
||||||
@@ -63,17 +63,17 @@ nav .action {
|
|||||||
display: block;
|
display: block;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
padding: .5em;
|
padding: 0.5em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav>div {
|
nav > div {
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .action>* {
|
nav .action > * {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,19 +97,25 @@ main {
|
|||||||
|
|
||||||
.breadcrumbs a {
|
.breadcrumbs a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
transition: .1s ease-in;
|
transition: 0.1s ease-in;
|
||||||
border-radius: .125em;
|
border-radius: 0.125em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs a:hover {
|
.breadcrumbs a:hover {
|
||||||
background-color: rgba(0,0,0, 0.05);
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumbs span a {
|
.breadcrumbs span a {
|
||||||
padding: .2em;
|
padding: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress {
|
.files {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -118,11 +124,11 @@ main {
|
|||||||
z-index: 9999999999;
|
z-index: 9999999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress div {
|
.progress div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #40c4ff;
|
background-color: #40c4ff;
|
||||||
width: 0;
|
width: 0;
|
||||||
transition: .2s ease width;
|
transition: 0.2s ease width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.break-word {
|
.break-word {
|
||||||
|
|||||||
@@ -110,4 +110,64 @@
|
|||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "~material-design-icons/iconfont/material-icons.css";
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-cyrillic-ext.woff2) format('woff2');
|
||||||
|
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-cyrillic.woff2) format('woff2');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-greek-ext.woff2) format('woff2');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-greek.woff2) format('woff2');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-vietnamese.woff2) format('woff2');
|
||||||
|
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-latin-ext.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-latin.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import '~material-icons/iconfont/filled.css';
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
@@ -84,7 +84,8 @@ header .menu-button {
|
|||||||
#search #input {
|
#search #input {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0.75em;
|
height: 100%;
|
||||||
|
padding: 0em 0.75em;
|
||||||
border-radius: 0.3em;
|
border-radius: 0.3em;
|
||||||
transition: .1s ease all;
|
transition: .1s ease all;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
205
frontend/src/css/listing-icons.css
Normal file
205
frontend/src/css/listing-icons.css
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/* Icons */
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
|
||||||
|
.file-icons [aria-label^="."] { opacity: 0.33 }
|
||||||
|
.file-icons [aria-label$=".bak"] { opacity: 0.33 }
|
||||||
|
|
||||||
|
.file-icons [data-type=audio] i::before { content: 'volume_up' }
|
||||||
|
.file-icons [data-type=blob] i::before { content: 'insert_drive_file' }
|
||||||
|
.file-icons [data-type=image] i::before { content: 'image' }
|
||||||
|
.file-icons [data-type=pdf] i::before { content: 'description' }
|
||||||
|
.file-icons [data-type=text] i::before { content: 'description' }
|
||||||
|
.file-icons [data-type=video] i::before { content: 'movie' }
|
||||||
|
.file-icons [data-type=invalid_link] i::before { content: 'link_off' }
|
||||||
|
|
||||||
|
/* #f90 - Image */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".ai"] i::before,
|
||||||
|
.file-icons [aria-label$=".odg"] i::before,
|
||||||
|
.file-icons [aria-label$=".xcf"] i::before
|
||||||
|
{ content: 'image' }
|
||||||
|
|
||||||
|
/* #f90 - Presentation */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".odp"] i::before,
|
||||||
|
.file-icons [aria-label$=".ppt"] i::before,
|
||||||
|
.file-icons [aria-label$=".pptx"] i::before
|
||||||
|
{ content: 'slideshow' }
|
||||||
|
|
||||||
|
/* #0f0 - Spreadsheet/Database */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".csv"] i::before,
|
||||||
|
.file-icons [aria-label$=".db"] i::before,
|
||||||
|
.file-icons [aria-label$=".odb"] i::before,
|
||||||
|
.file-icons [aria-label$=".ods"] i::before,
|
||||||
|
.file-icons [aria-label$=".xls"] i::before,
|
||||||
|
.file-icons [aria-label$=".xlsx"] i::before
|
||||||
|
{ content: 'border_all' }
|
||||||
|
|
||||||
|
/* #00f - Document */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".doc"] i::before,
|
||||||
|
.file-icons [aria-label$=".docx"] i::before,
|
||||||
|
.file-icons [aria-label$=".log"] i::before,
|
||||||
|
.file-icons [aria-label$=".odt"] i::before,
|
||||||
|
.file-icons [aria-label$=".rtf"] i::before
|
||||||
|
{ content: 'description' }
|
||||||
|
|
||||||
|
/* #999 - Code */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".c"] i::before,
|
||||||
|
.file-icons [aria-label$=".cpp"] i::before,
|
||||||
|
.file-icons [aria-label$=".cs"] i::before,
|
||||||
|
.file-icons [aria-label$=".css"] i::before,
|
||||||
|
.file-icons [aria-label$=".go"] i::before,
|
||||||
|
.file-icons [aria-label$=".h"] i::before,
|
||||||
|
.file-icons [aria-label$=".html"] i::before,
|
||||||
|
.file-icons [aria-label$=".java"] i::before,
|
||||||
|
.file-icons [aria-label$=".js"] i::before,
|
||||||
|
.file-icons [aria-label$=".json"] i::before,
|
||||||
|
.file-icons [aria-label$=".kt"] i::before,
|
||||||
|
.file-icons [aria-label$=".php"] i::before,
|
||||||
|
.file-icons [aria-label$=".py"] i::before,
|
||||||
|
.file-icons [aria-label$=".rb"] i::before,
|
||||||
|
.file-icons [aria-label$=".rs"] i::before,
|
||||||
|
.file-icons [aria-label$=".vue"] i::before,
|
||||||
|
.file-icons [aria-label$=".xml"] i::before,
|
||||||
|
.file-icons [aria-label$=".yml"] i::before
|
||||||
|
{ content: 'code' }
|
||||||
|
|
||||||
|
/* #999 - Executable */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".apk"] i::before,
|
||||||
|
.file-icons [aria-label$=".bat"] i::before,
|
||||||
|
.file-icons [aria-label$=".exe"] i::before,
|
||||||
|
.file-icons [aria-label$=".jar"] i::before,
|
||||||
|
.file-icons [aria-label$=".ps1"] i::before,
|
||||||
|
.file-icons [aria-label$=".sh"] i::before
|
||||||
|
{ content: 'web_asset' }
|
||||||
|
|
||||||
|
/* #999 - Installer */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".deb"] i::before,
|
||||||
|
.file-icons [aria-label$=".msi"] i::before,
|
||||||
|
.file-icons [aria-label$=".pkg"] i::before,
|
||||||
|
.file-icons [aria-label$=".rpm"] i::before
|
||||||
|
{ content: 'archive' }
|
||||||
|
|
||||||
|
/* #999 - Compressed */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".7z"] i::before,
|
||||||
|
.file-icons [aria-label$=".bz2"] i::before,
|
||||||
|
.file-icons [aria-label$=".cab"] i::before,
|
||||||
|
.file-icons [aria-label$=".gz"] i::before,
|
||||||
|
.file-icons [aria-label$=".rar"] i::before,
|
||||||
|
.file-icons [aria-label$=".tar"] i::before,
|
||||||
|
.file-icons [aria-label$=".xz"] i::before,
|
||||||
|
.file-icons [aria-label$=".zip"] i::before,
|
||||||
|
.file-icons [aria-label$=".zst"] i::before
|
||||||
|
{ content: 'folder_zip' }
|
||||||
|
|
||||||
|
/* #999 - Disk */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".ccd"] i::before,
|
||||||
|
.file-icons [aria-label$=".dmg"] i::before,
|
||||||
|
.file-icons [aria-label$=".iso"] i::before,
|
||||||
|
.file-icons [aria-label$=".mdf"] i::before,
|
||||||
|
.file-icons [aria-label$=".vdi"] i::before,
|
||||||
|
.file-icons [aria-label$=".vhd"] i::before,
|
||||||
|
.file-icons [aria-label$=".vmdk"] i::before,
|
||||||
|
.file-icons [aria-label$=".wim"] i::before
|
||||||
|
{ content: 'album' }
|
||||||
|
|
||||||
|
/* #999 - Font */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".otf"] i::before,
|
||||||
|
.file-icons [aria-label$=".ttf"] i::before,
|
||||||
|
.file-icons [aria-label$=".woff"] i::before,
|
||||||
|
.file-icons [aria-label$=".woff2"] i::before
|
||||||
|
{ content: 'font_download' }
|
||||||
|
|
||||||
|
/* Colors */
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
|
||||||
|
.file-icons [data-type=audio] i { color: var(--icon-yellow) }
|
||||||
|
.file-icons [data-type=image] i { color: var(--icon-orange) }
|
||||||
|
.file-icons [data-type=video] i { color: var(--icon-violet) }
|
||||||
|
.file-icons [data-type=invalid_link] i { color: var(--icon-red) }
|
||||||
|
|
||||||
|
/* #f00 - Adobe/Oracle */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".ai"] i,
|
||||||
|
.file-icons [aria-label$=".java"] i,
|
||||||
|
.file-icons [aria-label$=".jar"] i,
|
||||||
|
.file-icons [aria-label$=".psd"] i,
|
||||||
|
.file-icons [aria-label$=".rb"] i,
|
||||||
|
.file-icons [data-type=pdf] i
|
||||||
|
{ color: var(--icon-red) }
|
||||||
|
|
||||||
|
/* #f90 - Image/Presentation */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".html"] i,
|
||||||
|
.file-icons [aria-label$=".odg"] i,
|
||||||
|
.file-icons [aria-label$=".odp"] i,
|
||||||
|
.file-icons [aria-label$=".ppt"] i,
|
||||||
|
.file-icons [aria-label$=".pptx"] i,
|
||||||
|
.file-icons [aria-label$=".vue"] i,
|
||||||
|
.file-icons [aria-label$=".xcf"] i
|
||||||
|
{ color: var(--icon-orange) }
|
||||||
|
|
||||||
|
/* #ff0 - Various */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".css"] i,
|
||||||
|
.file-icons [aria-label$=".js"] i,
|
||||||
|
.file-icons [aria-label$=".json"] i,
|
||||||
|
.file-icons [aria-label$=".zip"] i
|
||||||
|
{ color: var(--icon-yellow) }
|
||||||
|
|
||||||
|
/* #0f0 - Spreadsheet/Google */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".apk"] i,
|
||||||
|
.file-icons [aria-label$=".dex"] i,
|
||||||
|
.file-icons [aria-label$=".go"] i,
|
||||||
|
.file-icons [aria-label$=".ods"] i,
|
||||||
|
.file-icons [aria-label$=".xls"] i,
|
||||||
|
.file-icons [aria-label$=".xlsx"] i
|
||||||
|
{ color: var(--icon-green) }
|
||||||
|
|
||||||
|
/* #00f - Document/Microsoft/Apple/Closed */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".aac"] i,
|
||||||
|
.file-icons [aria-label$=".bat"] i,
|
||||||
|
.file-icons [aria-label$=".cab"] i,
|
||||||
|
.file-icons [aria-label$=".cs"] i,
|
||||||
|
.file-icons [aria-label$=".dmg"] i,
|
||||||
|
.file-icons [aria-label$=".doc"] i,
|
||||||
|
.file-icons [aria-label$=".docx"] i,
|
||||||
|
.file-icons [aria-label$=".emf"] i,
|
||||||
|
.file-icons [aria-label$=".exe"] i,
|
||||||
|
.file-icons [aria-label$=".ico"] i,
|
||||||
|
.file-icons [aria-label$=".mp2"] i,
|
||||||
|
.file-icons [aria-label$=".mp3"] i,
|
||||||
|
.file-icons [aria-label$=".mp4"] i,
|
||||||
|
.file-icons [aria-label$=".mpg"] i,
|
||||||
|
.file-icons [aria-label$=".msi"] i,
|
||||||
|
.file-icons [aria-label$=".odt"] i,
|
||||||
|
.file-icons [aria-label$=".ps1"] i,
|
||||||
|
.file-icons [aria-label$=".rtf"] i,
|
||||||
|
.file-icons [aria-label$=".vob"] i,
|
||||||
|
.file-icons [aria-label$=".wim"] i
|
||||||
|
{ color: var(--icon-blue) }
|
||||||
|
|
||||||
|
/* #60f - Various */
|
||||||
|
|
||||||
|
.file-icons [aria-label$=".iso"] i,
|
||||||
|
.file-icons [aria-label$=".php"] i,
|
||||||
|
.file-icons [aria-label$=".rar"] i
|
||||||
|
{ color: var(--icon-violet) }
|
||||||
|
|
||||||
|
/* Overrides */
|
||||||
|
|
||||||
|
.file-icons [data-dir=true] i { color: var(--icon-blue) }
|
||||||
|
.file-icons [data-dir=true] i::before { content: 'folder' }
|
||||||
|
.file-icons [aria-selected=true] i { color: var(--item-selected) }
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#listing {
|
||||||
|
--item-selected: white;
|
||||||
|
}
|
||||||
|
|
||||||
#listing h2 {
|
#listing h2 {
|
||||||
margin: 0 0 0 0.5em;
|
margin: 0 0 0 0.5em;
|
||||||
font-size: .9em;
|
font-size: .9em;
|
||||||
@@ -25,6 +29,7 @@
|
|||||||
transition: .1s ease background, .1s ease opacity;
|
transition: .1s ease background, .1s ease opacity;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing .item div:last-of-type {
|
#listing .item div:last-of-type {
|
||||||
@@ -55,6 +60,7 @@
|
|||||||
#listing .item img {
|
#listing .item img {
|
||||||
width: 4em;
|
width: 4em;
|
||||||
height: 4em;
|
height: 4em;
|
||||||
|
object-fit: cover;
|
||||||
margin-right: 0.1em;
|
margin-right: 0.1em;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
@@ -104,6 +110,41 @@
|
|||||||
width: calc(100% - 5vw);
|
width: calc(100% - 5vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#listing.mosaic.gallery .item div:first-of-type {
|
||||||
|
width: 100%;
|
||||||
|
height: 12em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.mosaic.gallery .item div:last-of-type {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.5em;
|
||||||
|
padding: 1em;
|
||||||
|
width: calc(100% - 1em);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.mosaic.gallery .item[data-type=image] div:last-of-type {
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(#0000, #0009);
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.mosaic.gallery .item i {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 0;
|
||||||
|
font-size: 8em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.mosaic.gallery .item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.gallery .size,
|
||||||
|
#listing.gallery .modified {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#listing.list {
|
#listing.list {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -125,7 +166,7 @@
|
|||||||
|
|
||||||
#listing .item[aria-selected=true] {
|
#listing .item[aria-selected=true] {
|
||||||
background: var(--blue) !important;
|
background: var(--blue) !important;
|
||||||
color: #fff !important;
|
color: var(--item-selected) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .item div:first-of-type {
|
#listing.list .item div:first-of-type {
|
||||||
@@ -236,5 +277,5 @@
|
|||||||
|
|
||||||
#listing #multiple-selection p,
|
#listing #multiple-selection p,
|
||||||
#listing #multiple-selection i {
|
#listing #multiple-selection i {
|
||||||
color: #fff;
|
color: var(--item-selected);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
@import "./base.css";
|
@import "./base.css";
|
||||||
@import "./header.css";
|
@import "./header.css";
|
||||||
@import "./listing.css";
|
@import "./listing.css";
|
||||||
|
@import "./listing-icons.css";
|
||||||
|
@import "./upload-files.css";
|
||||||
@import "./dashboard.css";
|
@import "./dashboard.css";
|
||||||
@import "./login.css";
|
@import "./login.css";
|
||||||
|
|
||||||
@@ -17,6 +19,48 @@
|
|||||||
color: var(--blue);
|
color: var(--blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main .spinner {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 0;
|
||||||
|
padding: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .spinner > div {
|
||||||
|
width: .8em;
|
||||||
|
height: .8em;
|
||||||
|
margin: 0 .1em;
|
||||||
|
font-size: 1em;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .spinner .bounce1 {
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .spinner .bounce2 {
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delayed {
|
||||||
|
animation: delayed linear 100ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes delayed {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
99% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * *
|
||||||
* ACTION *
|
* ACTION *
|
||||||
* * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * */
|
||||||
@@ -109,6 +153,7 @@
|
|||||||
|
|
||||||
#previewer {
|
#previewer {
|
||||||
background-color: rgba(0, 0, 0, 0.9);
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
padding-top: 4em;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -142,7 +187,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#previewer .preview {
|
#previewer .preview {
|
||||||
margin-top: 4em;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: calc(100vh - 4em);
|
height: calc(100vh - 4em);
|
||||||
}
|
}
|
||||||
@@ -163,6 +207,34 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#previewer .preview .info {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
#previewer .preview .info .title {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
#previewer .preview .info .title i {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: .1em;
|
||||||
|
font-size: 4em;
|
||||||
|
}
|
||||||
|
#previewer .preview .info .button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#previewer .preview .info .button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2)
|
||||||
|
}
|
||||||
|
#previewer .preview .info .button i {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
#previewer .pdf {
|
#previewer .pdf {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -204,26 +276,38 @@
|
|||||||
right: 0.5em;
|
right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#previewer .spinner {
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
top: calc(50% + 1.85em);
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#previewer .spinner > div {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
/* EDITOR */
|
/* EDITOR */
|
||||||
|
|
||||||
#editor-container {
|
#editor-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
margin-top: 4em;
|
padding-top: 4em;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer .loading {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#editor-container #editor {
|
#editor-container #editor {
|
||||||
height: calc(100vh - 8.4em);
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editor-container .breadcrumbs {
|
#editor-container .breadcrumbs {
|
||||||
@@ -232,11 +316,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#editor-container .breadcrumbs span {
|
#editor-container .breadcrumbs span {
|
||||||
font-size: 12px;
|
font-size: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editor-container .breadcrumbs i {
|
#editor-container .breadcrumbs i {
|
||||||
font-size: 16px;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * *
|
||||||
@@ -252,7 +336,7 @@
|
|||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
border: 1px solid rgba(0,0,0,0.1);
|
border: 1px solid rgba(0,0,0,0.1);
|
||||||
box-shadow: 0 0 0 0;
|
box-shadow: 0 0 0 0;
|
||||||
font-size: 14px;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * *
|
||||||
@@ -283,8 +367,7 @@
|
|||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(-360deg);
|
transform: rotate(360deg);
|
||||||
transform: rotate(-360deg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
61
frontend/src/css/upload-files.css
Normal file
61
frontend/src/css/upload-files.css
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
.upload-files .card.floating {
|
||||||
|
left: auto;
|
||||||
|
top: auto;
|
||||||
|
margin: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files .file {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files .file .file-name {
|
||||||
|
font-size: 1.1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files .file .file-name i {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files .file .file-progress {
|
||||||
|
margin-top: 2px;
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files .file .file-progress div {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #40c4ff;
|
||||||
|
width: 0;
|
||||||
|
transition: 0.2s ease width;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files.closed .card-content {
|
||||||
|
display: none;
|
||||||
|
padding: 0em 1em 1em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files .card .card-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 1em 1em 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-files.closed .card-title {
|
||||||
|
font-size: 0.7em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 450px) {
|
||||||
|
.upload-files .card.floating {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,7 +92,10 @@
|
|||||||
"ptBR": "Português (Brasil)",
|
"ptBR": "Português (Brasil)",
|
||||||
"ro": "",
|
"ro": "",
|
||||||
"ru": "Русский",
|
"ru": "Русский",
|
||||||
|
"sk": "Slovenčina",
|
||||||
"svSE": "",
|
"svSE": "",
|
||||||
|
"tr" : "Türkçe",
|
||||||
|
"ua": "Українська",
|
||||||
"zhCN": "中文 (简体)",
|
"zhCN": "中文 (简体)",
|
||||||
"zhTW": "中文 (繁體)"
|
"zhTW": "中文 (繁體)"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
"copyToClipboard": "In Zwischenablage kopieren",
|
"copyToClipboard": "In Zwischenablage kopieren",
|
||||||
"create": "Neu",
|
"create": "Neu",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"download": "Downloaden",
|
"download": "Herunterladen",
|
||||||
"hideDotfiles": "",
|
"file": "Datei",
|
||||||
|
"folder": "Ordner",
|
||||||
|
"hideDotfiles": "Versteckte Dateien ausblenden",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"more": "mehr",
|
"more": "mehr",
|
||||||
"move": "Verschieben",
|
"move": "Verschieben",
|
||||||
@@ -29,24 +31,27 @@
|
|||||||
"selectMultiple": "Mehrfachauswahl",
|
"selectMultiple": "Mehrfachauswahl",
|
||||||
"share": "Teilen",
|
"share": "Teilen",
|
||||||
"shell": "Kommandozeile ein/ausschalten",
|
"shell": "Kommandozeile ein/ausschalten",
|
||||||
|
"submit": "Absenden",
|
||||||
"switchView": "Ansicht wechseln",
|
"switchView": "Ansicht wechseln",
|
||||||
"toggleSidebar": "Seitenleiste anzeigen",
|
"toggleSidebar": "Seitenleiste anzeigen",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"upload": "Upload"
|
"upload": "Upload",
|
||||||
|
"openFile": "Datei öffnen"
|
||||||
},
|
},
|
||||||
"download": {
|
"download": {
|
||||||
"downloadFile": "Download Datei",
|
"downloadFile": "Download Datei",
|
||||||
"downloadFolder": "Download Ordner",
|
"downloadFolder": "Download Ordner",
|
||||||
"downloadSelected": ""
|
"downloadSelected": "Auswahl herunterladen"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"forbidden": "Sie haben keine Berechtigung dies abzurufen.",
|
"forbidden": "Sie haben keine Berechtigung dies abzurufen.",
|
||||||
"internal": "Etwas ist schief gelaufen.",
|
"internal": "Etwas ist schief gelaufen.",
|
||||||
"notFound": "Dieser Ort kann nicht angezeigt werden."
|
"notFound": "Dieser Ort kann nicht angezeigt werden.",
|
||||||
|
"connection": "Der Server ist nicht erreichbar."
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"body": "Body",
|
"body": "Body",
|
||||||
"clear": "Clear",
|
"clear": "Schließen",
|
||||||
"closePreview": "Vorschau schließen",
|
"closePreview": "Vorschau schließen",
|
||||||
"files": "Dateien",
|
"files": "Dateien",
|
||||||
"folders": "Ordner",
|
"folders": "Ordner",
|
||||||
@@ -55,24 +60,25 @@
|
|||||||
"loading": "Lade...",
|
"loading": "Lade...",
|
||||||
"lonely": "Hier scheint nichts zu sein...",
|
"lonely": "Hier scheint nichts zu sein...",
|
||||||
"metadata": "Metadaten",
|
"metadata": "Metadaten",
|
||||||
"multipleSelectionEnabled": "Mehrfachauswahl ausgewählt",
|
"multipleSelectionEnabled": "Mehrfachauswahl aktiviert",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"size": "Größe",
|
"size": "Größe",
|
||||||
"sortByLastModified": "Nach Änderungsdatum sortieren",
|
"sortByLastModified": "Nach Änderungsdatum sortieren",
|
||||||
"sortByName": "Nach Namen sortieren",
|
"sortByName": "Nach Namen sortieren",
|
||||||
"sortBySize": "Nach Größe sortieren"
|
"sortBySize": "Nach Größe sortieren",
|
||||||
|
"noPreview": "Für diese Datei ist keine Vorschau verfügbar."
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"click": "wähle Datei oder Ordner",
|
"click": "Wähle Datei oder Ordner",
|
||||||
"ctrl": {
|
"ctrl": {
|
||||||
"click": "markiere mehrere Dateien oder Ordner",
|
"click": "Markiere mehrere Dateien oder Ordner",
|
||||||
"f": "öffnet eine neue Suche",
|
"f": "Öffnet eine neue Suche",
|
||||||
"s": "speichert eine Datei oder einen Ordner am akutellen Ort"
|
"s": "Speichert eine Datei oder einen Ordner am akutellen Ort"
|
||||||
},
|
},
|
||||||
"del": "löscht die ausgewählten Elemente",
|
"del": "Löscht die ausgewählten Elemente",
|
||||||
"doubleClick": "öffnet eine Datei oder einen Ordner",
|
"doubleClick": "Öffnet eine Datei oder einen Ordner",
|
||||||
"esc": "Auswahl zurücksetzen und/oder Dialog schließen",
|
"esc": "Auswahl zurücksetzen und/oder Dialog schließen",
|
||||||
"f1": "diese Informationsseite",
|
"f1": "Diese Informationsseite",
|
||||||
"f2": "Datei umbenennen",
|
"f2": "Datei umbenennen",
|
||||||
"help": "Hilfe"
|
"help": "Hilfe"
|
||||||
},
|
},
|
||||||
@@ -82,17 +88,20 @@
|
|||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
"fr": "Français",
|
"fr": "Français",
|
||||||
"is": "",
|
"is": "Icelandic",
|
||||||
"it": "Italiano",
|
"it": "Italiano",
|
||||||
"ja": "日本語",
|
"ja": "日本語",
|
||||||
"ko": "한국어",
|
"ko": "한국어",
|
||||||
"nlBE": "",
|
"nlBE": "Dutch (Belgium)",
|
||||||
"pl": "Polski",
|
"pl": "Polski",
|
||||||
"pt": "Português",
|
"pt": "Português",
|
||||||
"ptBR": "Português (Brasil)",
|
"ptBR": "Português (Brasil)",
|
||||||
"ro": "",
|
"ro": "Romanian",
|
||||||
"ru": "Русский",
|
"ru": "Русский",
|
||||||
"svSE": "",
|
"sk": "Slovenčina",
|
||||||
|
"svSE": "Swedish (Sweden)",
|
||||||
|
"tr": "Türkçe",
|
||||||
|
"ua": "Українська",
|
||||||
"zhCN": "中文 (简体)",
|
"zhCN": "中文 (简体)",
|
||||||
"zhTW": "中文 (繁體)"
|
"zhTW": "中文 (繁體)"
|
||||||
},
|
},
|
||||||
@@ -115,16 +124,17 @@
|
|||||||
"currentlyNavigating": "Aktueller Ort:",
|
"currentlyNavigating": "Aktueller Ort:",
|
||||||
"deleteMessageMultiple": "Sind Sie sicher, dass Sie {count} Datei(en) löschen möchten?",
|
"deleteMessageMultiple": "Sind Sie sicher, dass Sie {count} Datei(en) löschen möchten?",
|
||||||
"deleteMessageSingle": "Sind Sie sicher, dass Sie diesen Ordner/diese Datei löschen möchten?",
|
"deleteMessageSingle": "Sind Sie sicher, dass Sie diesen Ordner/diese Datei löschen möchten?",
|
||||||
|
"deleteMessageShare": "Sind Sie sicher, dass Sie diese Freigabe löschen möchten ({path})?",
|
||||||
"deleteTitle": "Lösche Dateien",
|
"deleteTitle": "Lösche Dateien",
|
||||||
"displayName": "Display Name:",
|
"displayName": "Anzeigename:",
|
||||||
"download": "Lade Dateien",
|
"download": "Lade Dateien",
|
||||||
"downloadMessage": "Wählen Sie ein Format zum downloaden aus.",
|
"downloadMessage": "Wählen Sie ein Format zum Herunterladen aus.",
|
||||||
"error": "Etwas ist schief gelaufen",
|
"error": "Etwas ist schief gelaufen",
|
||||||
"fileInfo": "Dateiinformation",
|
"fileInfo": "Dateiinformation",
|
||||||
"filesSelected": "{count} Dateien ausgewählt.",
|
"filesSelected": "{count} Dateien ausgewählt.",
|
||||||
"lastModified": "Zuletzt geändert",
|
"lastModified": "Zuletzt geändert",
|
||||||
"move": "Verschieben",
|
"move": "Verschieben",
|
||||||
"moveMessage": "Wählen sie einen neuen Platz für ihre Datei(en)/Ordner:",
|
"moveMessage": "Wählen Sie einen neuen Platz für ihre Datei(en)/Ordner:",
|
||||||
"newArchetype": "Erstelle neuen Beitrag auf dem Archetyp. Ihre Datei wird im Inhalteordner erstellt.",
|
"newArchetype": "Erstelle neuen Beitrag auf dem Archetyp. Ihre Datei wird im Inhalteordner erstellt.",
|
||||||
"newDir": "Neuer Ordner",
|
"newDir": "Neuer Ordner",
|
||||||
"newDirMessage": "Geben Sie den Namen des neuen Ordners an.",
|
"newDirMessage": "Geben Sie den Namen des neuen Ordners an.",
|
||||||
@@ -140,16 +150,18 @@
|
|||||||
"scheduleMessage": "Wählen Sie ein Datum und eine Zeit für die Veröffentlichung dieses Beitrags.",
|
"scheduleMessage": "Wählen Sie ein Datum und eine Zeit für die Veröffentlichung dieses Beitrags.",
|
||||||
"show": "Anzeigen",
|
"show": "Anzeigen",
|
||||||
"size": "Größe",
|
"size": "Größe",
|
||||||
"upload": "",
|
"upload": "Upload",
|
||||||
"uploadMessage": ""
|
"uploadFiles": "Upload von {files} Dateien...",
|
||||||
|
"uploadMessage": "Wählen Sie eine Upload-Methode",
|
||||||
|
"optionalPassword": "Optionales Passwort"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"images": "Bilder",
|
"images": "Bilder",
|
||||||
"music": "Musik",
|
"music": "Musik",
|
||||||
"pdf": "PDF",
|
"pdf": "PDF",
|
||||||
"pressToSearch": "Drücken sie Enter um zu suchen...",
|
"pressToSearch": "Drücken Sie Enter um zu suchen...",
|
||||||
"search": "Suche...",
|
"search": "Suche...",
|
||||||
"typeToSearch": "Tippe um zu suchen...",
|
"typeToSearch": "Tippen um zu suchen...",
|
||||||
"types": "Typen",
|
"types": "Typen",
|
||||||
"video": "Video"
|
"video": "Video"
|
||||||
},
|
},
|
||||||
@@ -162,26 +174,26 @@
|
|||||||
"allowPublish": "Veröffentlichen von neuen Beiträgen und Seiten",
|
"allowPublish": "Veröffentlichen von neuen Beiträgen und Seiten",
|
||||||
"allowSignup": "Erlaube Benutzern sich zu registrieren",
|
"allowSignup": "Erlaube Benutzern sich zu registrieren",
|
||||||
"avoidChanges": "(leer lassen um Änderungen zu vermeiden)",
|
"avoidChanges": "(leer lassen um Änderungen zu vermeiden)",
|
||||||
"branding": "Marke",
|
"branding": "Design",
|
||||||
"brandingDirectoryPath": "Markenverzeichnispfad",
|
"brandingDirectoryPath": "Designverzeichnispfad",
|
||||||
"brandingHelp": "Sie können das Erscheinungsbild ihres File Browser anpassen, in dem sie den Namen ändern, das Logo austauchsen oder eigene Stile definieren und sogar externe Links zu GitHub deaktivieren.\nUm mehr Informationen zum Anpassen an ihre Marke zu bekommen, gehen sie bitte zu {0}.",
|
"brandingHelp": "Sie können das Erscheinungsbild Ihres File Browser anpassen, in dem Sie den Namen ändern, das Logo austauchsen oder eigene Stile definieren und sogar externe Links zu GitHub deaktivieren.\nUm mehr Informationen zum Anpassen des Designs zu bekommen, gehen Sie bitte zu {0}.",
|
||||||
"changePassword": "Ändere das Passwort",
|
"changePassword": "Passwort ändern",
|
||||||
"commandRunner": "Befehlseingabe",
|
"commandRunner": "Befehlseingabe",
|
||||||
"commandRunnerHelp": "Hier könne sie Befehle eintragen die bei benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen, lesen sie bitte das {2}.",
|
"commandRunnerHelp": "Hier könne Sie Befehle eintragen, welche bei den benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen Befehl eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen, lesen Sie bitte die {2}.",
|
||||||
"commandsUpdated": "Befehle aktualisiert!",
|
"commandsUpdated": "Befehle aktualisiert!",
|
||||||
"createUserDir": "Auto create user home dir while adding new user",
|
"createUserDir": "Automatisches Erstellen des Home-Verzeichnisses beim Anlegen neuer Benutzer",
|
||||||
"customStylesheet": "Individuelles Stylesheet",
|
"customStylesheet": "Individuelles Stylesheet",
|
||||||
"defaultUserDescription": "Das sind die Standard Einstellunge für Benutzer",
|
"defaultUserDescription": "Das sind die Standardeinstellung für Benutzer",
|
||||||
"disableExternalLinks": "Externe Links deaktivieren (außer Dokumentation)",
|
"disableExternalLinks": "Externe Links deaktivieren (außer Dokumentation)",
|
||||||
"documentation": "Dokumentation",
|
"documentation": "Dokumentation",
|
||||||
"examples": "Beispiele",
|
"examples": "Beispiele",
|
||||||
"executeOnShell": "In shell ausführen",
|
"executeOnShell": "In Shell ausführen",
|
||||||
"executeOnShellDescription": "Es ist voreingestellt das der File Brower Befehle ausführt in dem er die Befehlsdatein direkt auf ruft. Wenn sie wollen das sie auf einer Kommandozeile (wo Bash oder PowerShell) laufen, könne sie das hier definieren mit allen bennötigten Argumenten und Optionen. Wenn gesetzt, wird das Kommando das ausgeführt werden soll als Parameter angehängt. Das gilt für Benuzerkommandos sowie auch für Ereignisse.",
|
"executeOnShellDescription": "Es ist voreingestellt das der File Brower Befehle ausführt in dem er die Befehlsdateien direkt aufruft. Wenn Sie wollen, dass sie über einer Kommandozeile (wie Bash oder PowerShell) laufen, könne Sie diese hier definieren mit allen bennötigten Argumenten und Optionen. Wenn gesetzt, wird das Kommando das ausgeführt werden soll als Parameter angehängt. Das gilt für Benuzerkommandos sowie auch für Ereignisse.",
|
||||||
"globalRules": "Das ist ein globales Set von Regeln die erlauben oder nicht erlauben. Die sind für alle Benutzer zutreffend. Es können spezielle Regeln in den Einstellungen der Benutzer definiert werden die diese übersteuern.",
|
"globalRules": "Das ist ein globales Set von Regeln die erlauben oder nicht erlauben. Die sind für alle Benutzer zutreffend. Es können spezielle Regeln in den Einstellungen der Benutzer definiert werden, die diese überschreiben.",
|
||||||
"globalSettings": "Globale Einstellungen",
|
"globalSettings": "Globale Einstellungen",
|
||||||
"hideDotfiles": "",
|
"hideDotfiles": "Versteckte Dateien ausblenden",
|
||||||
"insertPath": "Pfad einfügen",
|
"insertPath": "Pfad einfügen",
|
||||||
"insertRegex": "Regex Ausdruck einfügen",
|
"insertRegex": "Regulären Ausdruck (Regex) einfügen",
|
||||||
"instanceName": "Instanzname",
|
"instanceName": "Instanzname",
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
"lockPassword": "Verhindere, dass der Benutzer sein Passwort ändert",
|
"lockPassword": "Verhindere, dass der Benutzer sein Passwort ändert",
|
||||||
@@ -190,7 +202,7 @@
|
|||||||
"newUser": "Neuer Benutzer",
|
"newUser": "Neuer Benutzer",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"passwordUpdated": "Passwort aktualisiert!",
|
"passwordUpdated": "Passwort aktualisiert!",
|
||||||
"path": "",
|
"path": "Pfad",
|
||||||
"perm": {
|
"perm": {
|
||||||
"create": "Dateien und Ordner erstellen",
|
"create": "Dateien und Ordner erstellen",
|
||||||
"delete": "Dateien und Ordner löschen",
|
"delete": "Dateien und Ordner löschen",
|
||||||
@@ -203,23 +215,25 @@
|
|||||||
"permissions": "Berechtigungen",
|
"permissions": "Berechtigungen",
|
||||||
"permissionsHelp": "Sie können einem Benutzer Administratorrechte einräumen oder die Berechtigunen individuell festlegen. Wenn Sie \"Administrator\" auswählen, werden alle anderen Rechte automatisch vergeben. Die Nutzerverwaltung kann nur durch einen Administrator erfolgen.\n",
|
"permissionsHelp": "Sie können einem Benutzer Administratorrechte einräumen oder die Berechtigunen individuell festlegen. Wenn Sie \"Administrator\" auswählen, werden alle anderen Rechte automatisch vergeben. Die Nutzerverwaltung kann nur durch einen Administrator erfolgen.\n",
|
||||||
"profileSettings": "Profileinstellungen",
|
"profileSettings": "Profileinstellungen",
|
||||||
"ruleExample1": "Verhindert den Zugang zu dot Dateien (dot Files, wie .git, .gitignore) in allen Ordnern\n",
|
"ruleExample1": "Verhindert den Zugang zu versteckten Dateien (dot-Files, wie .git, .gitignore) in allen Ordnern\n",
|
||||||
"ruleExample2": "blockiert den Zugang auf Dateien mit dem Namen Caddyfile in der Wurzel/Basis des scopes.",
|
"ruleExample2": "blockiert den Zugang auf Dateien mit dem Namen Caddyfile in der Wurzel/Basis des Scopes.",
|
||||||
"rules": "Regeln",
|
"rules": "Regeln",
|
||||||
"rulesHelp": "Hier können Sie erlaubte und verbotene Aktionen für einen einzelnen Benutzer festlegen. Bockierte Dateien werden nicht im Listing angezeigt und sind nicht erreichbar für den Nutzer. Wir unterstützen reguläre Ausdrücke (Regex) und Pfade die relativ zum Benutzerordner sind. \n",
|
"rulesHelp": "Hier können Sie erlaubte und verbotene Aktionen für einen einzelnen Benutzer festlegen. Blockierte Dateien werden nicht im Listing angezeigt und sind nicht erreichbar für den Nutzer. Wir unterstützen reguläre Ausdrücke (Regex) und Pfade die relativ zum Benutzerordner sind. \n",
|
||||||
"scope": "Scope",
|
"scope": "Scope",
|
||||||
|
"setDateFormat": "Exaktes Datumsformat setzen",
|
||||||
"settingsUpdated": "Einstellungen aktualisiert!",
|
"settingsUpdated": "Einstellungen aktualisiert!",
|
||||||
"shareDuration": "",
|
"shareDuration": "Dauer",
|
||||||
"shareManagement": "",
|
"shareManagement": "Freigaben verwalten",
|
||||||
"singleClick": "",
|
"shareDeleted": "Freigabe gelöscht!",
|
||||||
|
"singleClick": "Einfacher Klick zum Öffnen von Dateien und Ordnern",
|
||||||
"themes": {
|
"themes": {
|
||||||
"dark": "",
|
"dark": "Dunkel",
|
||||||
"light": "",
|
"light": "Hell",
|
||||||
"title": ""
|
"title": "Erscheinungsbild"
|
||||||
},
|
},
|
||||||
"user": "Benutzer",
|
"user": "Benutzer",
|
||||||
"userCommands": "Befehle",
|
"userCommands": "Befehle",
|
||||||
"userCommandsHelp": "Eine Liste, mit einem Leerzeichen als Trennung, mit den für diesen Nutzer verfügbaren Befehlen. Example:\n",
|
"userCommandsHelp": "Eine Liste, mit einem Leerzeichen als Trennung, mit den für diesen Nutzer verfügbaren Befehlen. Beispiel:\n",
|
||||||
"userCreated": "Benutzer angelegt!",
|
"userCreated": "Benutzer angelegt!",
|
||||||
"userDefaults": "Benutzer Standard Einstellungen",
|
"userDefaults": "Benutzer Standard Einstellungen",
|
||||||
"userDeleted": "Benutzer gelöscht!",
|
"userDeleted": "Benutzer gelöscht!",
|
||||||
@@ -232,7 +246,7 @@
|
|||||||
"help": "Hilfe",
|
"help": "Hilfe",
|
||||||
"hugoNew": "Hugo Neu",
|
"hugoNew": "Hugo Neu",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"logout": "Logout",
|
"logout": "Abmelden",
|
||||||
"myFiles": "Meine Dateien",
|
"myFiles": "Meine Dateien",
|
||||||
"newFile": "Neue Datei",
|
"newFile": "Neue Datei",
|
||||||
"newFolder": "Neuer Ordner",
|
"newFolder": "Neuer Ordner",
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
"create": "Create",
|
"create": "Create",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
|
"file": "File",
|
||||||
|
"folder": "Folder",
|
||||||
"hideDotfiles": "Hide dotfiles",
|
"hideDotfiles": "Hide dotfiles",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
@@ -33,7 +35,8 @@
|
|||||||
"switchView": "Switch view",
|
"switchView": "Switch view",
|
||||||
"toggleSidebar": "Toggle sidebar",
|
"toggleSidebar": "Toggle sidebar",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"upload": "Upload"
|
"upload": "Upload",
|
||||||
|
"openFile": "Open file"
|
||||||
},
|
},
|
||||||
"download": {
|
"download": {
|
||||||
"downloadFile": "Download File",
|
"downloadFile": "Download File",
|
||||||
@@ -43,7 +46,8 @@
|
|||||||
"errors": {
|
"errors": {
|
||||||
"forbidden": "You don't have permissions to access this.",
|
"forbidden": "You don't have permissions to access this.",
|
||||||
"internal": "Something really went wrong.",
|
"internal": "Something really went wrong.",
|
||||||
"notFound": "This location can't be reached."
|
"notFound": "This location can't be reached.",
|
||||||
|
"connection": "The server can't be reached."
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"body": "Body",
|
"body": "Body",
|
||||||
@@ -61,7 +65,8 @@
|
|||||||
"size": "Size",
|
"size": "Size",
|
||||||
"sortByLastModified": "Sort by last modified",
|
"sortByLastModified": "Sort by last modified",
|
||||||
"sortByName": "Sort by name",
|
"sortByName": "Sort by name",
|
||||||
"sortBySize": "Sort by size"
|
"sortBySize": "Sort by size",
|
||||||
|
"noPreview": "Preview is not available for this file."
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"click": "select file or directory",
|
"click": "select file or directory",
|
||||||
@@ -93,7 +98,10 @@
|
|||||||
"ptBR": "Português (Brasil)",
|
"ptBR": "Português (Brasil)",
|
||||||
"ro": "Romanian",
|
"ro": "Romanian",
|
||||||
"ru": "Русский",
|
"ru": "Русский",
|
||||||
|
"sk": "Slovenčina",
|
||||||
"svSE": "Swedish (Sweden)",
|
"svSE": "Swedish (Sweden)",
|
||||||
|
"tr": "Türkçe",
|
||||||
|
"ua": "Українська",
|
||||||
"zhCN": "中文 (简体)",
|
"zhCN": "中文 (简体)",
|
||||||
"zhTW": "中文 (繁體)"
|
"zhTW": "中文 (繁體)"
|
||||||
},
|
},
|
||||||
@@ -143,6 +151,7 @@
|
|||||||
"show": "Show",
|
"show": "Show",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
|
"uploadFiles": "Uploading {files} files...",
|
||||||
"uploadMessage": "Select an option to upload.",
|
"uploadMessage": "Select an option to upload.",
|
||||||
"optionalPassword": "Optional password"
|
"optionalPassword": "Optional password"
|
||||||
},
|
},
|
||||||
@@ -173,6 +182,9 @@
|
|||||||
"commandRunnerHelp": "Here you can set commands that are executed in the named events. You must write one per line. The environment variables {0} and {1} will be available, being {0} relative to {1}. For more information about this feature and the available environment variables, please read the {2}.",
|
"commandRunnerHelp": "Here you can set commands that are executed in the named events. You must write one per line. The environment variables {0} and {1} will be available, being {0} relative to {1}. For more information about this feature and the available environment variables, please read the {2}.",
|
||||||
"commandsUpdated": "Commands updated!",
|
"commandsUpdated": "Commands updated!",
|
||||||
"createUserDir": "Auto create user home dir while adding new user",
|
"createUserDir": "Auto create user home dir while adding new user",
|
||||||
|
"userHomeBasePath": "Base path for user home directories",
|
||||||
|
"userScopeGenerationPlaceholder": "The scope will be auto generated",
|
||||||
|
"createUserHomeDirectory": "Create user home directory",
|
||||||
"customStylesheet": "Custom Stylesheet",
|
"customStylesheet": "Custom Stylesheet",
|
||||||
"defaultUserDescription": "This are the default settings for new users.",
|
"defaultUserDescription": "This are the default settings for new users.",
|
||||||
"disableExternalLinks": "Disable external links (except documentation)",
|
"disableExternalLinks": "Disable external links (except documentation)",
|
||||||
@@ -211,9 +223,11 @@
|
|||||||
"rules": "Rules",
|
"rules": "Rules",
|
||||||
"rulesHelp": "Here you can define a set of allow and disallow rules for this specific user. The blocked files won't show up in the listings and they wont be accessible to the user. We support regex and paths relative to the users scope.\n",
|
"rulesHelp": "Here you can define a set of allow and disallow rules for this specific user. The blocked files won't show up in the listings and they wont be accessible to the user. We support regex and paths relative to the users scope.\n",
|
||||||
"scope": "Scope",
|
"scope": "Scope",
|
||||||
|
"setDateFormat": "Set exact date format",
|
||||||
"settingsUpdated": "Settings updated!",
|
"settingsUpdated": "Settings updated!",
|
||||||
"shareDuration": "Share Duration",
|
"shareDuration": "Share Duration",
|
||||||
"shareManagement": "Share Management",
|
"shareManagement": "Share Management",
|
||||||
|
"shareDeleted": "Share deleted!",
|
||||||
"singleClick": "Use single clicks to open files and directories",
|
"singleClick": "Use single clicks to open files and directories",
|
||||||
"themes": {
|
"themes": {
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
|
|||||||
@@ -92,7 +92,10 @@
|
|||||||
"ptBR": "Português (Brasil)",
|
"ptBR": "Português (Brasil)",
|
||||||
"ro": "",
|
"ro": "",
|
||||||
"ru": "Русский",
|
"ru": "Русский",
|
||||||
|
"sk": "Slovenčina",
|
||||||
"svSE": "",
|
"svSE": "",
|
||||||
|
"tr" : "Türkçe",
|
||||||
|
"ua": "Українська",
|
||||||
"zhCN": "中文 (简体)",
|
"zhCN": "中文 (简体)",
|
||||||
"zhTW": "中文 (繁體)"
|
"zhTW": "中文 (繁體)"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
"create": "Créer",
|
"create": "Créer",
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"download": "Télécharger",
|
"download": "Télécharger",
|
||||||
"hideDotfiles": "",
|
"file": "Fichier",
|
||||||
|
"folder": "Dossier",
|
||||||
|
"hideDotfiles": "Masquer les dotfiles",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"more": "Plus",
|
"more": "Plus",
|
||||||
"move": "Déplacer",
|
"move": "Déplacer",
|
||||||
@@ -28,21 +30,24 @@
|
|||||||
"select": "Sélectionner",
|
"select": "Sélectionner",
|
||||||
"selectMultiple": "Sélection multiple",
|
"selectMultiple": "Sélection multiple",
|
||||||
"share": "Partager",
|
"share": "Partager",
|
||||||
"shell": "Toggle shell",
|
"shell": "Activer/Désactiver le shell",
|
||||||
|
"submit": "Envoyer",
|
||||||
"switchView": "Changer le mode d'affichage",
|
"switchView": "Changer le mode d'affichage",
|
||||||
"toggleSidebar": "Afficher/Masquer la barre latérale",
|
"toggleSidebar": "Afficher/Masquer la barre latérale",
|
||||||
"update": "Mettre à jour",
|
"update": "Mettre à jour",
|
||||||
"upload": "Importer"
|
"upload": "Importer",
|
||||||
|
"openFile": "Ouvrir le fichier"
|
||||||
},
|
},
|
||||||
"download": {
|
"download": {
|
||||||
"downloadFile": "Download File",
|
"downloadFile": "Télécharger le fichier",
|
||||||
"downloadFolder": "Download Folder",
|
"downloadFolder": "Télécharger le dossier",
|
||||||
"downloadSelected": ""
|
"downloadSelected": "Télécharger la selection"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"forbidden": "You don't have permissions to access this.",
|
"forbidden": "Vous n'avez pas la permission d'accéder à cela.",
|
||||||
"internal": "Aïe ! Quelque chose s'est mal passé.",
|
"internal": "Aïe ! Quelque chose s'est mal passé.",
|
||||||
"notFound": "Impossible d'accéder à cet emplacement."
|
"notFound": "Impossible d'accéder à cet emplacement.",
|
||||||
|
"connection": "Le serveur n'est pas accessible."
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"body": "Corps",
|
"body": "Corps",
|
||||||
@@ -60,7 +65,8 @@
|
|||||||
"size": "Taille",
|
"size": "Taille",
|
||||||
"sortByLastModified": "Trier par date de dernière modification",
|
"sortByLastModified": "Trier par date de dernière modification",
|
||||||
"sortByName": "Trier par nom",
|
"sortByName": "Trier par nom",
|
||||||
"sortBySize": "Trier par taille"
|
"sortBySize": "Trier par taille",
|
||||||
|
"noPreview": "Il n'y a pas de prévisualisation pour ce fichier."
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"click": "Sélectionner un élément",
|
"click": "Sélectionner un élément",
|
||||||
@@ -92,20 +98,23 @@
|
|||||||
"ptBR": "Português (Brasil)",
|
"ptBR": "Português (Brasil)",
|
||||||
"ro": "",
|
"ro": "",
|
||||||
"ru": "Русский",
|
"ru": "Русский",
|
||||||
|
"sk": "Slovenčina",
|
||||||
"svSE": "",
|
"svSE": "",
|
||||||
|
"tr" : "Türkçe",
|
||||||
|
"ua": "Українська",
|
||||||
"zhCN": "中文 (简体)",
|
"zhCN": "中文 (简体)",
|
||||||
"zhTW": "中文 (繁體)"
|
"zhTW": "中文 (繁體)"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"createAnAccount": "Create an account",
|
"createAnAccount": "Créer un compte",
|
||||||
"loginInstead": "Already have an account",
|
"loginInstead": "Vous avez déjà un compte",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"passwordConfirm": "Password Confirmation",
|
"passwordConfirm": "Confirmation de mot de passe",
|
||||||
"passwordsDontMatch": "Passwords don't match",
|
"passwordsDontMatch": "Les mots de passe ne concordent pas",
|
||||||
"signup": "Signup",
|
"signup": "S'inscrire",
|
||||||
"submit": "Se connecter",
|
"submit": "Se connecter",
|
||||||
"username": "Utilisateur",
|
"username": "Utilisateur",
|
||||||
"usernameTaken": "Username already taken",
|
"usernameTaken": "Le nom d'utilisateur est déjà pris",
|
||||||
"wrongCredentials": "Identifiants incorrects !"
|
"wrongCredentials": "Identifiants incorrects !"
|
||||||
},
|
},
|
||||||
"permanent": "Permanent",
|
"permanent": "Permanent",
|
||||||
@@ -113,8 +122,9 @@
|
|||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"copyMessage": "Choisissez l'emplacement où copier la sélection :",
|
"copyMessage": "Choisissez l'emplacement où copier la sélection :",
|
||||||
"currentlyNavigating": "Dossier courant :",
|
"currentlyNavigating": "Dossier courant :",
|
||||||
"deleteMessageMultiple": "Etes-vous sûr de vouloir supprimer ces {count} élément(s) ?",
|
"deleteMessageMultiple": "Êtes-vous sûr de vouloir supprimer ces {count} élément(s) ?",
|
||||||
"deleteMessageSingle": "Etes-vous sûr de vouloir supprimer cet élément ?",
|
"deleteMessageSingle": "Êtes-vous sûr de vouloir supprimer cet élément ?",
|
||||||
|
"deleteMessageShare": "Êtes-vous sûr de vouloir supprimer ce partage ({path}) ?",
|
||||||
"deleteTitle": "Supprimer",
|
"deleteTitle": "Supprimer",
|
||||||
"displayName": "Nom :",
|
"displayName": "Nom :",
|
||||||
"download": "Télécharger",
|
"download": "Télécharger",
|
||||||
@@ -140,16 +150,18 @@
|
|||||||
"scheduleMessage": "Choisissez une date pour planifier la publication de ce post",
|
"scheduleMessage": "Choisissez une date pour planifier la publication de ce post",
|
||||||
"show": "Montrer",
|
"show": "Montrer",
|
||||||
"size": "Taille",
|
"size": "Taille",
|
||||||
"upload": "",
|
"upload": "Importer",
|
||||||
"uploadMessage": ""
|
"uploadFiles": "Importation de {files} fichiers...",
|
||||||
|
"uploadMessage": "Séléctionnez une option d'import.",
|
||||||
|
"optionalPassword": "Mot de passe optionnel"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"images": "Images",
|
"images": "Images",
|
||||||
"music": "Musique",
|
"music": "Musique",
|
||||||
"pdf": "PDF",
|
"pdf": "PDF",
|
||||||
"pressToSearch": "Press enter to search...",
|
"pressToSearch": "Appuyez du entrée pour chercher...",
|
||||||
"search": "Recherche en cours...",
|
"search": "Recherche en cours...",
|
||||||
"typeToSearch": "Type to search...",
|
"typeToSearch": "Écrivez pour chercher...",
|
||||||
"types": "Types",
|
"types": "Types",
|
||||||
"video": "Video"
|
"video": "Video"
|
||||||
},
|
},
|
||||||
@@ -160,31 +172,31 @@
|
|||||||
"allowEdit": "Editer, renommer et supprimer des fichiers ou des dossiers",
|
"allowEdit": "Editer, renommer et supprimer des fichiers ou des dossiers",
|
||||||
"allowNew": "Créer de nouveaux fichiers et dossiers",
|
"allowNew": "Créer de nouveaux fichiers et dossiers",
|
||||||
"allowPublish": "Publier de nouveaux posts et pages",
|
"allowPublish": "Publier de nouveaux posts et pages",
|
||||||
"allowSignup": "Allow users to signup",
|
"allowSignup": "Autoriser les utilisateurs à s'inscrire",
|
||||||
"avoidChanges": "(Laisser vide pour conserver l'actuel)",
|
"avoidChanges": "(Laisser vide pour conserver l'actuel)",
|
||||||
"branding": "Branding",
|
"branding": "Image de marque",
|
||||||
"brandingDirectoryPath": "Branding directory path",
|
"brandingDirectoryPath": "Chemin du dossier d'image de marque",
|
||||||
"brandingHelp": "You can customize how your File Browser instance looks and feels by changing its name, replacing the logo, adding custom styles and even disable external links to GitHub.\nFor more information about custom branding, please check out the {0}.",
|
"brandingHelp": "Vous pouvez personnaliser l'apparence de votre instance de File Browser en changeant son nom, en remplaçant le logo, en ajoutant des styles personnalisés et même en désactivant les liens externes vers GitHub.\nPour plus d'informations sur la personnalisation de l'image de marque, veuillez consulter la {0}.",
|
||||||
"changePassword": "Modifier le mot de passe",
|
"changePassword": "Modifier le mot de passe",
|
||||||
"commandRunner": "Command runner",
|
"commandRunner": "Command runner",
|
||||||
"commandRunnerHelp": "Here you can set commands that are executed in the named events. You must write one per line. The environment variables {0} and {1} will be available, being {0} relative to {1}. For more information about this feature and the available environment variables, please read the {2}.",
|
"commandRunnerHelp" : "Ici, vous pouvez définir les commandes qui sont exécutées pour les é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 !",
|
"commandsUpdated": "Commandes mises à jour !",
|
||||||
"createUserDir": "Auto create user home dir while adding new user",
|
"createUserDir": "Créer automatiquement un dossier pour l'utilisateur",
|
||||||
"customStylesheet": "Feuille de style personnalisée",
|
"customStylesheet": "Feuille de style personnalisée",
|
||||||
"defaultUserDescription": "This are the default settings for new users.",
|
"defaultUserDescription": "Paramètres par défaut pour les nouveaux utilisateurs.",
|
||||||
"disableExternalLinks": "Disable external links (except documentation)",
|
"disableExternalLinks": "Désactiver les liens externes (sauf la documentation)",
|
||||||
"documentation": "documentation",
|
"documentation": "documentation",
|
||||||
"examples": "Exemples",
|
"examples": "Exemples",
|
||||||
"executeOnShell": "Execute on shell",
|
"executeOnShell": "Exécuter dans le shell",
|
||||||
"executeOnShellDescription": "By default, File Browser executes the commands by calling their binaries directly. If you want to run them on a shell instead (such as Bash or PowerShell), you can define it here with the required arguments and flags. If set, the command you execute will be appended as an argument. This apply to both user commands and event hooks.",
|
"executeOnShellDescription": "Par défaut, File Browser exécute les commandes en appelant directement leurs binaires. Si vous voulez les exécuter sur un shell à la place (comme Bash ou PowerShell), vous pouvez le définir ici avec les arguments et les drapeaux requis. S'il est défini, la commande que vous exécutez sera ajoutée en tant qu'argument. Cela s'applique à la fois aux commandes utilisateur et aux crochets d'événements.",
|
||||||
"globalRules": "This is a global set of allow and disallow rules. They apply to every user. You can define specific rules on each user's settings to override this ones.",
|
"globalRules": "Il s'agit d'un ensemble global de règles d'autorisation et d'interdiction. Elles s'appliquent à tous les utilisateurs. Vous pouvez définir des règles spécifiques sur les paramètres de chaque utilisateur pour remplacer celles-ci.",
|
||||||
"globalSettings": "Paramètres généraux",
|
"globalSettings": "Paramètres généraux",
|
||||||
"hideDotfiles": "",
|
"hideDotfiles": "Cacher les fichiers de configuration utilisateur (dotfiles)",
|
||||||
"insertPath": "Insert the path",
|
"insertPath": "Insérez le chemin",
|
||||||
"insertRegex": "Insert regex expression",
|
"insertRegex": "Insérez l'expression régulière",
|
||||||
"instanceName": "Instance name",
|
"instanceName": "Nom de l'instance",
|
||||||
"language": "Langue",
|
"language": "Langue",
|
||||||
"lockPassword": "Prevent the user from changing the password",
|
"lockPassword": "Empêcher l'utilisateur de changer son mot de passe",
|
||||||
"newPassword": "Votre nouveau mot de passe",
|
"newPassword": "Votre nouveau mot de passe",
|
||||||
"newPasswordConfirm": "Confirmation du nouveau mot de passe",
|
"newPasswordConfirm": "Confirmation du nouveau mot de passe",
|
||||||
"newUser": "Nouvel Utilisateur",
|
"newUser": "Nouvel Utilisateur",
|
||||||
@@ -192,13 +204,13 @@
|
|||||||
"passwordUpdated": "Mot de passe mis à jour !",
|
"passwordUpdated": "Mot de passe mis à jour !",
|
||||||
"path": "",
|
"path": "",
|
||||||
"perm": {
|
"perm": {
|
||||||
"create": "Create files and directories",
|
"create": "Créer des fichiers et des dossiers",
|
||||||
"delete": "Delete files and directories",
|
"delete": "Supprimer des fichiers et des dossiers",
|
||||||
"download": "Download",
|
"download": "Télécharger",
|
||||||
"execute": "Execute commands",
|
"execute": "Exécuter des commandes",
|
||||||
"modify": "Edit files",
|
"modify": "Modifier des fichiers",
|
||||||
"rename": "Rename or move files and directories",
|
"rename": "Renommer ou déplacer des fichiers ou des dossiers",
|
||||||
"share": "Share files"
|
"share": "Partager des fichiers"
|
||||||
},
|
},
|
||||||
"permissions": "Permissions",
|
"permissions": "Permissions",
|
||||||
"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",
|
"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",
|
||||||
@@ -208,14 +220,16 @@
|
|||||||
"rules": "Règles",
|
"rules": "Règles",
|
||||||
"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",
|
"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",
|
"scope": "Portée du dossier utilisateur",
|
||||||
|
"setDateFormat": "Définir le format de la date",
|
||||||
"settingsUpdated": "Les paramètres ont été mis à jour !",
|
"settingsUpdated": "Les paramètres ont été mis à jour !",
|
||||||
"shareDuration": "",
|
"shareDuration": "Durée du partage",
|
||||||
"shareManagement": "",
|
"shareManagement": "Gestion des partages",
|
||||||
"singleClick": "",
|
"shareDeleted": "Partage supprimé !",
|
||||||
|
"singleClick": "Utiliser un simple clic pour ouvrir les fichiers et les dossiers",
|
||||||
"themes": {
|
"themes": {
|
||||||
"dark": "",
|
"dark": "Sombre",
|
||||||
"light": "",
|
"light": "Lumineux",
|
||||||
"title": ""
|
"title": "Thème"
|
||||||
},
|
},
|
||||||
"user": "Utilisateur",
|
"user": "Utilisateur",
|
||||||
"userCommands": "Commandes",
|
"userCommands": "Commandes",
|
||||||
@@ -238,11 +252,11 @@
|
|||||||
"newFolder": "Nouveau dossier",
|
"newFolder": "Nouveau dossier",
|
||||||
"preview": "Prévisualiser",
|
"preview": "Prévisualiser",
|
||||||
"settings": "Paramètres",
|
"settings": "Paramètres",
|
||||||
"signup": "Signup",
|
"signup": "S'inscrire",
|
||||||
"siteSettings": "Paramètres du site"
|
"siteSettings": "Paramètres du site"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"linkCopied": "Link copied!"
|
"linkCopied": "Lien copié!"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"days": "Jours",
|
"days": "Jours",
|
||||||
|
|||||||
@@ -1,116 +1,126 @@
|
|||||||
import Vue from 'vue'
|
import Vue from "vue";
|
||||||
import VueI18n from 'vue-i18n'
|
import VueI18n from "vue-i18n";
|
||||||
|
|
||||||
import ar from './ar.json'
|
import ar from "./ar.json";
|
||||||
import de from './de.json'
|
import de from "./de.json";
|
||||||
import en from './en.json'
|
import en from "./en.json";
|
||||||
import es from './es.json'
|
import es from "./es.json";
|
||||||
import fr from './fr.json'
|
import fr from "./fr.json";
|
||||||
import is from './is.json'
|
import is from "./is.json";
|
||||||
import it from './it.json'
|
import it from "./it.json";
|
||||||
import ja from './ja.json'
|
import ja from "./ja.json";
|
||||||
import ko from './ko.json'
|
import ko from "./ko.json";
|
||||||
import nlBE from './nl-be.json'
|
import nlBE from "./nl-be.json";
|
||||||
import pl from './pl.json'
|
import pl from "./pl.json";
|
||||||
import pt from './pt.json'
|
import pt from "./pt.json";
|
||||||
import ptBR from './pt-br.json'
|
import ptBR from "./pt-br.json";
|
||||||
import ro from './ro.json'
|
import ro from "./ro.json";
|
||||||
import ru from './ru.json'
|
import ru from "./ru.json";
|
||||||
import svSE from './sv-se.json'
|
import sk from "./sk.json";
|
||||||
import zhCN from './zh-cn.json'
|
import ua from "./ua.json";
|
||||||
import zhTW from './zh-tw.json'
|
import svSE from "./sv-se.json";
|
||||||
|
import zhCN from "./zh-cn.json";
|
||||||
|
import zhTW from "./zh-tw.json";
|
||||||
|
|
||||||
Vue.use(VueI18n)
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
export function detectLocale () {
|
export function detectLocale() {
|
||||||
let locale = (navigator.language || navigator.browserLangugae).toLowerCase()
|
let locale = (navigator.language || navigator.browserLangugae).toLowerCase();
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case /^ar.*/i.test(locale):
|
case /^ar.*/i.test(locale):
|
||||||
locale = 'ar'
|
locale = "ar";
|
||||||
break
|
break;
|
||||||
case /^es.*/i.test(locale):
|
case /^es.*/i.test(locale):
|
||||||
locale = 'es'
|
locale = "es";
|
||||||
break
|
break;
|
||||||
case /^en.*/i.test(locale):
|
case /^en.*/i.test(locale):
|
||||||
locale = 'en'
|
locale = "en";
|
||||||
break
|
break;
|
||||||
case /^it.*/i.test(locale):
|
case /^it.*/i.test(locale):
|
||||||
locale = 'it'
|
locale = "it";
|
||||||
break
|
break;
|
||||||
case /^fr.*/i.test(locale):
|
case /^fr.*/i.test(locale):
|
||||||
locale = 'fr'
|
locale = "fr";
|
||||||
break
|
break;
|
||||||
case /^pt.*/i.test(locale):
|
case /^pt.*/i.test(locale):
|
||||||
locale = 'pt'
|
locale = "pt";
|
||||||
break
|
break;
|
||||||
case /^pt-BR.*/i.test(locale):
|
case /^pt-BR.*/i.test(locale):
|
||||||
locale = 'pt-br'
|
locale = "pt-br";
|
||||||
break
|
break;
|
||||||
case /^ja.*/i.test(locale):
|
case /^ja.*/i.test(locale):
|
||||||
locale = 'ja'
|
locale = "ja";
|
||||||
break
|
break;
|
||||||
case /^zh-CN/i.test(locale):
|
case /^zh-CN/i.test(locale):
|
||||||
locale = 'zh-cn'
|
locale = "zh-cn";
|
||||||
break
|
break;
|
||||||
case /^zh-TW/i.test(locale):
|
case /^zh-TW/i.test(locale):
|
||||||
locale = 'zh-tw'
|
locale = "zh-tw";
|
||||||
break
|
break;
|
||||||
case /^zh.*/i.test(locale):
|
case /^zh.*/i.test(locale):
|
||||||
locale = 'zh-cn'
|
locale = "zh-cn";
|
||||||
break
|
break;
|
||||||
case /^de.*/i.test(locale):
|
case /^de.*/i.test(locale):
|
||||||
locale = 'de'
|
locale = "de";
|
||||||
break
|
break;
|
||||||
case /^ru.*/i.test(locale):
|
case /^ru.*/i.test(locale):
|
||||||
locale = 'ru'
|
locale = "ru";
|
||||||
break
|
break;
|
||||||
case /^pl.*/i.test(locale):
|
case /^pl.*/i.test(locale):
|
||||||
locale = 'pl'
|
locale = "pl";
|
||||||
break
|
break;
|
||||||
case /^ko.*/i.test(locale):
|
case /^ko.*/i.test(locale):
|
||||||
locale = 'ko'
|
locale = "ko";
|
||||||
break
|
break;
|
||||||
|
case /^sk.*/i.test(locale):
|
||||||
|
locale = "sk";
|
||||||
|
break;
|
||||||
|
case /^ua.*/i.test(locale):
|
||||||
|
locale = "ua";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
locale = 'en'
|
locale = "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
return locale
|
return locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeEmpty = (obj) =>
|
const removeEmpty = (obj) =>
|
||||||
Object.keys(obj)
|
Object.keys(obj)
|
||||||
.filter((k) => obj[k] !== null && obj[k] !== undefined && obj[k] !== '') // Remove undef. and null and empty.string.
|
.filter((k) => obj[k] !== null && obj[k] !== undefined && obj[k] !== "") // Remove undef. and null and empty.string.
|
||||||
.reduce(
|
.reduce(
|
||||||
(newObj, k) =>
|
(newObj, k) =>
|
||||||
typeof obj[k] === 'object'
|
typeof obj[k] === "object"
|
||||||
? Object.assign(newObj, { [k]: removeEmpty(obj[k]) }) // Recurse.
|
? Object.assign(newObj, { [k]: removeEmpty(obj[k]) }) // Recurse.
|
||||||
: Object.assign(newObj, { [k]: obj[k] }), // Copy value.
|
: Object.assign(newObj, { [k]: obj[k] }), // Copy value.
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
locale: detectLocale(),
|
locale: detectLocale(),
|
||||||
fallbackLocale: 'en',
|
fallbackLocale: "en",
|
||||||
messages: {
|
messages: {
|
||||||
'ar': removeEmpty(ar),
|
ar: removeEmpty(ar),
|
||||||
'de': removeEmpty(de),
|
de: removeEmpty(de),
|
||||||
'en': en,
|
en: en,
|
||||||
'es': removeEmpty(es),
|
es: removeEmpty(es),
|
||||||
'fr': removeEmpty(fr),
|
fr: removeEmpty(fr),
|
||||||
'is': removeEmpty(is),
|
is: removeEmpty(is),
|
||||||
'it': removeEmpty(it),
|
it: removeEmpty(it),
|
||||||
'ja': removeEmpty(ja),
|
ja: removeEmpty(ja),
|
||||||
'ko': removeEmpty(ko),
|
ko: removeEmpty(ko),
|
||||||
'nl-be': removeEmpty(nlBE),
|
"nl-be": removeEmpty(nlBE),
|
||||||
'pl': removeEmpty(pl),
|
pl: removeEmpty(pl),
|
||||||
'pt-br': removeEmpty(ptBR),
|
"pt-br": removeEmpty(ptBR),
|
||||||
'pt': removeEmpty(pt),
|
pt: removeEmpty(pt),
|
||||||
'ru': removeEmpty(ru),
|
ru: removeEmpty(ru),
|
||||||
'ro': removeEmpty(ro),
|
ro: removeEmpty(ro),
|
||||||
'sv-se': removeEmpty(svSE),
|
sk: removeEmpty(sk),
|
||||||
'zh-cn': removeEmpty(zhCN),
|
"sv-se": removeEmpty(svSE),
|
||||||
'zh-tw': removeEmpty(zhTW)
|
ua: removeEmpty(ua),
|
||||||
}
|
"zh-cn": removeEmpty(zhCN),
|
||||||
})
|
"zh-tw": removeEmpty(zhTW),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default i18n
|
export default i18n;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user