Compare commits
4
Commits
beta/v0.8.3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97e5f12f7a | ||
|
|
256f4ca0b8 | ||
|
|
831708fe65 | ||
|
|
50bd853266 |
No files matched your search
@@ -39,7 +39,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: frontend/dist
|
||||
path: backend/http/dist
|
||||
build_backend:
|
||||
name: Build backend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+41
-1
@@ -2,6 +2,46 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
|
||||
|
||||
## v0.8.4-beta
|
||||
|
||||
**New Features**:
|
||||
- New media player styles and features
|
||||
- Custom Media Player: enhanced media player using plyr thanks @Kurami32 (see #1160)
|
||||
- Custom Media Player: also adds support for metadata
|
||||
- added embeded video subtitle support (for both native and custom player). @maxbin123 #1072 #1157
|
||||
- Users can disable the customer player and opt of native in profile settings.
|
||||
- Option to disable backend update check via `server.disableUpdateCheck` #1134
|
||||
- added `frontend.favicon` and `frontend.description` for html overrides
|
||||
- onlyoffice is now supported in shares. Both viewing and editing can be configured per-share.
|
||||
- Added only office debug view and wiki to assist with debugging issues #1068 #911 #1074
|
||||
- Dark mode enforcement possible for shared links #1029
|
||||
- added `System & Admin` section to settings
|
||||
- includes a new config viewer to see current running config (hides secrets) #838
|
||||
- added `server.minSearchLength` to allow adjusting the length requirement for search #1174
|
||||
|
||||
**Notes**:
|
||||
- access management: specific folders/files with access are shown instead permission denied for parent folder
|
||||
- navigation no longer appends last location hash which should fix some unwanted navation behavior #1070
|
||||
- altered the context menu style and behavior.
|
||||
- documentation update: comma or Space separated extensions #1138
|
||||
- Files and folders can be created with "/" or "\" on the name #1126
|
||||
- Share management should not be allowed without authentication #1163
|
||||
- Question about customizing session timeout #1184
|
||||
|
||||
**BugFixes**:
|
||||
- access management: delay showing rule changes in the list fixed. #1131
|
||||
- Color names are not localized #1159
|
||||
- rename issues #1170 #1171
|
||||
- some shortcuts not working #1056
|
||||
- Can't copy/paste text on mobile #1168
|
||||
- Can't change between images inside of the share image viewer. #1144
|
||||
- fixed and updated translations with variables always showing english.
|
||||
|
||||
## v0.8.3-beta
|
||||
|
||||
**BugFixes**:
|
||||
- fixed search bar style bug in mobile #1147
|
||||
|
||||
## v0.8.2-beta
|
||||
|
||||
**New Features**:
|
||||
@@ -13,7 +53,7 @@ All notable changes to this project will be documented in this file. For commit
|
||||
**Notes**:
|
||||
- 8.0 ffmpeg version bundled with docker
|
||||
- go 1.25 upgrade with green tea GC enabled
|
||||
- totp secrets accept non-secure strings, only throwing warning
|
||||
- totp secrets accept non-secure strings, only throwing warning
|
||||
- adjusted download limit so it also counts viewing text "content" of files (like in editor). You can also "disable file viewing" to stop the editor from showing. lower quality file image previews are not counted as downloads.
|
||||
- updated invalid share message to be more clear https://github.com/gtsteffaniak/filebrowser/issues/1120
|
||||
|
||||
|
||||
+11
-22
@@ -4,7 +4,7 @@ Thank you for your interest in contributing to FileBrowser Quantum! This guide w
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Go 1.24+** (see `backend/go.mod`)
|
||||
- **Go 1.25+** (see `backend/go.mod`)
|
||||
- **Node.js 18.0.0+** with npm 7.0.0+ (see `frontend/package.json`)
|
||||
- **Docker** (optional, for containerized development)
|
||||
- **Git**
|
||||
@@ -31,8 +31,8 @@ cd filebrowser
|
||||
# 2. Initial setup - installs dependencies and creates test config
|
||||
make setup
|
||||
|
||||
# 3. Run in development mode
|
||||
make run
|
||||
# 3. Run in development mode with hot-reloading
|
||||
make dev
|
||||
```
|
||||
|
||||
`make setup` installs all dependencies and creates a test configuration file.
|
||||
@@ -52,14 +52,14 @@ make run
|
||||
- **Framework**: Vue 3 + Vite + TypeScript
|
||||
- **State**: Custom store in `frontend/src/store/`
|
||||
- **API Client**: Axios-based in `frontend/src/api/`
|
||||
- **i18n**: 25+ languages with English as master
|
||||
- **i18n**: 25+ languages with English as main
|
||||
- **Components**: Feature-based organization
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Essential Commands
|
||||
```bash
|
||||
make run # Start development server
|
||||
make dev # Start development server with hot-reloading
|
||||
make test # Run all tests
|
||||
make lint # Check code quality
|
||||
make check-all # Lint + tests
|
||||
@@ -70,12 +70,12 @@ make build # Build Docker image
|
||||
```
|
||||
|
||||
### Frontend Development
|
||||
|
||||
Note: consider using make commands above instead.
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev # Dev server with hot reload
|
||||
npm run lint:fix # Auto-fix linting issues
|
||||
npm run typecheck # TypeScript validation
|
||||
npm run i18n:sync # Sync translations
|
||||
npm run i18n:sync # Sync translations changes
|
||||
```
|
||||
|
||||
## Testing
|
||||
@@ -128,12 +128,11 @@ make build-backend # Build Go binary with embedded assets
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
make build # Full image with ffmpeg (~200MB)
|
||||
make run-proxy # Docker Compose with nginx proxy
|
||||
make build # Full image with ffmpeg and muPDF
|
||||
```
|
||||
|
||||
### Configuration
|
||||
- **Test Config**: `backend/test_config.yaml` Auto-generated after running `make setup`
|
||||
- **Test Config**: `backend/test_config.yaml` Auto-generated after running `make setup`
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -141,7 +140,7 @@ make run-proxy # Docker Compose with nginx proxy
|
||||
|
||||
1. Fork and create a feature branch
|
||||
2. Make your changes following the code standards
|
||||
3. Run `make run` to build and run with your changes
|
||||
3. Run `make dev` to build and run with your changes. Supports hot-reloading frontend and backend changes.
|
||||
4. When ready, run `make check-all` to verify tests and linting
|
||||
5. Submit PR with a clear description
|
||||
|
||||
@@ -175,16 +174,6 @@ cd frontend && rm -rf node_modules && npm install
|
||||
cd backend && go mod tidy && go clean -modcache
|
||||
```
|
||||
|
||||
**Debugging:**
|
||||
```bash
|
||||
# Backend with debug logs
|
||||
cd backend
|
||||
FILEBROWSER_NO_EMBEDDED=true go run -tags mupdf . -c test_config.yaml --log-level debug
|
||||
|
||||
# API documentation
|
||||
# Visit /swagger endpoint when running
|
||||
```
|
||||
|
||||
**Authentication Issues:**
|
||||
|
||||
Always first enable debug logging in your config file if you have issues.
|
||||
|
||||
+3
-1
@@ -16,6 +16,9 @@ RUN upx filebrowser
|
||||
FROM node:lts-slim AS nbuild
|
||||
WORKDIR /app
|
||||
COPY ./frontend/package.json ./
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \
|
||||
&& git config --global url."https://github.com/".insteadOf "git@github.com:" \
|
||||
&& git config --global url."https://github.com/".insteadOf "ssh://git@github.com/"
|
||||
RUN npm i --maxsockets 1
|
||||
COPY ./frontend/ ./
|
||||
RUN npm run build-docker
|
||||
@@ -24,7 +27,6 @@ FROM alpine:latest
|
||||
COPY --from=ffmpeg [ "/ffmpeg", "/ffprobe", "/usr/local/bin/" ]
|
||||
ENV FILEBROWSER_FFMPEG_PATH="/usr/local/bin/"
|
||||
ENV FILEBROWSER_DATABASE="/home/filebrowser/data/database.db"
|
||||
ENV FILEBROWSER_NO_EMBEDED="true"
|
||||
ENV PATH="$PATH:/home/filebrowser"
|
||||
RUN apk --no-cache add ca-certificates mailcap tzdata
|
||||
RUN adduser -D -s /bin/true -u 1000 filebrowser
|
||||
|
||||
@@ -15,4 +15,4 @@ COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
|
||||
# Run tests
|
||||
RUN ./filebrowser set -u testuser1,testuser1
|
||||
RUN FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -13,4 +13,4 @@ RUN mkdir -p ./http/dist
|
||||
COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
|
||||
# Run tests
|
||||
RUN FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -10,4 +10,4 @@ COPY ["./backend/filebrowser", "./"]
|
||||
COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
# Run tests
|
||||
WORKDIR /app/frontend
|
||||
RUN node mock-oidc-server.js & sleep 2 && cd ../backend/ && FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN node mock-oidc-server.js & sleep 2 && cd ../backend/ && ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -11,4 +11,4 @@ RUN apt update && apt install nginx -y
|
||||
RUN mv default.conf /etc/nginx/conf.d/default.conf
|
||||
RUN mv .htpasswd /etc/nginx/conf.d/.htpasswd
|
||||
RUN sed -i 's/filebrowser/localhost/g' /etc/nginx/conf.d/default.conf
|
||||
RUN nginx & FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN nginx & ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -14,4 +14,4 @@ RUN mkdir -p ./http/dist
|
||||
COPY [ "./backend/http/embed/", "./http/dist/"]
|
||||
|
||||
# Run tests
|
||||
RUN FILEBROWSER_NO_EMBEDED=true ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -14,12 +14,14 @@ RUN upx filebrowser
|
||||
FROM node:lts-slim AS nbuild
|
||||
WORKDIR /app
|
||||
COPY ./frontend/package.json ./
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \
|
||||
&& git config --global url."https://github.com/".insteadOf "git@github.com:" \
|
||||
&& git config --global url."https://github.com/".insteadOf "ssh://git@github.com/"
|
||||
RUN npm i --maxsockets 1
|
||||
COPY ./frontend/ ./
|
||||
RUN npm run build-docker
|
||||
|
||||
FROM alpine:latest
|
||||
ENV FILEBROWSER_NO_EMBEDED="true"
|
||||
ENV FILEBROWSER_DATABASE="/home/filebrowser/data/database.db"
|
||||
ENV PATH="$PATH:/home/filebrowser"
|
||||
RUN apk --no-cache add ca-certificates mailcap tzdata
|
||||
|
||||
@@ -11,7 +11,7 @@ import { defineConfig, devices } from "@playwright/test";
|
||||
*/
|
||||
export default defineConfig({
|
||||
globalSetup: "./tests/playwright/proxy-setup.ts",
|
||||
timeout: 3000,
|
||||
timeout: 5000,
|
||||
testDir: "./tests/playwright/proxy",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: false,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
root = "."
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
cmd = "go build -o ./tmp/filebrowser --tags=mupdf --ldflags=\"-w -s -X 'github.com/gtsteffaniak/filebrowser/backend/version.CommitSHA=testingCommit' -X 'github.com/gtsteffaniak/filebrowser/backend/version.Version=testing'\" ."
|
||||
bin = "tmp/filebrowser"
|
||||
full_bin = "FILEBROWSER_DEVMODE=true CGO_ENABLED=1 ./tmp/filebrowser -c test_config.yaml"
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
exclude_dir = ["tmp", "vendor", "http/dist", "http/embed", "swagger/docs"]
|
||||
log = "air.log"
|
||||
delay = 1000 # ms
|
||||
|
||||
[log]
|
||||
time = true
|
||||
|
||||
[misc]
|
||||
clean_on_exit = true
|
||||
@@ -0,0 +1,5 @@
|
||||
tmp/
|
||||
air.log
|
||||
filebrowser
|
||||
generated.yaml
|
||||
test_config.yaml
|
||||
@@ -28,18 +28,16 @@ import (
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
)
|
||||
|
||||
func FileInfoFaster(opts iteminfo.FileOptions) (iteminfo.ExtendedFileInfo, error) {
|
||||
response := iteminfo.ExtendedFileInfo{}
|
||||
func FileInfoFaster(opts iteminfo.FileOptions) (*iteminfo.ExtendedFileInfo, error) {
|
||||
response := &iteminfo.ExtendedFileInfo{}
|
||||
index := indexing.GetIndex(opts.Source)
|
||||
if index == nil {
|
||||
return response, fmt.Errorf("could not get index: %v ", opts.Source)
|
||||
}
|
||||
if opts.Access != nil && !opts.Access.Permitted(index.Path, opts.Path, opts.Username) {
|
||||
return response, errors.ErrPermissionDenied
|
||||
}
|
||||
|
||||
realPath, isDir, err := index.GetRealPath(opts.Path)
|
||||
if err != nil {
|
||||
return response, err
|
||||
return response, fmt.Errorf("could not get real path for requested path: %v", opts.Path)
|
||||
}
|
||||
opts.IsDir = isDir
|
||||
var info *iteminfo.FileInfo
|
||||
@@ -60,33 +58,71 @@ func FileInfoFaster(opts iteminfo.FileOptions) (iteminfo.ExtendedFileInfo, error
|
||||
return response, fmt.Errorf("could not get metadata for path: %v", opts.Path)
|
||||
}
|
||||
}
|
||||
if opts.Content {
|
||||
if info.Size < 20*1024*1024 { // 20 megabytes in bytes
|
||||
content, err := getContent(realPath)
|
||||
if err != nil {
|
||||
logger.Debugf("could not get content for file: "+info.Path, info.Name, err)
|
||||
return response, err
|
||||
}
|
||||
response.Content = content
|
||||
} else {
|
||||
logger.Debug("skipping large text file contents (20MB limit): "+info.Path, info.Name)
|
||||
}
|
||||
}
|
||||
response.FileInfo = *info
|
||||
response.RealPath = realPath
|
||||
response.Source = index.Name
|
||||
response.Source = opts.Source
|
||||
|
||||
if opts.Access != nil && !opts.Access.Permitted(index.Path, opts.Path, opts.Username) {
|
||||
// check if any subpath is permitted
|
||||
// keep track of permitted paths and only show them at the end
|
||||
subFolders := info.Folders
|
||||
subFiles := info.Files
|
||||
response.Folders = make([]iteminfo.ItemInfo, 0)
|
||||
response.Files = make([]iteminfo.ItemInfo, 0)
|
||||
hasPermittedPaths := false
|
||||
for _, subFolder := range subFolders {
|
||||
indexPath := info.Path + subFolder.Name
|
||||
if opts.Access.Permitted(index.Path, indexPath, opts.Username) {
|
||||
hasPermittedPaths = true
|
||||
response.Folders = append(response.Folders, subFolder)
|
||||
}
|
||||
}
|
||||
for _, subFile := range subFiles {
|
||||
indexPath := info.Path + subFile.Name
|
||||
if opts.Access.Permitted(index.Path, indexPath, opts.Username) {
|
||||
hasPermittedPaths = true
|
||||
response.Files = append(response.Files, subFile)
|
||||
}
|
||||
}
|
||||
if !hasPermittedPaths {
|
||||
return response, errors.ErrPermissionDenied
|
||||
}
|
||||
}
|
||||
if opts.Content {
|
||||
processContent(response, index)
|
||||
}
|
||||
if settings.Config.Integrations.OnlyOffice.Secret != "" && info.Type != "directory" && iteminfo.IsOnlyOffice(info.Name) {
|
||||
response.OnlyOfficeId = generateOfficeId(realPath)
|
||||
}
|
||||
if strings.HasPrefix(info.Type, "video") {
|
||||
parentInfo, exists := index.GetReducedMetadata(filepath.Dir(info.Path), true)
|
||||
if exists {
|
||||
response.DetectSubtitles(parentInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func processContent(info *iteminfo.ExtendedFileInfo, idx *indexing.Index) {
|
||||
isVideo := strings.HasPrefix(info.Type, "video")
|
||||
if isVideo {
|
||||
parentInfo, exists := idx.GetReducedMetadata(filepath.Dir(info.Path), true)
|
||||
if exists {
|
||||
info.DetectSubtitles(parentInfo)
|
||||
err := info.LoadSubtitleContent()
|
||||
if err != nil {
|
||||
logger.Debug("failed to load subtitle content: " + err.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if info.Size < 20*1024*1024 { // 20 megabytes in bytes
|
||||
content, err := getContent(info.RealPath)
|
||||
if err != nil {
|
||||
logger.Debugf("could not get content for file: "+info.RealPath, info.Name, err)
|
||||
return
|
||||
}
|
||||
info.Content = content
|
||||
} else {
|
||||
logger.Debug("skipping large text file contents (20MB limit): "+info.Path, info.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func generateOfficeId(realPath string) string {
|
||||
key, ok := utils.OnlyOfficeCache.Get(realPath).(string)
|
||||
if !ok {
|
||||
|
||||
+1
-1
@@ -332,7 +332,7 @@ func createConfig(configpath string) {
|
||||
}
|
||||
// cleanup database if it exists
|
||||
if _, err := os.Stat(config.Server.Database); err == nil {
|
||||
response := askYesNoQuestion(reader, "Database specified already exists. Move databse file to backup to start fresh?", "no")
|
||||
response := askYesNoQuestion(reader, "Database specified already exists. Move database file to backup to start fresh?", "no")
|
||||
if !response {
|
||||
return
|
||||
}
|
||||
|
||||
+7
-5
@@ -54,12 +54,14 @@ func StartFilebrowser() {
|
||||
if !keepGoing {
|
||||
return
|
||||
}
|
||||
info, _ := utils.CheckForUpdates()
|
||||
if info.LatestVersion != "" {
|
||||
logger.Infof("A new version is available: %s (current: %s)", info.LatestVersion, info.CurrentVersion)
|
||||
logger.Infof("Release notes: %s", info.ReleaseNotes)
|
||||
if !settings.Config.Server.DisableUpdateCheck {
|
||||
info, _ := utils.CheckForUpdates()
|
||||
if info.LatestVersion != "" {
|
||||
logger.Infof("A new version is available: %s (current: %s)", info.LatestVersion, info.CurrentVersion)
|
||||
logger.Infof("Release notes: %s", info.ReleaseNotes)
|
||||
}
|
||||
go utils.StartCheckForUpdates()
|
||||
}
|
||||
go utils.StartCheckForUpdates()
|
||||
|
||||
// Create context and channels for graceful shutdown
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -12,12 +12,12 @@ import (
|
||||
)
|
||||
|
||||
type Auth struct {
|
||||
TokenExpirationHours int `json:"tokenExpirationHours"` // the number of hours until the token expires. Default is 2 hours.
|
||||
TokenExpirationHours int `json:"tokenExpirationHours"` // time in hours each web UI session token is valid for. Default is 2 hours.
|
||||
Methods LoginMethods `json:"methods"`
|
||||
Key string `json:"key"` // the key used to sign the JWT tokens. If not set, a random key will be generated.
|
||||
AdminUsername string `json:"adminUsername"` // the username of the admin user. If not set, the default is "admin".
|
||||
AdminPassword string `json:"adminPassword"` // the password of the admin user. If not set, the default is "admin".
|
||||
TotpSecret string `json:"totpSecret"` // secret used to encrypt TOTP secrets
|
||||
Key string `json:"key"` // secret: the key used to sign the JWT tokens. If not set, a random key will be generated.
|
||||
AdminUsername string `json:"adminUsername"` // secret: the username of the admin user. If not set, the default is "admin".
|
||||
AdminPassword string `json:"adminPassword"` // secret: the password of the admin user. If not set, the default is "admin".
|
||||
TotpSecret string `json:"totpSecret"` // secret: secret used to encrypt TOTP secrets
|
||||
AuthMethods []string `json:"-"`
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ type Recaptcha struct {
|
||||
// OpenID OAuth2.0
|
||||
type OidcConfig struct {
|
||||
Enabled bool `json:"enabled"` // whether to enable OIDC authentication
|
||||
ClientID string `json:"clientId"` // client id of the OIDC application
|
||||
ClientSecret string `json:"clientSecret"` // client secret of the OIDC application
|
||||
ClientID string `json:"clientId"` // secret: client id of the OIDC application
|
||||
ClientSecret string `json:"clientSecret"` // secret: client secret of the OIDC application
|
||||
IssuerUrl string `json:"issuerUrl"` // authorization URL of the OIDC provider
|
||||
Scopes string `json:"scopes"` // scopes to request from the OIDC provider
|
||||
UserIdentifier string `json:"userIdentifier"` // the field value to use as the username. Default is "preferred_username", can also be "email" or "username", or "phone"
|
||||
|
||||
@@ -48,6 +48,9 @@ func Initialize(configFile string) {
|
||||
}
|
||||
|
||||
func setupFrontend(generate bool) {
|
||||
if Config.Server.MinSearchLength == 0 {
|
||||
Config.Server.MinSearchLength = 3
|
||||
}
|
||||
if !Config.Frontend.DisableDefaultLinks {
|
||||
Config.Frontend.ExternalLinks = append(Config.Frontend.ExternalLinks, ExternalLink{
|
||||
Text: fmt.Sprintf("(%v)", version.Version),
|
||||
@@ -56,12 +59,15 @@ func setupFrontend(generate bool) {
|
||||
})
|
||||
Config.Frontend.ExternalLinks = append(Config.Frontend.ExternalLinks, ExternalLink{
|
||||
Text: "Help",
|
||||
Url: "https://github.com/gtsteffaniak/filebrowser/wiki",
|
||||
Url: "help prompt",
|
||||
})
|
||||
}
|
||||
if Config.Frontend.Description == "" {
|
||||
Config.Frontend.Description = "FileBrowser Quantum is a file manager for the web which can be used to manage files on your server"
|
||||
}
|
||||
Config.Frontend.Styling.LightBackground = FallbackColor(Config.Frontend.Styling.LightBackground, "#f5f5f5")
|
||||
Config.Frontend.Styling.DarkBackground = FallbackColor(Config.Frontend.Styling.DarkBackground, "#141D24")
|
||||
Config.Frontend.Styling.CustomCSS = readCustomCSS(Config.Frontend.Styling.CustomCSS)
|
||||
Config.Frontend.Styling.CustomCSSRaw = readCustomCSS(Config.Frontend.Styling.CustomCSS)
|
||||
Config.Frontend.Styling.CustomThemeOptions = map[string]CustomTheme{}
|
||||
Config.Frontend.Styling.CustomThemes = map[string]CustomTheme{}
|
||||
for name, theme := range Config.Frontend.Styling.CustomThemes {
|
||||
@@ -85,6 +91,9 @@ func setupFrontend(generate bool) {
|
||||
if !ok {
|
||||
addCustomTheme("default", "The default theme", "")
|
||||
}
|
||||
|
||||
// Load custom favicon if configured
|
||||
loadCustomFavicon()
|
||||
}
|
||||
|
||||
func getRealPath(path string) string {
|
||||
@@ -225,8 +234,14 @@ func setupLogging() {
|
||||
}
|
||||
}
|
||||
for _, logConfig := range Config.Server.Logging {
|
||||
// Enable debug logging automatically in dev mode
|
||||
levels := logConfig.Levels
|
||||
if os.Getenv("FILEBROWSER_DEVMODE") == "true" {
|
||||
levels = "info|warning|error|debug"
|
||||
}
|
||||
|
||||
logConfig := logger.JsonConfig{
|
||||
Levels: logConfig.Levels,
|
||||
Levels: levels,
|
||||
ApiLevels: logConfig.ApiLevels,
|
||||
Output: logConfig.Output,
|
||||
Utc: logConfig.Utc,
|
||||
@@ -505,6 +520,54 @@ func GetSources(u *users.User) []string {
|
||||
return sources
|
||||
}
|
||||
|
||||
func loadCustomFavicon() {
|
||||
// Check if a custom favicon path is configured
|
||||
if Config.Frontend.Favicon == "" {
|
||||
logger.Debug("No custom favicon configured, using default")
|
||||
return
|
||||
}
|
||||
|
||||
// Get absolute path for the favicon
|
||||
faviconPath, err := filepath.Abs(Config.Frontend.Favicon)
|
||||
if err != nil {
|
||||
logger.Warningf("Could not resolve favicon path '%v': %v", Config.Frontend.Favicon, err)
|
||||
Config.Frontend.Favicon = "" // Unset invalid path
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the favicon file exists and get info
|
||||
stat, err := os.Stat(faviconPath)
|
||||
if err != nil {
|
||||
logger.Warningf("Could not access custom favicon file '%v': %v", faviconPath, err)
|
||||
Config.Frontend.Favicon = "" // Unset invalid path
|
||||
return
|
||||
}
|
||||
|
||||
// Check file size (limit to 1MB for security)
|
||||
const maxFaviconSize = 1024 * 1024 // 1MB
|
||||
if stat.Size() > maxFaviconSize {
|
||||
logger.Warningf("Favicon file '%v' is too large (%d bytes), maximum allowed is %d bytes", faviconPath, stat.Size(), maxFaviconSize)
|
||||
Config.Frontend.Favicon = "" // Unset invalid path
|
||||
return
|
||||
}
|
||||
|
||||
// Validate file format based on extension
|
||||
ext := strings.ToLower(filepath.Ext(faviconPath))
|
||||
switch ext {
|
||||
case ".ico", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp":
|
||||
// Valid favicon formats
|
||||
default:
|
||||
logger.Warningf("Unsupported favicon format '%v', supported formats: .ico, .png, .jpg, .gif, .svg, .webp", ext)
|
||||
Config.Frontend.Favicon = "" // Unset invalid path
|
||||
return
|
||||
}
|
||||
|
||||
// Update to absolute path and mark as valid
|
||||
Config.Frontend.Favicon = faviconPath
|
||||
|
||||
logger.Infof("Successfully validated custom favicon at '%v' (%d bytes, %s)", faviconPath, stat.Size(), ext)
|
||||
}
|
||||
|
||||
func modifyExcludeInclude(config *Source) {
|
||||
normalize := func(s []string, checkExists bool) {
|
||||
for i, v := range s {
|
||||
|
||||
@@ -14,19 +14,44 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// commentsMap[typeName][fieldName] = combined doc+inline comment text
|
||||
type commentsMap map[string]map[string]string
|
||||
// CommentsMap[typeName][fieldName] = combined doc+inline comment text
|
||||
type CommentsMap map[string]map[string]string
|
||||
|
||||
// collectComments parses all Go source in the directory of srcPath and returns commentsMap.
|
||||
func collectComments(srcPath string) (commentsMap, error) {
|
||||
// SecretFieldsMap[typeName][fieldName] = true if field should be redacted
|
||||
type SecretFieldsMap map[string]map[string]bool
|
||||
|
||||
// DeprecatedFieldsMap[typeName][fieldName] = true if field is deprecated
|
||||
type DeprecatedFieldsMap map[string]map[string]bool
|
||||
|
||||
// getStringStyle determines whether a string should be quoted in YAML
|
||||
func getStringStyle(value string) yaml.Style {
|
||||
// Always quote all strings for consistency
|
||||
return yaml.DoubleQuotedStyle
|
||||
}
|
||||
|
||||
// CollectComments parses all Go source in the directory of srcPath and returns CommentsMap.
|
||||
func CollectComments(srcPath string) (CommentsMap, error) {
|
||||
comments, _, _, err := CollectCommentsAndSecrets(srcPath)
|
||||
return comments, err
|
||||
}
|
||||
|
||||
// CollectCommentsAndSecrets parses all Go source and returns comments, secrets, and deprecated field mappings
|
||||
func CollectCommentsAndSecrets(srcPath string) (CommentsMap, SecretFieldsMap, DeprecatedFieldsMap, error) {
|
||||
// parse entire package so we capture comments on all types
|
||||
dir := filepath.Dir(srcPath)
|
||||
dir := srcPath
|
||||
if filepath.IsAbs(srcPath) {
|
||||
// If it's an absolute path to a file, get the directory
|
||||
dir = filepath.Dir(srcPath)
|
||||
}
|
||||
|
||||
fset := token.NewFileSet()
|
||||
pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
out := make(commentsMap)
|
||||
comments := make(CommentsMap)
|
||||
secrets := make(SecretFieldsMap)
|
||||
deprecated := make(DeprecatedFieldsMap)
|
||||
for _, pkg := range pkgs {
|
||||
for _, file := range pkg.Files {
|
||||
for _, decl := range file.Decls {
|
||||
@@ -43,48 +68,81 @@ func collectComments(srcPath string) (commentsMap, error) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
m := make(map[string]string)
|
||||
out[ts.Name.Name] = m
|
||||
typeName := ts.Name.Name
|
||||
commentMap := make(map[string]string)
|
||||
secretMap := make(map[string]bool)
|
||||
deprecatedMap := make(map[string]bool)
|
||||
comments[typeName] = commentMap
|
||||
secrets[typeName] = secretMap
|
||||
deprecated[typeName] = deprecatedMap
|
||||
|
||||
for _, field := range st.Fields.List {
|
||||
if len(field.Names) == 0 {
|
||||
continue
|
||||
}
|
||||
name := field.Names[0].Name
|
||||
var parts []string
|
||||
var fullComment string
|
||||
|
||||
if field.Doc != nil {
|
||||
parts = append(parts, strings.TrimSpace(field.Doc.Text()))
|
||||
docText := strings.TrimSpace(field.Doc.Text())
|
||||
parts = append(parts, docText)
|
||||
fullComment += docText + " "
|
||||
}
|
||||
if field.Comment != nil {
|
||||
parts = append(parts, strings.TrimSpace(field.Comment.Text()))
|
||||
commentText := strings.TrimSpace(field.Comment.Text())
|
||||
parts = append(parts, commentText)
|
||||
fullComment += commentText
|
||||
}
|
||||
|
||||
if len(parts) > 0 {
|
||||
m[name] = strings.Join(parts, " : ")
|
||||
commentMap[name] = strings.Join(parts, " : ")
|
||||
}
|
||||
|
||||
// Check if field should be treated as secret
|
||||
if strings.Contains(strings.ToLower(fullComment), "secret:") {
|
||||
secretMap[name] = true
|
||||
}
|
||||
|
||||
// Check if field should be treated as deprecated
|
||||
if strings.Contains(strings.ToLower(fullComment), "deprecated:") {
|
||||
deprecatedMap[name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
return comments, secrets, deprecated, nil
|
||||
}
|
||||
|
||||
// buildNode constructs a yaml.Node for any Go value, injecting comments on struct fields.
|
||||
func buildNode(v reflect.Value, comm commentsMap) (*yaml.Node, error) {
|
||||
// BuildNode constructs a yaml.Node for any Go value, injecting comments on struct fields.
|
||||
func BuildNode(v reflect.Value, comm CommentsMap) (*yaml.Node, error) {
|
||||
return buildNodeWithDefaults(v, comm, reflect.Value{}, SecretFieldsMap{}, DeprecatedFieldsMap{})
|
||||
}
|
||||
|
||||
// buildNodeWithDefaults constructs a yaml.Node for any Go value, skipping fields that match defaults, redacting secrets, and filtering deprecated fields
|
||||
func buildNodeWithDefaults(v reflect.Value, comm CommentsMap, defaults reflect.Value, secrets SecretFieldsMap, deprecated DeprecatedFieldsMap) (*yaml.Node, error) {
|
||||
// Dereference pointers
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!null", Value: "null"}, nil
|
||||
}
|
||||
return buildNode(v.Elem(), comm)
|
||||
var defaultsElem reflect.Value
|
||||
if defaults.IsValid() && !defaults.IsNil() {
|
||||
defaultsElem = defaults.Elem()
|
||||
}
|
||||
return buildNodeWithDefaults(v.Elem(), comm, defaultsElem, secrets, deprecated)
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
value := v.String()
|
||||
return &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: v.String(),
|
||||
Style: yaml.DoubleQuotedStyle,
|
||||
Value: value,
|
||||
Style: getStringStyle(value),
|
||||
}, nil
|
||||
case reflect.Struct:
|
||||
rt := v.Type()
|
||||
@@ -104,6 +162,26 @@ func buildNode(v reflect.Value, comm commentsMap) (*yaml.Node, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip deprecated fields if filtering is enabled
|
||||
if len(deprecated) > 0 && deprecated[typeName][sf.Name] {
|
||||
continue
|
||||
}
|
||||
|
||||
currentField := v.Field(i)
|
||||
|
||||
// If we have defaults, compare and skip if values match
|
||||
if defaults.IsValid() && i < defaults.NumField() {
|
||||
defaultField := defaults.Field(i)
|
||||
currentValue := currentField.Interface()
|
||||
defaultValue := defaultField.Interface()
|
||||
|
||||
isEqual := reflect.DeepEqual(currentValue, defaultValue)
|
||||
|
||||
if isEqual {
|
||||
continue // Skip this field as it matches the default
|
||||
}
|
||||
}
|
||||
|
||||
// determine key: yaml tag > json tag > field name
|
||||
yamlTag := sf.Tag.Get("yaml")
|
||||
jsonTag := sf.Tag.Get("json")
|
||||
@@ -118,22 +196,43 @@ func buildNode(v reflect.Value, comm commentsMap) (*yaml.Node, error) {
|
||||
|
||||
keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: keyName}
|
||||
|
||||
// attach validate and comments inline
|
||||
// attach validate and comments inline (only when comments are enabled)
|
||||
var parts []string
|
||||
if cm := comm[typeName][sf.Name]; cm != "" {
|
||||
parts = append(parts, cm)
|
||||
}
|
||||
if vt := sf.Tag.Get("validate"); vt != "" {
|
||||
parts = append(parts, fmt.Sprintf(" validate:%s", vt))
|
||||
// Only add validation tags if comments map is not empty (meaning comments are enabled)
|
||||
if len(comm) > 0 {
|
||||
if vt := sf.Tag.Get("validate"); vt != "" {
|
||||
parts = append(parts, fmt.Sprintf(" validate:%s", vt))
|
||||
}
|
||||
}
|
||||
|
||||
if len(parts) > 0 {
|
||||
keyNode.LineComment = strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
valNode, err := buildNode(v.Field(i), comm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Check if this field should be redacted as a secret
|
||||
var valNode *yaml.Node
|
||||
var err error
|
||||
if secrets[typeName][sf.Name] {
|
||||
valNode = &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: "**hidden**",
|
||||
Style: yaml.DoubleQuotedStyle, // Keep secrets quoted for clarity
|
||||
}
|
||||
} else {
|
||||
// Pass through the corresponding default field for recursive comparison
|
||||
var defaultField reflect.Value
|
||||
if defaults.IsValid() && i < defaults.NumField() {
|
||||
defaultField = defaults.Field(i)
|
||||
}
|
||||
|
||||
valNode, err = buildNodeWithDefaults(currentField, comm, defaultField, secrets, deprecated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mapNode.Content = append(mapNode.Content, keyNode, valNode)
|
||||
}
|
||||
@@ -148,7 +247,11 @@ func buildNode(v reflect.Value, comm commentsMap) (*yaml.Node, error) {
|
||||
// placeholder for empty slice of structs
|
||||
if v.Len() == 0 && v.Type().Elem().Kind() == reflect.Struct {
|
||||
zero := reflect.Zero(v.Type().Elem())
|
||||
n, err := buildNode(zero, comm)
|
||||
var defaultElem reflect.Value
|
||||
if defaults.IsValid() && defaults.Len() > 0 {
|
||||
defaultElem = defaults.Index(0)
|
||||
}
|
||||
n, err := buildNodeWithDefaults(zero, comm, defaultElem, secrets, deprecated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,7 +259,11 @@ func buildNode(v reflect.Value, comm commentsMap) (*yaml.Node, error) {
|
||||
return seq, nil
|
||||
}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
n, err := buildNode(v.Index(i), comm)
|
||||
var defaultElem reflect.Value
|
||||
if defaults.IsValid() && i < defaults.Len() {
|
||||
defaultElem = defaults.Index(i)
|
||||
}
|
||||
n, err := buildNodeWithDefaults(v.Index(i), comm, defaultElem, secrets, deprecated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,6 +271,33 @@ func buildNode(v reflect.Value, comm commentsMap) (*yaml.Node, error) {
|
||||
}
|
||||
return seq, nil
|
||||
|
||||
case reflect.Map:
|
||||
mapNode := &yaml.Node{Kind: yaml.MappingNode}
|
||||
|
||||
for _, key := range v.MapKeys() {
|
||||
// Handle key
|
||||
keyStr := fmt.Sprintf("%v", key.Interface())
|
||||
keyNode := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: keyStr,
|
||||
}
|
||||
|
||||
// Handle value recursively
|
||||
mapVal := v.MapIndex(key)
|
||||
var defaultVal reflect.Value
|
||||
if defaults.IsValid() {
|
||||
defaultVal = defaults.MapIndex(key)
|
||||
}
|
||||
|
||||
valNode, err := buildNodeWithDefaults(mapVal, comm, defaultVal, secrets, deprecated)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mapNode.Content = append(mapNode.Content, keyNode, valNode)
|
||||
}
|
||||
return mapNode, nil
|
||||
|
||||
default:
|
||||
n := &yaml.Node{}
|
||||
if err := n.Encode(v.Interface()); err != nil {
|
||||
@@ -186,21 +320,73 @@ func GenerateYaml() {
|
||||
setupSources(true)
|
||||
setupUrls()
|
||||
setupFrontend(true)
|
||||
input := "common/settings/settings.go" // "path to Go source file or directory containing structs"
|
||||
output := "generated.yaml" // "output YAML file"
|
||||
output := "generated.yaml" // "output YAML file"
|
||||
|
||||
comm, err := collectComments(input)
|
||||
// Generate YAML with comments enabled, full config, and deprecated fields filtered
|
||||
// Force the source path to be correct for static generation
|
||||
yamlContent, err := GenerateConfigYamlWithSource(&Config, true, true, true, "common/settings")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing comments: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "error generating YAML: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
node, err := buildNode(reflect.ValueOf(Config), comm)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error building YAML node: %v\n", err)
|
||||
if err := os.WriteFile(output, []byte(yamlContent), 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing YAML: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Generated YAML with comments (deprecated fields filtered): %s\n", output)
|
||||
}
|
||||
|
||||
// GenerateConfigYaml generates YAML from a given config with options for comments and filtering
|
||||
func GenerateConfigYaml(config *Settings, showComments bool, showFull bool, filterDeprecated bool) (string, error) {
|
||||
// Try different source paths to handle both runtime and test scenarios
|
||||
sourcePaths := []string{
|
||||
"common/settings", // When running from backend directory
|
||||
".", // When running tests from settings directory
|
||||
"../settings", // Alternative test path
|
||||
}
|
||||
|
||||
for _, sourcePath := range sourcePaths {
|
||||
yamlOutput, err := GenerateConfigYamlWithSource(config, showComments, showFull, filterDeprecated, sourcePath)
|
||||
if err == nil {
|
||||
return yamlOutput, nil
|
||||
}
|
||||
// If it's not a file not found error, return immediately
|
||||
if !strings.Contains(err.Error(), "no such file or directory") && !strings.Contains(err.Error(), "cannot find") {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// If all paths failed, try with empty maps to at least generate basic YAML
|
||||
return GenerateConfigYamlWithEmptyMaps(config, showFull)
|
||||
}
|
||||
|
||||
// GenerateConfigYamlWithEmptyMaps generates YAML without comment parsing when source files are unavailable
|
||||
func GenerateConfigYamlWithEmptyMaps(config *Settings, showFull bool) (string, error) {
|
||||
// Create empty maps
|
||||
comm := make(CommentsMap)
|
||||
secrets := make(SecretFieldsMap)
|
||||
deprecated := make(DeprecatedFieldsMap)
|
||||
|
||||
var node *yaml.Node
|
||||
var err error
|
||||
|
||||
if showFull {
|
||||
// Show the full current config
|
||||
node, err = buildNodeWithDefaults(reflect.ValueOf(config), comm, reflect.Value{}, secrets, deprecated)
|
||||
} else {
|
||||
// Show only non-default values by comparing with defaults during node building
|
||||
defaultConfig := setDefaults(true)
|
||||
defaultConfig.Server.Sources = []Source{{Path: "."}}
|
||||
|
||||
node, err = buildNodeWithDefaults(reflect.ValueOf(config), comm, reflect.ValueOf(&defaultConfig), secrets, deprecated)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Convert to YAML
|
||||
doc := &yaml.Node{Kind: yaml.DocumentNode}
|
||||
doc.Content = []*yaml.Node{node}
|
||||
|
||||
@@ -208,17 +394,78 @@ func GenerateYaml() {
|
||||
enc := yaml.NewEncoder(&rawBuf)
|
||||
enc.SetIndent(2)
|
||||
if err := enc.Encode(doc); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error encoding YAML: %v\n", err)
|
||||
os.Exit(1)
|
||||
return "", err
|
||||
}
|
||||
|
||||
aligned := alignComments(rawBuf.String())
|
||||
return AlignComments(rawBuf.String()), nil
|
||||
}
|
||||
|
||||
if err := os.WriteFile(output, []byte(aligned), 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing YAML: %v\n", err)
|
||||
os.Exit(1)
|
||||
// GenerateConfigYamlWithSource generates YAML from a given config with options for comments and filtering, using a custom source path
|
||||
func GenerateConfigYamlWithSource(config *Settings, showComments bool, showFull bool, filterDeprecated bool, sourcePath string) (string, error) {
|
||||
var comm CommentsMap
|
||||
var secrets SecretFieldsMap
|
||||
var deprecated DeprecatedFieldsMap
|
||||
var err error
|
||||
|
||||
if showComments {
|
||||
// Collect comments, secrets, and deprecated fields from the settings source files
|
||||
comm, secrets, deprecated, err = CollectCommentsAndSecrets(sourcePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// Still need to collect secrets and deprecated fields even if not showing comments
|
||||
_, secrets, deprecated, err = CollectCommentsAndSecrets(sourcePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Create empty comments map
|
||||
comm = make(CommentsMap)
|
||||
}
|
||||
fmt.Printf("Generated YAML with comments: %s\n", output)
|
||||
|
||||
// If not filtering deprecated fields, clear the deprecated map
|
||||
if !filterDeprecated {
|
||||
deprecated = make(DeprecatedFieldsMap)
|
||||
}
|
||||
|
||||
var node *yaml.Node
|
||||
|
||||
if showFull {
|
||||
// Show the full current config
|
||||
node, err = buildNodeWithDefaults(reflect.ValueOf(config), comm, reflect.Value{}, secrets, deprecated)
|
||||
} else {
|
||||
// Show only non-default values by comparing with defaults during node building
|
||||
// Create a clean default config (no file loading, just pure defaults)
|
||||
defaultConfig := setDefaults(true)
|
||||
// Apply same setup as a fresh instance would have
|
||||
defaultConfig.Server.Sources = []Source{{Path: "."}}
|
||||
|
||||
node, err = buildNodeWithDefaults(reflect.ValueOf(config), comm, reflect.ValueOf(&defaultConfig), secrets, deprecated)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create document
|
||||
doc := &yaml.Node{Kind: yaml.DocumentNode}
|
||||
doc.Content = []*yaml.Node{node}
|
||||
|
||||
// Encode to YAML
|
||||
var rawBuf bytes.Buffer
|
||||
enc := yaml.NewEncoder(&rawBuf)
|
||||
enc.SetIndent(2)
|
||||
if err := enc.Encode(doc); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Apply comment alignment if comments are enabled
|
||||
yamlOutput := rawBuf.String()
|
||||
if showComments {
|
||||
yamlOutput = AlignComments(yamlOutput)
|
||||
}
|
||||
|
||||
return yamlOutput, nil
|
||||
}
|
||||
|
||||
// formatLine applies padding to a single line so its comment starts at a target column.
|
||||
@@ -257,8 +504,8 @@ func formatLine(line string) string {
|
||||
return line
|
||||
}
|
||||
|
||||
// alignComments formats each line of the YAML string independently.
|
||||
func alignComments(input string) string {
|
||||
// AlignComments formats each line of the YAML string independently.
|
||||
func AlignComments(input string) string {
|
||||
lines := strings.Split(input, "\n")
|
||||
outputLines := make([]string, len(lines))
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateConfigYaml_Basic(t *testing.T) {
|
||||
reNumber := regexp.MustCompile(`^-?\d+(\.\d+)?$`)
|
||||
// Test using the actual source directory structure
|
||||
config := &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
Locale: "es", // Non-default value
|
||||
DarkMode: true, // Default value
|
||||
DisableOfficePreviewExt: ".docx .xlsx", // This field is deprecated
|
||||
},
|
||||
Auth: Auth{
|
||||
Key: "secret123", // This is a secret field
|
||||
AdminUsername: "testadmin", // This is a secret field
|
||||
AdminPassword: "testpassword", // This is a secret field
|
||||
},
|
||||
}
|
||||
|
||||
// Test different combinations
|
||||
testCases := []struct {
|
||||
name string
|
||||
showComments bool
|
||||
showFull bool
|
||||
filterDeprecated bool
|
||||
expectSecrets bool
|
||||
expectDeprecated bool
|
||||
}{
|
||||
{
|
||||
name: "API_mode_all_fields",
|
||||
showComments: false,
|
||||
showFull: true,
|
||||
filterDeprecated: false,
|
||||
expectSecrets: true, // Secrets should be hidden
|
||||
expectDeprecated: true, // Deprecated should be shown
|
||||
},
|
||||
{
|
||||
name: "Static_config_filter_deprecated",
|
||||
showComments: true,
|
||||
showFull: true,
|
||||
filterDeprecated: true,
|
||||
expectSecrets: true, // Secrets should be hidden
|
||||
expectDeprecated: false, // Deprecated should be filtered
|
||||
},
|
||||
{
|
||||
name: "Filtered_non_defaults",
|
||||
showComments: false,
|
||||
showFull: false,
|
||||
filterDeprecated: false,
|
||||
expectSecrets: true, // Secrets should be hidden
|
||||
expectDeprecated: false, // Might not appear if matches default
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Use the current directory structure for real testing
|
||||
yamlOutput, err := GenerateConfigYaml(config, tc.showComments, tc.showFull, tc.filterDeprecated)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml failed: %v", err)
|
||||
}
|
||||
|
||||
// Basic validation - should produce some output
|
||||
if len(yamlOutput) == 0 {
|
||||
t.Fatal("Generated empty YAML")
|
||||
}
|
||||
|
||||
// Test string quoting - string values should be quoted, numbers and booleans should not
|
||||
lines := strings.Split(yamlOutput, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, ":") && !strings.HasSuffix(strings.TrimSpace(line), ":") {
|
||||
// This is a key-value line, skip comment portion
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
value := strings.TrimSpace(parts[1])
|
||||
// Remove comments
|
||||
if commentIdx := strings.Index(value, "#"); commentIdx >= 0 {
|
||||
value = strings.TrimSpace(value[:commentIdx])
|
||||
}
|
||||
|
||||
// Skip empty values, arrays, and objects
|
||||
if value == "" || strings.HasPrefix(value, "[") || strings.HasPrefix(value, "{") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it's a number (should NOT be quoted)
|
||||
if reNumber.MatchString(value) {
|
||||
if strings.HasPrefix(value, "\"") {
|
||||
t.Errorf("Numeric value should not be quoted in line: %s (value: '%s')", line, value)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it's a boolean (should NOT be quoted)
|
||||
if value == "true" || value == "false" {
|
||||
if strings.HasPrefix(value, "\"") {
|
||||
t.Errorf("Boolean value should not be quoted in line: %s (value: '%s')", line, value)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Everything else should be a quoted string
|
||||
if !strings.HasPrefix(value, "\"") || !strings.HasSuffix(value, "\"") {
|
||||
// Add debug info to see what's wrong
|
||||
t.Logf("DEBUG: Checking value '%s' from line '%s'", value, strings.TrimSpace(line))
|
||||
t.Errorf("String value should be quoted in line: %s (value: %s)", strings.TrimSpace(line), value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test secret hiding
|
||||
if tc.expectSecrets {
|
||||
if !strings.Contains(yamlOutput, "**hidden**") {
|
||||
t.Error("Expected secrets to be hidden with **hidden**")
|
||||
}
|
||||
if strings.Contains(yamlOutput, "secret123") || strings.Contains(yamlOutput, "testadmin") || strings.Contains(yamlOutput, "testpassword") {
|
||||
t.Error("Secret values should not appear in output")
|
||||
}
|
||||
}
|
||||
|
||||
// Test deprecated field filtering
|
||||
hasDeprecated := strings.Contains(yamlOutput, "disableOfficePreviewExt")
|
||||
if tc.expectDeprecated && !hasDeprecated {
|
||||
t.Error("Expected deprecated field to be present")
|
||||
}
|
||||
if !tc.expectDeprecated && hasDeprecated && tc.filterDeprecated {
|
||||
t.Error("Expected deprecated field to be filtered out when filterDeprecated=true")
|
||||
}
|
||||
|
||||
// Test comments
|
||||
hasComments := strings.Contains(yamlOutput, "#")
|
||||
if tc.showComments && !hasComments {
|
||||
t.Error("Expected comments to be present when showComments=true")
|
||||
}
|
||||
|
||||
t.Logf("Test case: %s\nGenerated YAML:\n%s\n", tc.name, yamlOutput)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectCommentsAndSecrets_Basic(t *testing.T) {
|
||||
// Test the comment and secret collection directly
|
||||
comments, secrets, deprecated, err := CollectCommentsAndSecrets(".")
|
||||
if err != nil {
|
||||
t.Fatalf("CollectCommentsAndSecrets failed: %v", err)
|
||||
}
|
||||
|
||||
// Should find some secrets in the actual source files
|
||||
foundSecrets := false
|
||||
for typeName, typeSecrets := range secrets {
|
||||
for fieldName := range typeSecrets {
|
||||
t.Logf("Found secret: %s.%s", typeName, fieldName)
|
||||
foundSecrets = true
|
||||
}
|
||||
}
|
||||
if !foundSecrets {
|
||||
t.Error("Expected to find some secret fields in the actual source code")
|
||||
}
|
||||
|
||||
// Should find some deprecated fields
|
||||
foundDeprecated := false
|
||||
for typeName, typeDeprecated := range deprecated {
|
||||
for fieldName := range typeDeprecated {
|
||||
t.Logf("Found deprecated: %s.%s", typeName, fieldName)
|
||||
foundDeprecated = true
|
||||
}
|
||||
}
|
||||
if !foundDeprecated {
|
||||
t.Error("Expected to find some deprecated fields in the actual source code")
|
||||
}
|
||||
|
||||
t.Logf("Comments: %d types", len(comments))
|
||||
t.Logf("Secrets: %d types", len(secrets))
|
||||
t.Logf("Deprecated: %d types", len(deprecated))
|
||||
}
|
||||
|
||||
func TestGenerateYaml_StaticGeneration(t *testing.T) {
|
||||
// Test the static generation function that's used by FILEBROWSER_GENERATE_CONFIG=true
|
||||
|
||||
// Create a temporary config for testing
|
||||
config := &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
Locale: "en",
|
||||
DisableOfficePreviewExt: ".docx .xlsx", // This should be filtered out
|
||||
},
|
||||
Auth: Auth{
|
||||
Key: "test-secret", // This should be redacted
|
||||
},
|
||||
}
|
||||
|
||||
// Test static generation with comments and deprecated filtering
|
||||
yamlOutput, err := GenerateConfigYamlWithSource(config, true, true, true, ".")
|
||||
if err != nil {
|
||||
t.Fatalf("Static generation failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify comments are included
|
||||
if !strings.Contains(yamlOutput, "#") {
|
||||
t.Error("Static generation should include comments")
|
||||
}
|
||||
|
||||
// Verify deprecated field is filtered out
|
||||
if strings.Contains(yamlOutput, "disableOfficePreviewExt") {
|
||||
t.Error("Static generation should filter out deprecated field disableOfficePreviewExt")
|
||||
}
|
||||
|
||||
// Verify secrets are redacted
|
||||
if !strings.Contains(yamlOutput, "**hidden**") {
|
||||
t.Error("Static generation should redact secrets")
|
||||
}
|
||||
if strings.Contains(yamlOutput, "test-secret") {
|
||||
t.Error("Static generation should not contain actual secret values")
|
||||
}
|
||||
|
||||
// Verify it has proper structure
|
||||
if !strings.Contains(yamlOutput, "userDefaults:") {
|
||||
t.Error("Static generation should contain userDefaults section")
|
||||
}
|
||||
if !strings.Contains(yamlOutput, "auth:") {
|
||||
t.Error("Static generation should contain auth section")
|
||||
}
|
||||
|
||||
t.Logf("Static generation successful with comments and proper filtering")
|
||||
}
|
||||
@@ -0,0 +1,470 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test structs for comprehensive testing
|
||||
type TestConfig struct {
|
||||
PublicField string `json:"publicField"` // A regular public field
|
||||
SecretField string `json:"secretField"` // secret: this should be hidden
|
||||
DeprecatedField string `json:"deprecatedField"` // deprecated: this field is old
|
||||
EmptyField string `json:"emptyField"` // An empty string field
|
||||
DefaultField string `json:"defaultField"` // A field that matches default
|
||||
NonDefaultField string `json:"nonDefaultField"` // A field that differs from default
|
||||
NestedStruct TestNested `json:"nestedStruct"`
|
||||
}
|
||||
|
||||
type TestNested struct {
|
||||
NestedPublic string `json:"nestedPublic"` // A nested public field
|
||||
NestedSecret string `json:"nestedSecret"` // secret: nested secret field
|
||||
NestedDeprecated string `json:"nestedDeprecated"` // deprecated: nested deprecated field
|
||||
}
|
||||
|
||||
// Helper to create test source files for comment parsing
|
||||
func createTestSourceFile(t *testing.T, content string) (string, func()) {
|
||||
tmpFile, err := os.CreateTemp("", "test_*.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := tmpFile.WriteString(content); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
cleanup := func() {
|
||||
os.Remove(tmpFile.Name())
|
||||
}
|
||||
|
||||
return tmpFile.Name(), cleanup
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_StringQuoting(t *testing.T) {
|
||||
reNumber := regexp.MustCompile(`^-?\d+(\.\d+)?$`)
|
||||
// Create a simple test source file for this test
|
||||
sourceContent := `package settings
|
||||
|
||||
type Settings struct {
|
||||
UserDefaults UserDefaults ` + "`json:\"userDefaults\"`" + `
|
||||
Auth Auth ` + "`json:\"auth\"`" + `
|
||||
}
|
||||
|
||||
type UserDefaults struct {
|
||||
Locale string ` + "`json:\"locale\"`" + `
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
AdminUsername string ` + "`json:\"adminUsername\"`" + `
|
||||
}
|
||||
`
|
||||
tmpFile, cleanup := createTestSourceFile(t, sourceContent)
|
||||
defer cleanup()
|
||||
sourcePath := tmpFile[:strings.LastIndex(tmpFile, "/")]
|
||||
|
||||
// Create real Settings with string values to test quoting
|
||||
settings := &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
Locale: "en-US",
|
||||
},
|
||||
Auth: Auth{
|
||||
AdminUsername: "admin",
|
||||
},
|
||||
}
|
||||
|
||||
// Test with no filtering - should quote all strings
|
||||
yamlOutput, err := GenerateConfigYamlWithSource(settings, false, true, false, sourcePath)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml failed: %v", err)
|
||||
}
|
||||
|
||||
// All string values should be quoted
|
||||
lines := strings.Split(yamlOutput, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, ":") && !strings.HasSuffix(strings.TrimSpace(line), ":") {
|
||||
// This is a key-value line
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
// Remove comments
|
||||
if commentIdx := strings.Index(value, "#"); commentIdx >= 0 {
|
||||
value = strings.TrimSpace(value[:commentIdx])
|
||||
}
|
||||
|
||||
// Skip empty values, arrays, objects, booleans
|
||||
if value == "" || value == "false" || value == "true" || strings.HasPrefix(value, "[") || strings.HasPrefix(value, "{") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it's a number (should NOT be quoted)
|
||||
if reNumber.MatchString(value) {
|
||||
if strings.HasPrefix(value, "\"") {
|
||||
t.Errorf("Numeric value should not be quoted: %s (value: '%s')", line, value)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Everything else should be a quoted string
|
||||
if !strings.HasPrefix(value, "\"") || !strings.HasSuffix(value, "\"") {
|
||||
t.Errorf("String value should be quoted: %s (value: '%s')", line, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_SecretHiding(t *testing.T) {
|
||||
// Create temporary source file with our test structs
|
||||
sourceContent := `package settings
|
||||
|
||||
type TestConfig struct {
|
||||
PublicField string ` + "`json:\"publicField\"`" + ` // A regular public field
|
||||
SecretField string ` + "`json:\"secretField\"`" + ` // secret: this should be hidden
|
||||
}
|
||||
|
||||
type TestNested struct {
|
||||
NestedPublic string ` + "`json:\"nestedPublic\"`" + ` // A nested public field
|
||||
NestedSecret string ` + "`json:\"nestedSecret\"`" + ` // secret: nested secret field
|
||||
}
|
||||
`
|
||||
|
||||
tmpFile, cleanup := createTestSourceFile(t, sourceContent)
|
||||
defer cleanup()
|
||||
|
||||
// Test secret detection
|
||||
_, secrets, _, err := CollectCommentsAndSecrets(tmpFile)
|
||||
if err != nil {
|
||||
t.Fatalf("CollectCommentsAndSecrets failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify secret fields are detected
|
||||
if !secrets["TestConfig"]["SecretField"] {
|
||||
t.Error("SecretField should be marked as secret")
|
||||
}
|
||||
if !secrets["TestNested"]["NestedSecret"] {
|
||||
t.Error("NestedSecret should be marked as secret")
|
||||
}
|
||||
if secrets["TestConfig"]["PublicField"] {
|
||||
t.Error("PublicField should not be marked as secret")
|
||||
}
|
||||
|
||||
// Test that secrets are properly collected
|
||||
if len(secrets["TestConfig"]) != 1 {
|
||||
t.Errorf("Expected 1 secret field in TestConfig, got %d", len(secrets["TestConfig"]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_DeprecatedFiltering(t *testing.T) {
|
||||
// Create temporary source file with deprecated fields
|
||||
sourceContent := `package settings
|
||||
|
||||
type TestConfig struct {
|
||||
PublicField string ` + "`json:\"publicField\"`" + ` // A regular public field
|
||||
DeprecatedField string ` + "`json:\"deprecatedField\"`" + ` // deprecated: this field is old
|
||||
}
|
||||
`
|
||||
|
||||
tmpFile, cleanup := createTestSourceFile(t, sourceContent)
|
||||
defer cleanup()
|
||||
|
||||
// Test deprecated detection
|
||||
_, _, deprecated, err := CollectCommentsAndSecrets(tmpFile)
|
||||
if err != nil {
|
||||
t.Fatalf("CollectCommentsAndSecrets failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify deprecated fields are detected
|
||||
if !deprecated["TestConfig"]["DeprecatedField"] {
|
||||
t.Error("DeprecatedField should be marked as deprecated")
|
||||
}
|
||||
if deprecated["TestConfig"]["PublicField"] {
|
||||
t.Error("PublicField should not be marked as deprecated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_FullVsFiltered(t *testing.T) {
|
||||
// Create minimal test source
|
||||
sourceContent := `package settings
|
||||
|
||||
type Settings struct {
|
||||
UserDefaults UserDefaults ` + "`json:\"userDefaults\"`" + `
|
||||
}
|
||||
|
||||
type UserDefaults struct {
|
||||
DarkMode bool ` + "`json:\"darkMode\"`" + `
|
||||
Locale string ` + "`json:\"locale\"`" + `
|
||||
}
|
||||
`
|
||||
tmpFile, cleanup := createTestSourceFile(t, sourceContent)
|
||||
defer cleanup()
|
||||
sourcePath := tmpFile[:strings.LastIndex(tmpFile, "/")]
|
||||
|
||||
// Create a config with both default and non-default values
|
||||
config := &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
DarkMode: true, // This matches default
|
||||
Locale: "es", // This differs from default ("en")
|
||||
},
|
||||
}
|
||||
|
||||
// Test full=true - should show all fields
|
||||
fullYaml, err := GenerateConfigYamlWithSource(config, false, true, false, sourcePath)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml with full=true failed: %v", err)
|
||||
}
|
||||
|
||||
// Test full=false - should only show non-default fields
|
||||
filteredYaml, err := GenerateConfigYamlWithSource(config, false, false, false, sourcePath)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml with full=false failed: %v", err)
|
||||
}
|
||||
|
||||
// Full output should be longer than filtered
|
||||
if len(fullYaml) <= len(filteredYaml) {
|
||||
t.Error("Full YAML should be longer than filtered YAML")
|
||||
}
|
||||
|
||||
// Filtered output should contain the non-default field
|
||||
if !strings.Contains(filteredYaml, "locale") {
|
||||
t.Error("Filtered YAML should contain non-default locale field")
|
||||
}
|
||||
|
||||
// Full output should contain default fields
|
||||
if !strings.Contains(fullYaml, "darkMode") {
|
||||
t.Error("Full YAML should contain darkMode field")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_CommentsOnOff(t *testing.T) {
|
||||
// Use the actual settings source directory since comments work there
|
||||
config := &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
Locale: "en",
|
||||
},
|
||||
}
|
||||
|
||||
// Test with comments=true using the real source
|
||||
withComments, err := GenerateConfigYaml(config, true, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml with comments=true failed: %v", err)
|
||||
}
|
||||
|
||||
// Test with comments=false using the real source
|
||||
withoutComments, err := GenerateConfigYaml(config, false, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml with comments=false failed: %v", err)
|
||||
}
|
||||
|
||||
// Output with comments should be longer
|
||||
if len(withComments) <= len(withoutComments) {
|
||||
t.Errorf("YAML with comments (%d chars) should be longer than without comments (%d chars)", len(withComments), len(withoutComments))
|
||||
}
|
||||
|
||||
// With comments should contain '#' characters
|
||||
if !strings.Contains(withComments, "#") {
|
||||
t.Error("YAML with comments should contain '#' characters")
|
||||
}
|
||||
|
||||
// Without comments should not contain '#' characters
|
||||
if strings.Contains(withoutComments, "#") {
|
||||
t.Error("YAML without comments should not contain '#' characters")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_IntegrationTest(t *testing.T) {
|
||||
reNumber := regexp.MustCompile(`^-?\d+(\.\d+)?$`)
|
||||
// Comprehensive test with all features
|
||||
config := &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
Locale: "es", // Non-default
|
||||
DarkMode: true, // Default
|
||||
DisableOfficePreviewExt: ".docx .xlsx", // This is deprecated
|
||||
},
|
||||
Auth: Auth{
|
||||
Key: "secret123", // This is secret
|
||||
AdminUsername: "admin", // This is secret
|
||||
AdminPassword: "password", // This is secret
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
showComments bool
|
||||
showFull bool
|
||||
filterDeprecated bool
|
||||
expectSecret bool
|
||||
expectDeprecated bool
|
||||
expectFull bool
|
||||
expectComments bool
|
||||
}{
|
||||
{
|
||||
name: "API mode: show all including deprecated",
|
||||
showComments: false,
|
||||
showFull: true,
|
||||
filterDeprecated: false,
|
||||
expectSecret: true, // secrets should be hidden
|
||||
expectDeprecated: true, // deprecated should be shown
|
||||
expectFull: true, // all fields shown
|
||||
expectComments: false, // no comments
|
||||
},
|
||||
{
|
||||
name: "Static config mode: filter deprecated",
|
||||
showComments: true,
|
||||
showFull: true,
|
||||
filterDeprecated: true,
|
||||
expectSecret: true, // secrets should be hidden
|
||||
expectDeprecated: false, // deprecated should be filtered
|
||||
expectFull: true, // all non-deprecated fields shown
|
||||
expectComments: true, // comments included
|
||||
},
|
||||
{
|
||||
name: "Filtered output: only changes",
|
||||
showComments: false,
|
||||
showFull: false,
|
||||
filterDeprecated: false,
|
||||
expectSecret: true, // secrets should be hidden
|
||||
expectDeprecated: false, // might not appear if matches default
|
||||
expectFull: false, // only non-default fields
|
||||
expectComments: false, // no comments
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
yamlOutput, err := GenerateConfigYaml(config, tt.showComments, tt.showFull, tt.filterDeprecated)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml failed: %v", err)
|
||||
}
|
||||
|
||||
// Check secret hiding
|
||||
if tt.expectSecret {
|
||||
if !strings.Contains(yamlOutput, "**hidden**") {
|
||||
t.Error("Expected secrets to be hidden with **hidden**")
|
||||
}
|
||||
if strings.Contains(yamlOutput, "secret123") {
|
||||
t.Error("Secret values should not appear in output")
|
||||
}
|
||||
}
|
||||
|
||||
// Check deprecated field filtering
|
||||
hasDeprecated := strings.Contains(yamlOutput, "disableOfficePreviewExt")
|
||||
if tt.expectDeprecated && !hasDeprecated {
|
||||
t.Error("Expected deprecated field to be present")
|
||||
}
|
||||
if !tt.expectDeprecated && hasDeprecated && tt.filterDeprecated {
|
||||
t.Error("Expected deprecated field to be filtered out")
|
||||
}
|
||||
|
||||
// Check comments
|
||||
hasComments := strings.Contains(yamlOutput, "#")
|
||||
if tt.expectComments && !hasComments {
|
||||
t.Error("Expected comments to be present")
|
||||
}
|
||||
if !tt.expectComments && hasComments {
|
||||
t.Error("Expected no comments")
|
||||
}
|
||||
|
||||
// Check string quoting - all string values should be quoted
|
||||
lines := strings.Split(yamlOutput, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, ":") && !strings.HasSuffix(strings.TrimSpace(line), ":") {
|
||||
// This is a key-value line
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
value := strings.TrimSpace(parts[1])
|
||||
// Skip comments and empty values
|
||||
if commentIdx := strings.Index(value, "#"); commentIdx >= 0 {
|
||||
// If comment starts at the beginning, there's no value, just a comment
|
||||
if commentIdx == 0 {
|
||||
continue
|
||||
}
|
||||
value = strings.TrimSpace(value[:commentIdx])
|
||||
}
|
||||
// Check if it's a number (should NOT be quoted)
|
||||
if reNumber.MatchString(value) {
|
||||
if strings.HasPrefix(value, "\"") {
|
||||
t.Errorf("Numeric value should not be quoted in line: %s (value: '%s')", line, value)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it's a boolean (should NOT be quoted)
|
||||
if value == "true" || value == "false" {
|
||||
if strings.HasPrefix(value, "\"") {
|
||||
t.Errorf("Boolean value should not be quoted in line: %s (value: '%s')", line, value)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip arrays, objects, and empty values
|
||||
if value == "" || strings.HasPrefix(value, "[") || strings.HasPrefix(value, "{") || strings.Contains(value, ":") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Everything else should be a quoted string
|
||||
if !strings.HasPrefix(value, "\"") || !strings.HasSuffix(value, "\"") {
|
||||
t.Errorf("String value should be quoted in line: %s (value: '%s')", line, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Generated YAML for %s:\n%s", tt.name, yamlOutput)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfigYaml_EdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *Settings
|
||||
desc string
|
||||
}{
|
||||
{
|
||||
name: "empty_config",
|
||||
config: &Settings{},
|
||||
desc: "Empty config should generate valid YAML",
|
||||
},
|
||||
{
|
||||
name: "only_defaults",
|
||||
config: &Settings{
|
||||
UserDefaults: UserDefaults{
|
||||
DarkMode: true, // default value
|
||||
Locale: "en", // default value
|
||||
},
|
||||
},
|
||||
desc: "Config with only default values",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Test all combinations
|
||||
for _, showComments := range []bool{false, true} {
|
||||
for _, showFull := range []bool{false, true} {
|
||||
for _, filterDeprecated := range []bool{false, true} {
|
||||
yamlOutput, err := GenerateConfigYaml(tt.config, showComments, showFull, filterDeprecated)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateConfigYaml failed for %s (comments=%v, full=%v, filterDeprecated=%v): %v",
|
||||
tt.desc, showComments, showFull, filterDeprecated, err)
|
||||
}
|
||||
|
||||
// Basic validation - should produce valid YAML structure
|
||||
if yamlOutput == "" {
|
||||
t.Errorf("Generated empty YAML for %s", tt.desc)
|
||||
}
|
||||
|
||||
// Should not contain obvious error messages (but allow "error" in log levels etc)
|
||||
if strings.Contains(yamlOutput, "Error:") || strings.Contains(yamlOutput, "ERROR:") {
|
||||
t.Errorf("YAML contains error for %s", tt.desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ type Settings struct {
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
MinSearchLength int `json:"minSearchLength"` // minimum length of search query to begin searching (default: 3)
|
||||
DisableUpdateCheck bool `json:"disableUpdateCheck"` // disables backend update check service
|
||||
NumImageProcessors int `json:"numImageProcessors"` // number of concurrent image processing jobs used to create previews, default is number of cpu cores available.
|
||||
Socket string `json:"socket"` // socket to listen on
|
||||
TLSKey string `json:"tlsKey"` // path to TLS key
|
||||
@@ -52,9 +54,9 @@ type Integrations struct {
|
||||
// onlyoffice secret is stored in the local.json file
|
||||
// docker exec <containerID> /var/www/onlyoffice/documentserver/npm/json -f /etc/onlyoffice/documentserver/local.json 'services.CoAuthoring.secret.session.string'
|
||||
type OnlyOffice struct {
|
||||
Url string `json:"url" validate:"required"` // The URL to the OnlyOffice Document Server, needs to be accessible to the user.
|
||||
InternalUrl string `json:"internalUrl"` // An optional internal address that the filebrowser server can use to communicate with the OnlyOffice Document Server, could be useful to bypass proxy.
|
||||
Secret string `json:"secret" validate:"required"`
|
||||
Url string `json:"url" validate:"required"` // The URL to the OnlyOffice Document Server, needs to be accessible to the user.
|
||||
InternalUrl string `json:"internalUrl"` // An optional internal address that the filebrowser server can use to communicate with the OnlyOffice Document Server, could be useful to bypass proxy.
|
||||
Secret string `json:"secret" validate:"required"` // secret: authentication key for OnlyOffice integration
|
||||
}
|
||||
|
||||
type Media struct {
|
||||
@@ -73,22 +75,22 @@ type LogConfig struct {
|
||||
type Source struct {
|
||||
Path string `json:"path" validate:"required"` // file system path. (Can be relative)
|
||||
Name string `json:"name"` // display name
|
||||
Config SourceConfig `json:"config"`
|
||||
Config SourceConfig `json:"config,omitempty"`
|
||||
}
|
||||
|
||||
type SourceConfig struct {
|
||||
DenyByDefault bool `json:"denyByDefault"` // deny access unless an "allow" access rule was specifically created.
|
||||
Private bool `json:"private"` // designate as source as private -- currently just means no sharing permitted.
|
||||
Disabled bool `json:"disabled"` // disable the source, this is useful so you don't need to remove it from the config file
|
||||
IndexingInterval uint32 `json:"indexingIntervalMinutes"` // optional manual overide interval in minutes to re-index the source
|
||||
DisableIndexing bool `json:"disableIndexing"` // disable the indexing of this source
|
||||
MaxWatchers int `json:"maxWatchers"` // number of concurrent watchers to use for this source, currently not supported
|
||||
NeverWatchPaths []string `json:"neverWatchPaths"` // paths that get initially once. Useful for folders that rarely change contents (without source path prefix)
|
||||
Exclude ExcludeIndexFilter `json:"exclude"` // exclude files and folders from indexing, if include is not set
|
||||
Include IncludeIndexFilter `json:"include"` // include files and folders from indexing, if exclude is not set
|
||||
DefaultUserScope string `json:"defaultUserScope"` // default "/" should match folders under path
|
||||
DefaultEnabled bool `json:"defaultEnabled"` // should be added as a default source for new users?
|
||||
CreateUserDir bool `json:"createUserDir"` // create a user directory for each user
|
||||
DenyByDefault bool `json:"denyByDefault,omitempty"` // deny access unless an "allow" access rule was specifically created.
|
||||
Private bool `json:"private"` // designate as source as private -- currently just means no sharing permitted.
|
||||
Disabled bool `json:"disabled,omitempty"` // disable the source, this is useful so you don't need to remove it from the config file
|
||||
IndexingInterval uint32 `json:"indexingIntervalMinutes,omitempty"` // optional manual overide interval in minutes to re-index the source
|
||||
DisableIndexing bool `json:"disableIndexing,omitempty"` // disable the indexing of this source
|
||||
MaxWatchers int `json:"maxWatchers"` // number of concurrent watchers to use for this source, currently not supported
|
||||
NeverWatchPaths []string `json:"neverWatchPaths"` // paths that get initially once. Useful for folders that rarely change contents (without source path prefix)
|
||||
Exclude ExcludeIndexFilter `json:"exclude"` // exclude files and folders from indexing, if include is not set
|
||||
Include IncludeIndexFilter `json:"include"` // include files and folders from indexing, if exclude is not set
|
||||
DefaultUserScope string `json:"defaultUserScope"` // default "/" should match folders under path
|
||||
DefaultEnabled bool `json:"defaultEnabled"` // should be added as a default source for new users?
|
||||
CreateUserDir bool `json:"createUserDir"` // create a user directory for each user
|
||||
}
|
||||
|
||||
type IncludeIndexFilter struct {
|
||||
@@ -114,19 +116,24 @@ type Frontend struct {
|
||||
ExternalLinks []ExternalLink `json:"externalLinks"`
|
||||
DisableNavButtons bool `json:"disableNavButtons"` // disable the nav buttons in the sidebar
|
||||
Styling StylingConfig `json:"styling"`
|
||||
Favicon string `json:"favicon"` // path to a favicon to use for the frontend
|
||||
Description string `json:"description"` // description that shows up in html head meta description
|
||||
}
|
||||
|
||||
type StylingConfig struct {
|
||||
CustomCSS string `json:"customCSS"` // if a valid path to a css file is provided, it will be applied for all users. (eg. "reduce-rounded-corners.css")
|
||||
LightBackground string `json:"lightBackground"` // specify a valid CSS color property value to use as the background color in light mode
|
||||
DarkBackground string `json:"darkBackground"` // Specify a valid CSS color property value to use as the background color in dark mode
|
||||
CustomThemes map[string]CustomTheme `json:"customThemes"` // A list of custom css files that each user can select to override the default styling. if "default" is key name then it will be the default option.
|
||||
CustomThemeOptions map[string]CustomTheme `json:"-"` // not exposed
|
||||
CustomCSS string `json:"customCSS"` // if a valid path to a css file is provided, it will be applied for all users. (eg. "reduce-rounded-corners.css")
|
||||
CustomCSSRaw string `json:"-"` // The css raw content to use for the custom css.
|
||||
LightBackground string `json:"lightBackground"` // specify a valid CSS color property value to use as the background color in light mode
|
||||
DarkBackground string `json:"darkBackground"` // Specify a valid CSS color property value to use as the background color in dark mode
|
||||
CustomThemes map[string]CustomTheme `json:"customThemes"` // A list of custom css files that each user can select to override the default styling. if "default" is key name then it will be the default option.
|
||||
// In-memory (not exposed to config)
|
||||
CustomThemeOptions map[string]CustomTheme `json:"-"` // not exposed
|
||||
}
|
||||
|
||||
type CustomTheme struct {
|
||||
Description string `json:"description"` // The description of the theme to display in the UI.
|
||||
CSS string `json:"css,omitempty"` // The css file path and filename to use for the theme.
|
||||
CssRaw string `json:"-"` // The css raw content to use for the theme.
|
||||
}
|
||||
|
||||
type ExternalLink struct {
|
||||
@@ -152,8 +159,8 @@ type UserDefaults struct {
|
||||
GallerySize int `json:"gallerySize"` // 0-9 - the size of the gallery thumbnails
|
||||
ThemeColor string `json:"themeColor"` // theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
|
||||
QuickDownload bool `json:"quickDownload"` // show icon to download in one click
|
||||
DisablePreviewExt string `json:"disablePreviewExt"` // comma separated list of file extensions to disable preview for
|
||||
DisableViewingExt string `json:"disableViewingExt"` // comma separated list of file extensions to disable viewing for
|
||||
DisablePreviewExt string `json:"disablePreviewExt"` // space separated list of file extensions to disable preview for
|
||||
DisableViewingExt string `json:"disableViewingExt"` // space separated list of file extensions to disable viewing for
|
||||
LockPassword bool `json:"lockPassword"` // disable the user from changing their password
|
||||
DisableSettings bool `json:"disableSettings,omitempty"` // disable the user from viewing the settings page
|
||||
Preview users.Preview `json:"preview"`
|
||||
@@ -167,4 +174,5 @@ type UserDefaults struct {
|
||||
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // list of file extensions to disable onlyoffice editor for
|
||||
CustomTheme string `json:"customTheme"` // Name of theme to use chosen from custom themes config.
|
||||
ShowSelectMultiple bool `json:"showSelectMultiple"` // show select multiple files on desktop
|
||||
DebugOffice bool `json:"debugOffice"` // debug onlyoffice editor
|
||||
}
|
||||
@@ -74,17 +74,21 @@ func FallbackColor(val, defaultColor string) string {
|
||||
}
|
||||
|
||||
func addCustomTheme(name, description, cssFilePath string) {
|
||||
// Store only file path in config (for YAML export)
|
||||
Config.Frontend.Styling.CustomThemes[name] = CustomTheme{
|
||||
Description: description,
|
||||
CSS: cssFilePath, // Store file path, not content
|
||||
}
|
||||
|
||||
// Load CSS content for runtime use (frontend consumption)
|
||||
cssContent := ""
|
||||
if cssFilePath != "" {
|
||||
cssContent = readCustomCSS(cssFilePath)
|
||||
}
|
||||
Config.Frontend.Styling.CustomThemes[name] = CustomTheme{
|
||||
Description: description,
|
||||
CSS: cssContent,
|
||||
}
|
||||
Config.Frontend.Styling.CustomThemeOptions[name] = CustomTheme{
|
||||
Description: description,
|
||||
CSS: cssFilePath,
|
||||
CssRaw: cssContent, // Store loaded content
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,4 +12,5 @@ var (
|
||||
SearchResultsCache = cache.NewCache(15*time.Second, 1*time.Hour)
|
||||
OnlyOfficeCache = cache.NewCache(48*time.Hour, 1*time.Hour)
|
||||
JwtCache = cache.NewCache(1*time.Hour, 72*time.Hour)
|
||||
MediaCache = cache.NewCache(24 * time.Hour) // subtitles get cached for 24 hours
|
||||
)
|
||||
@@ -121,3 +121,10 @@ func NonNilSlice[T any](in []T) []T {
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func Ternary[T any](cond bool, vtrue, vfalse T) T {
|
||||
if cond {
|
||||
return vtrue
|
||||
}
|
||||
return vfalse
|
||||
}
|
||||
@@ -176,6 +176,7 @@ func (s *Storage) DenyUser(sourcePath, indexPath, username string) error {
|
||||
}
|
||||
rule.Deny.Users[username] = struct{}{}
|
||||
s.incrementSourceVersion(sourcePath)
|
||||
accessCache.Set(accessChangedKey+sourcePath, false)
|
||||
return s.SaveToDB()
|
||||
}
|
||||
|
||||
@@ -195,6 +196,7 @@ func (s *Storage) AllowUser(sourcePath, indexPath, username string) error {
|
||||
}
|
||||
rule.Allow.Users[username] = struct{}{}
|
||||
s.incrementSourceVersion(sourcePath)
|
||||
accessCache.Set(accessChangedKey+sourcePath, false)
|
||||
return s.SaveToDB()
|
||||
}
|
||||
|
||||
@@ -212,6 +214,7 @@ func (s *Storage) DenyGroup(sourcePath, indexPath, groupname string) error {
|
||||
}
|
||||
rule.Deny.Groups[groupname] = struct{}{}
|
||||
s.incrementSourceVersion(sourcePath)
|
||||
accessCache.Set(accessChangedKey+sourcePath, false)
|
||||
return s.SaveToDB()
|
||||
}
|
||||
|
||||
@@ -229,6 +232,7 @@ func (s *Storage) AllowGroup(sourcePath, indexPath, groupname string) error {
|
||||
}
|
||||
rule.Allow.Groups[groupname] = struct{}{}
|
||||
s.incrementSourceVersion(sourcePath)
|
||||
accessCache.Set(accessChangedKey+sourcePath, false)
|
||||
return s.SaveToDB()
|
||||
}
|
||||
|
||||
@@ -242,6 +246,7 @@ func (s *Storage) DenyAll(sourcePath, indexPath string) error {
|
||||
}
|
||||
rule.DenyAll = true
|
||||
s.incrementSourceVersion(sourcePath)
|
||||
accessCache.Set(accessChangedKey+sourcePath, false)
|
||||
return s.SaveToDB()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,28 +4,31 @@ import "sync"
|
||||
|
||||
type CommonShare struct {
|
||||
//AllowEdit bool `json:"allowEdit,omitempty"`
|
||||
AllowUpload bool `json:"allowUpload,omitempty"`
|
||||
DisableFileViewer bool `json:"disableFileViewer,omitempty"`
|
||||
DownloadsLimit int `json:"downloadsLimit,omitempty"`
|
||||
ShareTheme string `json:"shareTheme,omitempty"`
|
||||
DisableAnonymous bool `json:"disableAnonymous,omitempty"`
|
||||
MaxBandwidth int `json:"maxBandwidth,omitempty"`
|
||||
DisableThumbnails bool `json:"disableThumbnails,omitempty"`
|
||||
KeepAfterExpiration bool `json:"keepAfterExpiration,omitempty"`
|
||||
AllowedUsernames []string `json:"allowedUsernames,omitempty"`
|
||||
ThemeColor string `json:"themeColor,omitempty"`
|
||||
Banner string `json:"banner,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Favicon string `json:"favicon,omitempty"`
|
||||
QuickDownload bool `json:"quickDownload,omitempty"`
|
||||
HideNavButtons bool `json:"hideNavButtons,omitempty"`
|
||||
DisableSidebar bool `json:"disableSidebar"`
|
||||
ViewMode string `json:"viewMode,omitempty"`
|
||||
Source string `json:"source,omitempty"` // backend source is path to maintain between name changes
|
||||
Path string `json:"path,omitempty"`
|
||||
DownloadURL string `json:"downloadURL,omitempty"`
|
||||
DisableShareCard bool `json:"disableShareCard,omitempty"`
|
||||
AllowUpload bool `json:"allowUpload,omitempty"`
|
||||
DisableFileViewer bool `json:"disableFileViewer,omitempty"`
|
||||
DownloadsLimit int `json:"downloadsLimit,omitempty"`
|
||||
ShareTheme string `json:"shareTheme,omitempty"`
|
||||
DisableAnonymous bool `json:"disableAnonymous,omitempty"`
|
||||
MaxBandwidth int `json:"maxBandwidth,omitempty"`
|
||||
DisableThumbnails bool `json:"disableThumbnails,omitempty"`
|
||||
KeepAfterExpiration bool `json:"keepAfterExpiration,omitempty"`
|
||||
AllowedUsernames []string `json:"allowedUsernames,omitempty"`
|
||||
ThemeColor string `json:"themeColor,omitempty"`
|
||||
Banner string `json:"banner,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Favicon string `json:"favicon,omitempty"`
|
||||
QuickDownload bool `json:"quickDownload,omitempty"`
|
||||
HideNavButtons bool `json:"hideNavButtons,omitempty"`
|
||||
DisableSidebar bool `json:"disableSidebar"`
|
||||
ViewMode string `json:"viewMode,omitempty"`
|
||||
Source string `json:"source,omitempty"` // backend source is path to maintain between name changes
|
||||
Path string `json:"path,omitempty"`
|
||||
DownloadURL string `json:"downloadURL,omitempty"`
|
||||
DisableShareCard bool `json:"disableShareCard,omitempty"`
|
||||
EnforceDarkLightMode string `json:"enforceDarkLightMode,omitempty"` // "dark" or "light"
|
||||
EnableOnlyOffice bool `json:"enableOnlyOffice,omitempty"`
|
||||
EnableOnlyOfficeEditing bool `json:"enableOnlyOfficeEditing,omitempty"`
|
||||
}
|
||||
type CreateBody struct {
|
||||
CommonShare
|
||||
|
||||
@@ -49,6 +49,7 @@ type Preview struct {
|
||||
Office bool `json:"office"` // show preview image for office files
|
||||
PopUp bool `json:"popup"` // show larger popup preview when hovering
|
||||
AutoplayMedia bool `json:"autoplayMedia"` // autoplay media files in preview
|
||||
DefaultMediaPlayer bool `json:"defaultMediaPlayer"` // disable html5 media player and use the default media player
|
||||
}
|
||||
|
||||
// User describes a user.
|
||||
@@ -99,10 +100,11 @@ type NonAdminEditable struct {
|
||||
FileLoading FileLoading `json:"fileLoading"` // upload and download settings
|
||||
DisableOfficePreviewExt string `json:"disableOfficePreviewExt"` // deprecated
|
||||
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // deprecated
|
||||
DisablePreviewExt string `json:"disablePreviewExt"` // comma separated list of file extensions to disable preview for
|
||||
DisableViewingExt string `json:"disableViewingExt"` // comma separated list of file extensions to disable viewing for
|
||||
DisablePreviewExt string `json:"disablePreviewExt"` // space separated list of file extensions to disable preview for
|
||||
DisableViewingExt string `json:"disableViewingExt"` // space separated list of file extensions to disable viewing for
|
||||
CustomTheme string `json:"customTheme"` // Name of theme to use chosen from custom themes config.
|
||||
ShowSelectMultiple bool `json:"showSelectMultiple"` // show select multiple files on desktop
|
||||
DebugOffice bool `json:"debugOffice"` // debug onlyoffice editor
|
||||
}
|
||||
|
||||
type FileLoading struct {
|
||||
@@ -110,10 +112,6 @@ type FileLoading struct {
|
||||
ChunkSize int `json:"uploadChunkSizeMb"`
|
||||
}
|
||||
|
||||
var AnonymousUser = User{
|
||||
Username: "anonymous", // temp user not registered
|
||||
}
|
||||
|
||||
func CleanUsername(s string) string {
|
||||
// Remove any trailing space to avoid ending on -
|
||||
s = strings.Trim(s, " ")
|
||||
|
||||
@@ -138,3 +138,14 @@ func handleSourceUpdates() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Shutdown() {
|
||||
userClientsMu.Lock()
|
||||
defer userClientsMu.Unlock()
|
||||
|
||||
for _, clientChannels := range userClients {
|
||||
for _, ch := range clientChannels {
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package ffmpeg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
)
|
||||
|
||||
// SubtitleTrack represents a subtitle track (embedded or external file)
|
||||
type SubtitleTrack struct {
|
||||
Name string `json:"name"` // filename for external, or descriptive name for embedded
|
||||
Language string `json:"language,omitempty"` // language code
|
||||
Title string `json:"title,omitempty"` // title/description
|
||||
Index *int `json:"index,omitempty"` // stream index for embedded subtitles (nil for external)
|
||||
Codec string `json:"codec,omitempty"` // codec name for embedded subtitles
|
||||
Content string `json:"content,omitempty"` // subtitle content
|
||||
IsFile bool `json:"isFile"` // true for external files, false for embedded streams
|
||||
}
|
||||
|
||||
// FFProbeOutput represents the JSON output from ffprobe
|
||||
type FFProbeOutput struct {
|
||||
Streams []struct {
|
||||
Index int `json:"index"`
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
Disposition map[string]int `json:"disposition,omitempty"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
|
||||
// DetectAllSubtitles finds both embedded and external subtitle tracks
|
||||
func DetectAllSubtitles(videoPath string, parentDir string, modtime time.Time) []SubtitleTrack {
|
||||
key := "all_subtitles:" + videoPath + ":" + modtime.Format(time.RFC3339)
|
||||
|
||||
// Check cache first
|
||||
if cached, ok := utils.MediaCache.Get(key).([]SubtitleTrack); ok {
|
||||
return cached
|
||||
}
|
||||
|
||||
var allSubtitles []SubtitleTrack
|
||||
|
||||
// First, get embedded subtitles
|
||||
embeddedSubs := detectEmbeddedSubtitles(videoPath)
|
||||
allSubtitles = append(allSubtitles, embeddedSubs...)
|
||||
|
||||
// Then, get external subtitle files
|
||||
externalSubs := detectExternalSubtitles(videoPath, parentDir)
|
||||
allSubtitles = append(allSubtitles, externalSubs...)
|
||||
|
||||
// Cache the complete list
|
||||
utils.MediaCache.Set(key, allSubtitles)
|
||||
|
||||
return allSubtitles
|
||||
}
|
||||
|
||||
// detectEmbeddedSubtitles uses ffprobe to find embedded subtitle tracks
|
||||
func detectEmbeddedSubtitles(realPath string) []SubtitleTrack {
|
||||
cmd := exec.Command("ffprobe",
|
||||
"-v", "quiet",
|
||||
"-print_format", "json",
|
||||
"-show_streams",
|
||||
"-select_streams", "s",
|
||||
realPath)
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
logger.Debug("ffprobe failed for file: " + realPath + ", error: " + err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
var probeOutput FFProbeOutput
|
||||
if err := json.Unmarshal(output, &probeOutput); err != nil {
|
||||
logger.Debug("failed to parse ffprobe output for file: " + realPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
var subtitles []SubtitleTrack
|
||||
for _, stream := range probeOutput.Streams {
|
||||
if stream.CodecType == "subtitle" {
|
||||
track := SubtitleTrack{
|
||||
Index: &stream.Index,
|
||||
Codec: stream.CodecName,
|
||||
IsFile: false, // This is an embedded subtitle
|
||||
}
|
||||
|
||||
// Extract language and title from tags
|
||||
if stream.Tags != nil {
|
||||
if lang, ok := stream.Tags["language"]; ok {
|
||||
track.Language = lang
|
||||
}
|
||||
if title, ok := stream.Tags["title"]; ok {
|
||||
track.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a descriptive name
|
||||
if track.Title != "" {
|
||||
track.Name = track.Title
|
||||
} else if track.Language != "" {
|
||||
track.Name = "Embedded (" + track.Language + ")"
|
||||
} else {
|
||||
track.Name = "Embedded Subtitle " + strconv.Itoa(stream.Index)
|
||||
}
|
||||
|
||||
subtitles = append(subtitles, track)
|
||||
}
|
||||
}
|
||||
return subtitles
|
||||
}
|
||||
|
||||
// detectExternalSubtitles finds external subtitle files in the same directory
|
||||
func detectExternalSubtitles(videoPath string, parentDir string) []SubtitleTrack {
|
||||
var subtitles []SubtitleTrack
|
||||
|
||||
// Get the base name of the video (without extension)
|
||||
videoBaseName := strings.TrimSuffix(filepath.Base(videoPath), filepath.Ext(videoPath))
|
||||
|
||||
// Common subtitle extensions
|
||||
subtitleExts := []string{".srt", ".vtt", ".lrc", ".sbv", ".ass", ".ssa", ".sub", ".smi"}
|
||||
|
||||
// Look for subtitle files with matching base name
|
||||
for _, ext := range subtitleExts {
|
||||
subtitlePath := filepath.Join(parentDir, videoBaseName+ext)
|
||||
if _, err := os.Stat(subtitlePath); err == nil {
|
||||
// File exists
|
||||
track := SubtitleTrack{
|
||||
Name: filepath.Base(subtitlePath),
|
||||
IsFile: true, // This is an external file
|
||||
}
|
||||
|
||||
// Try to infer language from filename patterns like "video.en.srt"
|
||||
parts := strings.Split(videoBaseName, ".")
|
||||
if len(parts) > 1 {
|
||||
// Check if the last part before extension looks like a language code
|
||||
lastPart := parts[len(parts)-1]
|
||||
if len(lastPart) == 2 || len(lastPart) == 3 {
|
||||
track.Language = lastPart
|
||||
}
|
||||
}
|
||||
|
||||
subtitles = append(subtitles, track)
|
||||
}
|
||||
}
|
||||
|
||||
return subtitles
|
||||
}
|
||||
|
||||
// ExtractSubtitleContent extracts subtitle content without service management
|
||||
func ExtractSubtitleContent(videoPath string, streamIndex int) (string, error) {
|
||||
cmd := exec.Command("ffmpeg",
|
||||
"-i", videoPath,
|
||||
"-map", fmt.Sprintf("0:%d", streamIndex),
|
||||
"-c:s", "webvtt",
|
||||
"-f", "webvtt",
|
||||
"-") // output to stdout
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("subtitle extraction failed: %v", err)
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// LoadAndConvertSubtitleFile loads a subtitle file and converts it to WebVTT format
|
||||
func LoadAndConvertSubtitleFile(subtitlePath string) (string, error) {
|
||||
ext := strings.ToLower(filepath.Ext(subtitlePath))
|
||||
|
||||
if ext == ".vtt" {
|
||||
// Already WebVTT, just read it
|
||||
content, err := os.ReadFile(subtitlePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
if ext == ".srt" {
|
||||
// Convert SRT to WebVTT using ffmpeg
|
||||
cmd := exec.Command("ffmpeg",
|
||||
"-i", subtitlePath,
|
||||
"-c:s", "webvtt",
|
||||
"-f", "webvtt",
|
||||
"-") // output to stdout
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert SRT to WebVTT: %v", err)
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// For other formats, try to read as plain text and hope for the best
|
||||
// This is a fallback - ideally you'd want format-specific converters
|
||||
content, err := os.ReadFile(subtitlePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Basic conversion wrapper for non-VTT formats
|
||||
vttHeader := "WEBVTT\n\n"
|
||||
return vttHeader + string(content), nil
|
||||
}
|
||||
|
||||
// ExtractSingleSubtitle extracts content for a specific subtitle track by array index
|
||||
func ExtractSingleSubtitle(videoPath string, parentDir string, trackIndex int, modtime time.Time) (SubtitleTrack, error) {
|
||||
// Get all subtitle tracks
|
||||
allTracks := DetectAllSubtitles(videoPath, parentDir, modtime)
|
||||
|
||||
if trackIndex >= len(allTracks) {
|
||||
return SubtitleTrack{}, fmt.Errorf("subtitle track %d not found (only %d tracks available)", trackIndex, len(allTracks))
|
||||
}
|
||||
|
||||
track := allTracks[trackIndex]
|
||||
|
||||
// Load content based on type
|
||||
if track.IsFile {
|
||||
// Load external subtitle file
|
||||
subtitlePath := filepath.Join(parentDir, track.Name)
|
||||
content, err := LoadAndConvertSubtitleFile(subtitlePath)
|
||||
if err != nil {
|
||||
return SubtitleTrack{}, fmt.Errorf("failed to load external subtitle: %v", err)
|
||||
}
|
||||
track.Content = content
|
||||
} else {
|
||||
// Extract embedded subtitle content
|
||||
if track.Index == nil {
|
||||
return SubtitleTrack{}, fmt.Errorf("embedded subtitle track has no stream index")
|
||||
}
|
||||
content, err := ExtractSubtitleContent(videoPath, *track.Index)
|
||||
if err != nil {
|
||||
return SubtitleTrack{}, fmt.Errorf("failed to extract embedded subtitle: %v", err)
|
||||
}
|
||||
track.Content = content
|
||||
}
|
||||
|
||||
return track, nil
|
||||
}
|
||||
|
||||
// LoadAllSubtitleContent loads the actual content for all detected subtitle tracks
|
||||
func LoadAllSubtitleContent(videoPath string, subtitles []SubtitleTrack, modtime time.Time) error {
|
||||
for idx := range subtitles {
|
||||
subtitle := &subtitles[idx]
|
||||
|
||||
// Check if content is already cached
|
||||
contentKey := fmt.Sprintf("subtitle_content:%s:%d:%s", videoPath, idx, modtime.Format(time.RFC3339))
|
||||
if cached, ok := utils.MediaCache.Get(contentKey).(string); ok {
|
||||
subtitle.Content = cached
|
||||
continue
|
||||
}
|
||||
|
||||
var content string
|
||||
var err error
|
||||
|
||||
if subtitle.IsFile {
|
||||
// Load external subtitle file content and convert to WebVTT
|
||||
subtitlePath := filepath.Join(filepath.Dir(videoPath), subtitle.Name)
|
||||
content, err = LoadAndConvertSubtitleFile(subtitlePath)
|
||||
if err != nil {
|
||||
logger.Debug("failed to read/convert subtitle file " + subtitlePath + ": " + err.Error())
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Load embedded subtitle content
|
||||
if subtitle.Index == nil {
|
||||
logger.Debug("embedded subtitle has no stream index")
|
||||
continue
|
||||
}
|
||||
content, err = ExtractSubtitleContent(videoPath, *subtitle.Index)
|
||||
if err != nil {
|
||||
logger.Debug("failed to extract embedded subtitle: " + err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
subtitle.Content = content
|
||||
// Cache the content for future requests
|
||||
utils.MediaCache.Set(contentKey, content)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package ffmpeg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
)
|
||||
|
||||
// VideoService handles video preview operations with ffmpeg
|
||||
type VideoService struct {
|
||||
ffmpegPath string
|
||||
ffprobePath string
|
||||
debug bool
|
||||
semaphore chan struct{}
|
||||
}
|
||||
|
||||
// NewVideoService creates a new video service instance
|
||||
func NewVideoService(ffmpegPath, ffprobePath string, maxConcurrent int, debug bool) *VideoService {
|
||||
return &VideoService{
|
||||
ffmpegPath: ffmpegPath,
|
||||
ffprobePath: ffprobePath,
|
||||
debug: debug,
|
||||
semaphore: make(chan struct{}, maxConcurrent),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *VideoService) acquire(ctx context.Context) error {
|
||||
select {
|
||||
case s.semaphore <- struct{}{}:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *VideoService) release() {
|
||||
<-s.semaphore
|
||||
}
|
||||
|
||||
// GenerateVideoPreview generates a single preview image from a video using ffmpeg.
|
||||
// videoPath: path to the input video file.
|
||||
// outputPath: path where the generated preview image will be saved (e.g., "/tmp/preview.jpg").
|
||||
// seekTime: how many seconds into the video to seek before capturing the frame.
|
||||
func (s *VideoService) GenerateVideoPreview(videoPath, outputPath string, percentageSeek int) ([]byte, error) {
|
||||
if err := s.acquire(context.Background()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.release()
|
||||
|
||||
// Step 1: Get video duration from the container format
|
||||
probeCmd := exec.Command(
|
||||
s.ffprobePath,
|
||||
"-v", "error",
|
||||
// Use format=duration for better compatibility
|
||||
"-show_entries", "format=duration",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1",
|
||||
videoPath,
|
||||
)
|
||||
|
||||
var probeOut bytes.Buffer
|
||||
probeCmd.Stdout = &probeOut
|
||||
if s.debug {
|
||||
probeCmd.Stderr = os.Stderr
|
||||
}
|
||||
if err := probeCmd.Run(); err != nil {
|
||||
logger.Errorf("ffprobe command failed on file '%v' : %v", videoPath, err)
|
||||
return nil, fmt.Errorf("ffprobe failed: %w", err)
|
||||
}
|
||||
|
||||
durationStr := strings.TrimSpace(probeOut.String())
|
||||
if durationStr == "" || durationStr == "N/A" {
|
||||
logger.Errorf("could not determine video duration for file '%v' using duration info '%v'", videoPath, durationStr)
|
||||
return nil, fmt.Errorf("could not determine video duration")
|
||||
}
|
||||
|
||||
durationFloat, err := strconv.ParseFloat(durationStr, 64)
|
||||
if err != nil {
|
||||
// The original error you saw would be caught here if "N/A" was still the output
|
||||
return nil, fmt.Errorf("invalid duration: %v", err)
|
||||
}
|
||||
|
||||
if durationFloat <= 0 {
|
||||
return nil, fmt.Errorf("video duration must be positive")
|
||||
}
|
||||
|
||||
// Step 2: Get the duration of the video in whole seconds
|
||||
duration := int(durationFloat)
|
||||
|
||||
// Step 3: Calculate seek time based on percentageSeek (percentage value)
|
||||
seekSeconds := duration * percentageSeek / 100
|
||||
|
||||
// Step 4: Convert seekSeconds to string for ffmpeg command
|
||||
seekTime := strconv.Itoa(seekSeconds)
|
||||
|
||||
// Step 5: Extract frame at seek time
|
||||
cmd := exec.Command(
|
||||
s.ffmpegPath,
|
||||
"-ss", seekTime,
|
||||
"-i", videoPath,
|
||||
"-frames:v", "1",
|
||||
"-q:v", "10",
|
||||
"-y", // overwrite output
|
||||
outputPath,
|
||||
)
|
||||
|
||||
if s.debug {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ffmpeg command failed on file '%v' : %w", videoPath, err)
|
||||
}
|
||||
return os.ReadFile(outputPath)
|
||||
}
|
||||
|
||||
// GetVideoDuration extracts the duration of a video file using ffprobe
|
||||
func GetVideoDuration(ffprobePath string, videoPath string) (float64, error) {
|
||||
cmd := exec.Command(ffprobePath,
|
||||
"-v", "error",
|
||||
"-show_entries", "format=duration",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1",
|
||||
videoPath)
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("ffprobe failed: %w", err)
|
||||
}
|
||||
|
||||
durationStr := strings.TrimSpace(string(output))
|
||||
if durationStr == "" || durationStr == "N/A" {
|
||||
return 0, fmt.Errorf("could not determine video duration")
|
||||
}
|
||||
|
||||
duration, err := strconv.ParseFloat(durationStr, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid duration: %v", err)
|
||||
}
|
||||
|
||||
return duration, nil
|
||||
}
|
||||
+52
-41
@@ -31,37 +31,46 @@ require (
|
||||
require (
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.2 // indirect
|
||||
github.com/4meepo/tagalign v1.4.2 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.3 // indirect
|
||||
codeberg.org/chavacava/garif v0.2.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
|
||||
dev.gaijin.team/go/golib v0.6.0 // indirect
|
||||
github.com/4meepo/tagalign v1.4.3 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.6 // indirect
|
||||
github.com/AlwxSin/noinlineerr v1.0.5 // indirect
|
||||
github.com/Antonboom/errname v1.1.0 // indirect
|
||||
github.com/Antonboom/nilnil v1.1.0 // indirect
|
||||
github.com/Antonboom/testifylint v1.6.1 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.17.2 // indirect
|
||||
github.com/air-verse/air v1.62.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 // indirect
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
|
||||
github.com/alexkohler/prealloc v1.0.0 // indirect
|
||||
github.com/alfatraining/structtag v1.0.0 // indirect
|
||||
github.com/alingse/asasalint v0.0.11 // indirect
|
||||
github.com/alingse/nilnesserr v0.2.0 // indirect
|
||||
github.com/ashanbrown/forbidigo v1.6.0 // indirect
|
||||
github.com/ashanbrown/makezero v1.2.0 // indirect
|
||||
github.com/ashanbrown/forbidigo/v2 v2.1.0 // indirect
|
||||
github.com/ashanbrown/makezero/v2 v2.0.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bep/godartsass/v2 v2.5.0 // indirect
|
||||
github.com/bep/golibsass v1.2.0 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.3 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
|
||||
github.com/bombsimon/wsl/v5 v5.1.1 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/breml/bidichk v0.3.3 // indirect
|
||||
github.com/breml/errchkjson v0.4.1 // indirect
|
||||
github.com/butuzov/ireturn v0.4.0 // indirect
|
||||
github.com/butuzov/mirror v1.3.0 // indirect
|
||||
github.com/catenacyber/perfsprint v0.9.1 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charithe/durationcheck v0.0.10 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
@@ -69,11 +78,11 @@ require (
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/ckaznocha/intrange v0.3.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/creack/pty v1.1.24 // indirect
|
||||
github.com/curioswitch/go-reassign v0.3.0 // indirect
|
||||
github.com/daixiang0/gci v0.13.6 // indirect
|
||||
github.com/daixiang0/gci v0.13.7 // indirect
|
||||
github.com/dave/dst v0.27.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
||||
@@ -85,9 +94,9 @@ require (
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.6 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.15 // indirect
|
||||
github.com/go-critic/go-critic v0.13.0 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
@@ -105,20 +114,22 @@ require (
|
||||
github.com/go-toolsmith/astp v1.1.0 // indirect
|
||||
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
||||
github.com/go-toolsmith/typep v1.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20250821133510-ecfc33a939ac // indirect
|
||||
github.com/gohugoio/hugo v0.147.6 // indirect
|
||||
github.com/golang/geo v0.0.0-20250825151631-54d70cc7cb31 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
|
||||
github.com/golangci/go-printf-func-name v0.1.0 // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
|
||||
github.com/golangci/golangci-lint/v2 v2.1.6 // indirect
|
||||
github.com/golangci/golangci-lint/v2 v2.4.0 // indirect
|
||||
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect
|
||||
github.com/golangci/misspell v0.6.0 // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.1 // indirect
|
||||
github.com/golangci/misspell v0.7.0 // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.2 // indirect
|
||||
github.com/golangci/revgrep v0.8.0 // indirect
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
|
||||
github.com/gordonklaus/ineffassign v0.1.0 // indirect
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
||||
@@ -131,9 +142,9 @@ require (
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jgautheron/goconst v1.8.1 // indirect
|
||||
github.com/jgautheron/goconst v1.8.2 // indirect
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.4 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/julz/importas v0.2.0 // indirect
|
||||
github.com/jupiterrider/ffi v0.5.1 // indirect
|
||||
@@ -143,18 +154,19 @@ require (
|
||||
github.com/kulti/thelper v0.6.3 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.14 // indirect
|
||||
github.com/lasiar/canonicalheader v1.1.2 // indirect
|
||||
github.com/ldez/exptostd v0.4.3 // indirect
|
||||
github.com/ldez/gomoddirectives v0.6.1 // indirect
|
||||
github.com/ldez/grignotin v0.9.0 // indirect
|
||||
github.com/ldez/exptostd v0.4.4 // indirect
|
||||
github.com/ldez/gomoddirectives v0.7.0 // indirect
|
||||
github.com/ldez/grignotin v0.10.0 // indirect
|
||||
github.com/ldez/tagliatelle v0.7.1 // indirect
|
||||
github.com/ldez/usetesting v0.4.3 // indirect
|
||||
github.com/ldez/usetesting v0.5.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/leonklingele/grouper v1.1.2 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/macabu/inamedparam v0.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/manuelarte/funcorder v0.2.1 // indirect
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.3.0 // indirect
|
||||
github.com/manuelarte/funcorder v0.5.0 // indirect
|
||||
github.com/maratori/testableexamples v1.0.0 // indirect
|
||||
github.com/maratori/testpackage v1.1.1 // indirect
|
||||
github.com/matoous/godox v1.1.0 // indirect
|
||||
@@ -162,16 +174,15 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgechev/revive v1.9.0 // indirect
|
||||
github.com/mgechev/revive v1.11.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
|
||||
github.com/moricho/tparallel v0.3.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.20.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@@ -192,18 +203,18 @@ require (
|
||||
github.com/ryancurrah/gomodguard v1.4.1 // indirect
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.3 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.22.7 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
github.com/sonatard/noctx v0.1.0 // indirect
|
||||
github.com/sonatard/noctx v0.4.0 // indirect
|
||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cast v1.8.0 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
|
||||
@@ -211,6 +222,7 @@ require (
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/swaggo/files v1.0.1 // indirect
|
||||
github.com/tdakkota/asciicheck v0.4.1 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.8.1 // indirect
|
||||
github.com/tetafro/godot v1.5.1 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
|
||||
github.com/timonwong/loggercheck v0.11.0 // indirect
|
||||
@@ -220,7 +232,7 @@ require (
|
||||
github.com/ultraware/whitespace v0.2.0 // indirect
|
||||
github.com/urfave/cli/v2 v2.3.0 // indirect
|
||||
github.com/uudashr/gocognit v1.2.0 // indirect
|
||||
github.com/uudashr/iface v1.3.1 // indirect
|
||||
github.com/uudashr/iface v1.4.1 // indirect
|
||||
github.com/xen0n/gosmopolitan v1.3.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
@@ -228,20 +240,18 @@ require (
|
||||
github.com/ykadowak/zerologlint v0.1.5 // indirect
|
||||
gitlab.com/bosi/decorder v0.4.2 // indirect
|
||||
go-simpler.org/musttag v0.13.1 // indirect
|
||||
go-simpler.org/sloglint v0.11.0 // indirect
|
||||
go-simpler.org/sloglint v0.11.1 // indirect
|
||||
go.augendre.info/arangolint v0.2.0 // indirect
|
||||
go.augendre.info/fatcontext v0.8.0 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated // indirect
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
@@ -252,6 +262,7 @@ require (
|
||||
)
|
||||
|
||||
tool (
|
||||
github.com/air-verse/air
|
||||
github.com/golangci/golangci-lint/v2/cmd/golangci-lint
|
||||
github.com/swaggo/swag/cmd/swag
|
||||
)
|
||||
+208
-113
@@ -34,17 +34,29 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY=
|
||||
codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y=
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI=
|
||||
dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo=
|
||||
dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E=
|
||||
github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
|
||||
github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
|
||||
github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
|
||||
github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8=
|
||||
github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c=
|
||||
github.com/Abirdcfly/dupword v0.1.6 h1:qeL6u0442RPRe3mcaLcbaCi2/Y/hOcdtw6DE9odjz9c=
|
||||
github.com/Abirdcfly/dupword v0.1.6/go.mod h1:s+BFMuL/I4YSiFv29snqyjwzDp4b65W2Kvy+PKzZ6cw=
|
||||
github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY=
|
||||
github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc=
|
||||
github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE=
|
||||
github.com/Antonboom/errname v1.1.0/go.mod h1:O1NMrzgUcVBGIfi3xlVuvX8Q/VP/73sseCaAppfjqZw=
|
||||
github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+ng=
|
||||
github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE=
|
||||
github.com/Antonboom/testifylint v1.6.1 h1:6ZSytkFWatT8mwZlmRCHkWz1gPi+q6UBSbieji2Gj/o=
|
||||
github.com/Antonboom/testifylint v1.6.1/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
@@ -53,8 +65,6 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
@@ -63,14 +73,16 @@ github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsu
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo=
|
||||
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM=
|
||||
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
|
||||
github.com/air-verse/air v1.62.0 h1:6CoXL4MAX9dc4xAzLfjMcDfbBoGmW5VjuuTV/1+bI+M=
|
||||
github.com/air-verse/air v1.62.0/go.mod h1:EO+jWuetL10tS9raffwg8WEV0t0KUeucRRaf9ii86dA=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI=
|
||||
github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
@@ -80,30 +92,60 @@ github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQ
|
||||
github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q=
|
||||
github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
|
||||
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
|
||||
github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc=
|
||||
github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus=
|
||||
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
|
||||
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
|
||||
github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w=
|
||||
github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac=
|
||||
github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0=
|
||||
github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY=
|
||||
github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
|
||||
github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU=
|
||||
github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4=
|
||||
github.com/ashanbrown/forbidigo/v2 v2.1.0 h1:NAxZrWqNUQiDz19FKScQ/xvwzmij6BiOw3S0+QUQ+Hs=
|
||||
github.com/ashanbrown/forbidigo/v2 v2.1.0/go.mod h1:0zZfdNAuZIL7rSComLGthgc/9/n2FqspBOH90xlCHdA=
|
||||
github.com/ashanbrown/makezero/v2 v2.0.1 h1:r8GtKetWOgoJ4sLyUx97UTwyt2dO7WkGFHizn/Lo8TY=
|
||||
github.com/ashanbrown/makezero/v2 v2.0.1/go.mod h1:kKU4IMxmYW1M4fiEHMb2vc5SFoPzXvgbMR9gIp5pjSw=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
|
||||
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA=
|
||||
github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw=
|
||||
github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA=
|
||||
github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c=
|
||||
github.com/bep/godartsass/v2 v2.5.0 h1:tKRvwVdyjCIr48qgtLa4gHEdtRkPF8H1OeEhJAEv7xg=
|
||||
github.com/bep/godartsass/v2 v2.5.0/go.mod h1:rjsi1YSXAl/UbsGL85RLDEjRKdIKUlMQHr6ChUNYOFU=
|
||||
github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI=
|
||||
github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
|
||||
github.com/bep/goportabletext v0.1.0 h1:8dqym2So1cEqVZiBa4ZnMM1R9l/DnC1h4ONg4J5kujw=
|
||||
github.com/bep/goportabletext v0.1.0/go.mod h1:6lzSTsSue75bbcyvVc0zqd1CdApuT+xkZQ6Re5DzZFg=
|
||||
github.com/bep/gowebp v0.4.0 h1:QihuVnvIKbRoeBNQkN0JPMM8ClLmD6V2jMftTFwSK3Q=
|
||||
github.com/bep/gowebp v0.4.0/go.mod h1:95gtYkAA8iIn1t3HkAPurRCVGV/6NhgaHJ1urz0iIwc=
|
||||
github.com/bep/imagemeta v0.12.0 h1:ARf+igs5B7pf079LrqRnwzQ/wEB8Q9v4NSDRZO1/F5k=
|
||||
github.com/bep/imagemeta v0.12.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8=
|
||||
github.com/bep/lazycache v0.8.0 h1:lE5frnRjxaOFbkPZ1YL6nijzOPPz6zeXasJq8WpG4L8=
|
||||
github.com/bep/lazycache v0.8.0/go.mod h1:BQ5WZepss7Ko91CGdWz8GQZi/fFnCcyWupv8gyTeKwk=
|
||||
github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ=
|
||||
github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0=
|
||||
github.com/bep/overlayfs v0.10.0 h1:wS3eQ6bRsLX+4AAmwGjvoFSAQoeheamxofFiJ2SthSE=
|
||||
github.com/bep/overlayfs v0.10.0/go.mod h1:ouu4nu6fFJaL0sPzNICzxYsBeWwrjiTdFZdK4lI3tro=
|
||||
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
|
||||
github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0=
|
||||
github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w=
|
||||
github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
|
||||
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
|
||||
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
|
||||
github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ=
|
||||
github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg=
|
||||
github.com/bombsimon/wsl/v5 v5.1.1 h1:cQg5KJf9FlctAH4cpL9vLKnziYknoCMCdqXl0wjl72Q=
|
||||
github.com/bombsimon/wsl/v5 v5.1.1/go.mod h1:Gp8lD04z27wm3FANIUPZycXp+8huVsn0oxc+n4qfV9I=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
@@ -117,8 +159,8 @@ github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
|
||||
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
|
||||
github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD2z0vls0=
|
||||
github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -136,13 +178,13 @@ github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0G
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
|
||||
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs=
|
||||
github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
||||
@@ -150,10 +192,12 @@ github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmr
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
|
||||
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
|
||||
github.com/daixiang0/gci v0.13.6 h1:RKuEOSkGpSadkGbvZ6hJ4ddItT3cVZ9Vn9Rybk6xjl8=
|
||||
github.com/daixiang0/gci v0.13.6/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
|
||||
github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ=
|
||||
github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ=
|
||||
github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY=
|
||||
github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
@@ -163,6 +207,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
|
||||
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
|
||||
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
|
||||
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||
@@ -190,22 +236,29 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
|
||||
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
|
||||
github.com/evanw/esbuild v0.25.3 h1:4JKyUsm/nHDhpxis4IyWXAi8GiyTwG1WdEp6OhGVE8U=
|
||||
github.com/evanw/esbuild v0.25.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
|
||||
github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gen2brain/go-fitz v1.24.15 h1:sJNB1MOWkqnzzENPHggFpgxTwW0+S5WF/rM5wUBpJWo=
|
||||
github.com/gen2brain/go-fitz v1.24.15/go.mod h1:SftkiVbTHqF141DuiLwBBM65zP7ig6AVDQpf2WlHamo=
|
||||
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
|
||||
github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghostiam/protogetter v0.3.15 h1:1KF5sXel0HE48zh1/vn0Loiw25A9ApyseLzQuif1mLY=
|
||||
github.com/ghostiam/protogetter v0.3.15/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
|
||||
github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY=
|
||||
@@ -227,8 +280,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA=
|
||||
github.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
@@ -269,10 +322,12 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
|
||||
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
|
||||
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
|
||||
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
|
||||
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
@@ -280,13 +335,29 @@ github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ=
|
||||
github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg=
|
||||
github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec=
|
||||
github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs=
|
||||
github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI=
|
||||
github.com/gohugoio/hugo v0.147.6 h1:rL4rnus/5qzj4+FoA+JMzsVvFJ2YZdVIH6pbuCB2P84=
|
||||
github.com/gohugoio/hugo v0.147.6/go.mod h1:Sb2COQPDPYG+tRSpePtzKytiuVDqkBivEhgIew1QbNo=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0 h1:gj49kTR5Z4Hnm0ZaQrgPVazL3DUkppw+x6XhHCmh+Wk=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0/go.mod h1:IMMj7xiUbLt1YNJ6m7AM4cnsX4cFnnfkleO/lBHGzUg=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 h1:nUzXfRTszLliZuN0JTKeunXTRaiFX6ksaWP0puLLYAY=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1/go.mod h1:Wy8ThAA8p2/w1DY05vEzq6EIeI2mzDjvHsu7ULBVwog=
|
||||
github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc=
|
||||
github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4=
|
||||
github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo=
|
||||
github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20250821133510-ecfc33a939ac h1:gBtvd74wZT02fkOyoG651Sx2imv5lKdVvDwpUjKPFnw=
|
||||
github.com/golang/geo v0.0.0-20250821133510-ecfc33a939ac/go.mod h1:AN0OjM34c3PbjAsX+QNma1nYtJtRxl+s9MZNV7S+efw=
|
||||
github.com/golang/geo v0.0.0-20250825151631-54d70cc7cb31 h1:226lwSa0uZO7sd7QU88n43nDIqGoc+bOh0vSO3Q/byU=
|
||||
github.com/golang/geo v0.0.0-20250825151631-54d70cc7cb31/go.mod h1:AN0OjM34c3PbjAsX+QNma1nYtJtRxl+s9MZNV7S+efw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -324,16 +395,19 @@ github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUP
|
||||
github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s=
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
|
||||
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
|
||||
github.com/golangci/golangci-lint/v2 v2.1.6 h1:LXqShFfAGM5BDzEOWD2SL1IzJAgUOqES/HRBsfKjI+w=
|
||||
github.com/golangci/golangci-lint/v2 v2.1.6/go.mod h1:EPj+fgv4TeeBq3TcqaKZb3vkiV5dP4hHHKhXhEhzci8=
|
||||
github.com/golangci/golangci-lint v1.64.8 h1:y5TdeVidMtBGG32zgSC7ZXTFNHrsJkDnpO4ItB3Am+I=
|
||||
github.com/golangci/golangci-lint/v2 v2.4.0 h1:qz6O6vr7kVzXJqyvHjHSz5fA3D+PM8v96QU5gxZCNWM=
|
||||
github.com/golangci/golangci-lint/v2 v2.4.0/go.mod h1:Oq7vuAf6L1iNL34uHDcsIF6Mnc0amOPdsT3/GlpHD+I=
|
||||
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 h1:AkK+w9FZBXlU/xUmBtSJN1+tAI4FIvy5WtnUnY8e4p8=
|
||||
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
|
||||
github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc=
|
||||
github.com/golangci/misspell v0.7.0 h1:4GOHr/T1lTW0hhR4tgaaV1WS/lJ+ncvYCoFKmqJsj0c=
|
||||
github.com/golangci/misspell v0.7.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg=
|
||||
github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg=
|
||||
github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw=
|
||||
github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s=
|
||||
github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM=
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s=
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM=
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -348,7 +422,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
@@ -362,8 +435,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
@@ -386,6 +459,8 @@ github.com/gtsteffaniak/go-cache v0.0.0-20250521142451-edc77dfcb063 h1:CoLbeqTmM
|
||||
github.com/gtsteffaniak/go-cache v0.0.0-20250521142451-edc77dfcb063/go.mod h1:rHUZvf4huhMX/bnNK4/oQ3sNAE2fYC2jFpAWZiBPWac=
|
||||
github.com/gtsteffaniak/go-logger v0.1.2 h1:0g6UxxNwU+Q+7IVRw+Yn7AuYsIK0ukiTgbzjTLm+Uy4=
|
||||
github.com/gtsteffaniak/go-logger v0.1.2/go.mod h1:U3ZkdAclcxXNMvoQLq+zDkqcTmdqhX3hTf+6AuTo6v0=
|
||||
github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo=
|
||||
github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q=
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
@@ -404,14 +479,16 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU=
|
||||
github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jgautheron/goconst v1.8.1 h1:PPqCYp3K/xlOj5JmIe6O1Mj6r1DbkdbLtR3AJuZo414=
|
||||
github.com/jgautheron/goconst v1.8.1/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako=
|
||||
github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4=
|
||||
github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
|
||||
github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc=
|
||||
github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk=
|
||||
github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8=
|
||||
github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
@@ -450,18 +527,20 @@ github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
|
||||
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
|
||||
github.com/kunwardeep/paralleltest v1.0.14 h1:wAkMoMeGX/kGfhQBPODT/BL8XhK23ol/nuQ3SwFaUw8=
|
||||
github.com/kunwardeep/paralleltest v1.0.14/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk=
|
||||
github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U=
|
||||
github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
|
||||
github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
|
||||
github.com/ldez/exptostd v0.4.3 h1:Ag1aGiq2epGePuRJhez2mzOpZ8sI9Gimcb4Sb3+pk9Y=
|
||||
github.com/ldez/exptostd v0.4.3/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
|
||||
github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc=
|
||||
github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs=
|
||||
github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow=
|
||||
github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk=
|
||||
github.com/ldez/exptostd v0.4.4 h1:58AtQjnLcT/tI5W/1KU7xE/O7zW9RAWB6c/ScQAnfus=
|
||||
github.com/ldez/exptostd v0.4.4/go.mod h1:QfdzPw6oHjFVdNV7ILoPu5sw3OZ3OG1JS0I5JN3J4Js=
|
||||
github.com/ldez/gomoddirectives v0.7.0 h1:EOx8Dd56BZYSez11LVgdj025lKwlP0/E5OLSl9HDwsY=
|
||||
github.com/ldez/gomoddirectives v0.7.0/go.mod h1:wR4v8MN9J8kcwvrkzrx6sC9xe9Cp68gWYCsda5xvyGc=
|
||||
github.com/ldez/grignotin v0.10.0 h1:NQPeh1E/Eza4F0exCeC1WkpnLvgUcQDT8MQ1vOLML0E=
|
||||
github.com/ldez/grignotin v0.10.0/go.mod h1:oR4iCKUP9fwoeO6vCQeD7M5SMxCT6xdVas4vg0h1LaI=
|
||||
github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk=
|
||||
github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I=
|
||||
github.com/ldez/usetesting v0.4.3 h1:pJpN0x3fMupdTf/IapYjnkhiY1nSTN+pox1/GyBRw3k=
|
||||
github.com/ldez/usetesting v0.4.3/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ=
|
||||
github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc=
|
||||
github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
|
||||
@@ -474,12 +553,18 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/manuelarte/funcorder v0.2.1 h1:7QJsw3qhljoZ5rH0xapIvjw31EcQeFbF31/7kQ/xS34=
|
||||
github.com/manuelarte/funcorder v0.2.1/go.mod h1:BQQ0yW57+PF9ZpjpeJDKOffEsQbxDFKW8F8zSMe/Zd0=
|
||||
github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE=
|
||||
github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc=
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.3.0 h1:VhGqK8gANDvFYDxQkjPbv7/gDJtsGU9k6qj/hC2hgso=
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.3.0/go.mod h1:LSo/IQpPfx1dXMcX4ibZCYA7Yy6ayZHIaOGM70+1Wy8=
|
||||
github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8=
|
||||
github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA=
|
||||
github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
|
||||
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
|
||||
github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
|
||||
github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
|
||||
github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mrXGUqIwn0=
|
||||
github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA=
|
||||
github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4=
|
||||
github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
@@ -488,42 +573,51 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgechev/revive v1.9.0 h1:8LaA62XIKrb8lM6VsBSQ92slt/o92z5+hTw3CmrvSrM=
|
||||
github.com/mgechev/revive v1.9.0/go.mod h1:LAPq3+MgOf7GcL5PlWIkHb0PT7XH4NuC2LdWymhb9Mo=
|
||||
github.com/mgechev/revive v1.11.0 h1:b/gLLpBE427o+Xmd8G58gSA+KtBwxWinH/A565Awh0w=
|
||||
github.com/mgechev/revive v1.11.0/go.mod h1:tI0oLF/2uj+InHCBLrrqfTKfjtFTBCFFfG05auyzgdw=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI=
|
||||
github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
|
||||
github.com/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc=
|
||||
github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
|
||||
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
|
||||
github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
|
||||
github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
|
||||
github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg=
|
||||
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4=
|
||||
github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
|
||||
github.com/nunnatsa/ginkgolinter v0.20.0 h1:OmWLkAFO2HUTYcU6mprnKud1Ey5pVdiVNYGO5HVicx8=
|
||||
github.com/nunnatsa/ginkgolinter v0.20.0/go.mod h1:dCIuFlTPfQerXgGUju3VygfAFPdC5aE1mdacCDKDJcQ=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||
@@ -531,10 +625,14 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -596,14 +694,14 @@ github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9f
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
|
||||
github.com/securego/gosec/v2 v2.22.3 h1:mRrCNmRF2NgZp4RJ8oJ6yPJ7G4x6OCiAXHd8x4trLRc=
|
||||
github.com/securego/gosec/v2 v2.22.3/go.mod h1:42M9Xs0v1WseinaB/BmNGO8AVqG8vRfhC2686ACY48k=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8=
|
||||
github.com/securego/gosec/v2 v2.22.7 h1:8/9P+oTYI4yIpAzccQKVsg1/90Po+JzGtAhqoHImDeM=
|
||||
github.com/securego/gosec/v2 v2.22.7/go.mod h1:510TFNDMrIPytokyHQAVLvPeDr41Yihn2ak8P+XQfNE=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
@@ -616,21 +714,22 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
|
||||
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
|
||||
github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM=
|
||||
github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c=
|
||||
github.com/sonatard/noctx v0.4.0 h1:7MC/5Gg4SQ4lhLYR6mvOP6mQVSxCrdyiExo7atBs27o=
|
||||
github.com/sonatard/noctx v0.4.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas=
|
||||
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
|
||||
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
@@ -639,18 +738,12 @@ github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8B
|
||||
github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
@@ -663,12 +756,20 @@ github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8=
|
||||
github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8=
|
||||
github.com/tdewolff/minify/v2 v2.23.5 h1:/P548KcpTkIOUvNg22zN83/GiaYSOIrbqtoue4I7kYM=
|
||||
github.com/tdewolff/minify/v2 v2.23.5/go.mod h1:2RI9tiIrzJU1Z5EasXEPaI1MqobRyxKHOOgrRkq5oEw=
|
||||
github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po=
|
||||
github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
|
||||
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
|
||||
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
|
||||
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
|
||||
github.com/tetafro/godot v1.5.1 h1:PZnjCol4+FqaEzvZg5+O8IY2P3hfY9JzRBNPv1pEDS4=
|
||||
github.com/tetafro/godot v1.5.1/go.mod h1:cCdPtEndkmqqrhiCfkmxDodMQJ/f3L1BCNskCUZdTwk=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk=
|
||||
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
|
||||
github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M=
|
||||
@@ -685,8 +786,8 @@ github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
|
||||
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
|
||||
github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U=
|
||||
github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg=
|
||||
github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU=
|
||||
github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM=
|
||||
@@ -706,14 +807,20 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo=
|
||||
github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
|
||||
github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA=
|
||||
gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
|
||||
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
|
||||
go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
|
||||
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
|
||||
go-simpler.org/musttag v0.13.1 h1:lw2sJyu7S1X8lc8zWUAdH42y+afdcCnHhWpnkWvd6vU=
|
||||
go-simpler.org/musttag v0.13.1/go.mod h1:8r450ehpMLQgvpb6sg+hV5Ur47eH6olp/3yEanfG97k=
|
||||
go-simpler.org/sloglint v0.11.0 h1:JlR1X4jkbeaffiyjLtymeqmGDKBDO1ikC6rjiuFAOco=
|
||||
go-simpler.org/sloglint v0.11.0/go.mod h1:CFDO8R1i77dlciGfPEPvYke2ZMx4eyGiEIWkyeW2Pvw=
|
||||
go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s=
|
||||
go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ=
|
||||
go.augendre.info/arangolint v0.2.0 h1:2NP/XudpPmfBhQKX4rMk+zDYIj//qbt4hfZmSSTcpj8=
|
||||
go.augendre.info/arangolint v0.2.0/go.mod h1:Vx4KSJwu48tkE+8uxuf0cbBnAPgnt8O1KWiT7bljq7w=
|
||||
go.augendre.info/fatcontext v0.8.0 h1:2dfk6CQbDGeu1YocF59Za5Pia7ULeAM6friJ3LP7lmk=
|
||||
go.augendre.info/fatcontext v0.8.0/go.mod h1:oVJfMgwngMsHO+KB2MdgzcO+RvtNdiCEOlWvSFtax/s=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
@@ -724,16 +831,14 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -755,12 +860,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b h1:KdrhdYPDUvJTvrDK9gdjfFd6JTk8vA1WJoldYSi0kHo=
|
||||
golang.org/x/exp/typeparams v0.0.0-20250620022241-b7579e27df2b/go.mod h1:LKZHyeOpPuZcMgxeHjJp4p5yvxrCX1xDvH10zYHhjjQ=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
|
||||
@@ -787,9 +892,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
@@ -831,10 +934,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
@@ -908,13 +1009,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -924,9 +1023,7 @@ golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
@@ -937,9 +1034,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
@@ -1000,9 +1095,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
@@ -1129,6 +1222,8 @@ mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
|
||||
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
|
||||
@@ -71,7 +71,11 @@ func sseHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int,
|
||||
return http.StatusInternalServerError, fmt.Errorf("error sending broadcast: %v, user: %s", err, username)
|
||||
}
|
||||
|
||||
case msg := <-sendChan:
|
||||
case msg, ok := <-sendChan:
|
||||
if !ok {
|
||||
logger.Debugf("SSE channel closed for user: %s, SessionId: %s", username, sessionId)
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
if err := msgr.sendEvent(msg.EventType, msg.Message); err != nil {
|
||||
return http.StatusInternalServerError, fmt.Errorf("error sending targeted message: %v, user: %s", err, username)
|
||||
}
|
||||
|
||||
+43
-65
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/version"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/database/storage/bolt"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/events"
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
// http-swagger middleware
|
||||
)
|
||||
@@ -23,9 +25,6 @@ import (
|
||||
//go:embed embed/*
|
||||
var assets embed.FS
|
||||
|
||||
// Boolean flag to determine whether to use the embedded FS or not
|
||||
var embeddedFS = os.Getenv("FILEBROWSER_NO_EMBEDED") != "true"
|
||||
|
||||
// Custom dirFS to handle both embedded and non-embedded file systems
|
||||
type dirFS struct {
|
||||
http.Dir
|
||||
@@ -45,7 +44,18 @@ var (
|
||||
func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete chan struct{}) {
|
||||
store = storage
|
||||
config = &settings.Config
|
||||
var err error
|
||||
|
||||
// Dev mode enables development features like template hot-reloading
|
||||
devMode := os.Getenv("FILEBROWSER_DEVMODE") == "true"
|
||||
_, err := os.Stat("http/dist")
|
||||
// In dev mode, always use filesystem assets. Otherwise, check if http/dist exists
|
||||
var embeddedFS bool
|
||||
if devMode {
|
||||
embeddedFS = false
|
||||
} else {
|
||||
embeddedFS = os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// --- START: ADD THIS DECRYPTION LOGIC ---
|
||||
if embeddedFS {
|
||||
// Embedded mode: Serve files from the embedded assets
|
||||
@@ -57,8 +67,20 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
|
||||
assetFs = dirFS{Dir: http.Dir("http/dist")}
|
||||
}
|
||||
|
||||
// In development mode, we want to reload the templates on each request.
|
||||
// In production (embedded), we parse them once.
|
||||
templates := template.New("").Funcs(template.FuncMap{
|
||||
"marshal": func(v interface{}) (string, error) {
|
||||
a, err := json.Marshal(v)
|
||||
return string(a), err
|
||||
},
|
||||
})
|
||||
if !devMode {
|
||||
templates = template.Must(templates.ParseFS(assetFs, "public/index.html"))
|
||||
}
|
||||
templateRenderer = &TemplateRenderer{
|
||||
templates: template.Must(template.ParseFS(assetFs, "public/index.html")),
|
||||
templates: templates,
|
||||
devMode: devMode,
|
||||
}
|
||||
|
||||
router := http.NewServeMux()
|
||||
@@ -95,6 +117,7 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
|
||||
api.HandleFunc("PATCH /resources", withUser(resourcePatchHandler))
|
||||
api.HandleFunc("GET /raw", withUser(rawHandler))
|
||||
api.HandleFunc("GET /preview", withUser(previewHandler))
|
||||
api.HandleFunc("GET /media/subtitles", withUser(subtitlesHandler))
|
||||
if version.Version == "testing" || version.Version == "untracked" {
|
||||
api.HandleFunc("GET /inspectIndex", inspectIndex)
|
||||
api.HandleFunc("GET /mockData", mockData)
|
||||
@@ -106,22 +129,28 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
|
||||
api.HandleFunc("GET /access/groups", withAdmin(groupGetHandler))
|
||||
api.HandleFunc("POST /access/group", withAdmin(groupPostHandler))
|
||||
api.HandleFunc("DELETE /access/group", withAdmin(groupDeleteHandler))
|
||||
|
||||
// Share management routes (require permission)
|
||||
api.HandleFunc("GET /shares", withPermShare(shareListHandler))
|
||||
api.HandleFunc("GET /share/direct", withPermShare(shareDirectDownloadHandler))
|
||||
api.HandleFunc("GET /share", withPermShare(shareGetHandler))
|
||||
api.HandleFunc("POST /share", withPermShare(sharePostHandler))
|
||||
api.HandleFunc("DELETE /share", withPermShare(shareDeleteHandler))
|
||||
|
||||
// Create API sub-router for public API endpoints
|
||||
publicAPI := http.NewServeMux()
|
||||
// NEW PUBLIC ROUTES - All publicly accessible endpoints
|
||||
// Share management routes (require permission but are publicly accessible)
|
||||
publicRoutes.HandleFunc("GET /shares", withPermShare(shareListHandler))
|
||||
publicRoutes.HandleFunc("GET /share/direct", withPermShare(shareDirectDownloadHandler))
|
||||
publicRoutes.HandleFunc("GET /share", withPermShare(shareGetHandler))
|
||||
publicRoutes.HandleFunc("POST /share", withPermShare(sharePostHandler))
|
||||
publicRoutes.HandleFunc("DELETE /share", withPermShare(shareDeleteHandler))
|
||||
// Public API routes (hash-based authentication)
|
||||
publicAPI.HandleFunc("GET /raw", withHashFile(publicRawHandler))
|
||||
publicAPI.HandleFunc("GET /preview", withHashFile(publicPreviewHandler))
|
||||
publicAPI.HandleFunc("GET /resources", withHashFile(publicShareHandler))
|
||||
publicAPI.HandleFunc("GET /users", withUser(userGetHandler))
|
||||
publicAPI.HandleFunc("POST /onlyoffice/callback", withHashFile(onlyofficeCallbackHandler))
|
||||
publicAPI.HandleFunc("GET /onlyoffice/config", withHashFile(onlyofficeClientConfigGetHandler))
|
||||
|
||||
// Settings routes
|
||||
api.HandleFunc("GET /settings", withAdmin(settingsGetHandler))
|
||||
api.HandleFunc("GET /settings/config", withAdmin(settingsConfigHandler))
|
||||
|
||||
// Events routes
|
||||
api.HandleFunc("GET /events", withUser(func(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
@@ -132,63 +161,9 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
|
||||
api.HandleFunc("GET /jobs/{action}/{target}", withUser(getJobsHandler))
|
||||
|
||||
api.HandleFunc("GET /onlyoffice/config", withUser(onlyofficeClientConfigGetHandler))
|
||||
publicAPI.HandleFunc("GET /onlyoffice/config", withHashFile(onlyofficeClientConfigGetHandler))
|
||||
api.HandleFunc("POST /onlyoffice/callback", withUser(onlyofficeCallbackHandler))
|
||||
publicAPI.HandleFunc("POST /onlyoffice/callback", withHashFile(onlyofficeCallbackHandler))
|
||||
api.HandleFunc("GET /onlyoffice/getToken", withUser(onlyofficeGetTokenHandler))
|
||||
publicAPI.HandleFunc("GET /onlyoffice/getToken", withHashFile(onlyofficeGetTokenHandler))
|
||||
|
||||
api.HandleFunc("GET /search", withUser(searchHandler))
|
||||
|
||||
// Share routes (DEPRECATED - maintain for backwards compatibility)
|
||||
// These will redirect to the new /public/shares endpoints
|
||||
api.HandleFunc("GET /shares", func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL := config.Server.BaseURL + "public/shares"
|
||||
if r.URL.RawQuery != "" {
|
||||
newURL += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, newURL, http.StatusMovedPermanently)
|
||||
})
|
||||
api.HandleFunc("GET /share", func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL := config.Server.BaseURL + "public/share"
|
||||
if r.URL.RawQuery != "" {
|
||||
newURL += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, newURL, http.StatusMovedPermanently)
|
||||
})
|
||||
api.HandleFunc("POST /share", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, config.Server.BaseURL+"public/share", http.StatusPermanentRedirect)
|
||||
})
|
||||
api.HandleFunc("DELETE /share", func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL := config.Server.BaseURL + "public/share"
|
||||
if r.URL.RawQuery != "" {
|
||||
newURL += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, newURL, http.StatusPermanentRedirect)
|
||||
})
|
||||
|
||||
api.HandleFunc("GET /public/raw", func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL := config.Server.BaseURL + "public/api/raw"
|
||||
if r.URL.RawQuery != "" {
|
||||
newURL += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, newURL, http.StatusMovedPermanently)
|
||||
})
|
||||
api.HandleFunc("GET /public/share", func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL := config.Server.BaseURL + "public/api/shared"
|
||||
if r.URL.RawQuery != "" {
|
||||
newURL += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, newURL, http.StatusMovedPermanently)
|
||||
})
|
||||
api.HandleFunc("GET /public/preview", func(w http.ResponseWriter, r *http.Request) {
|
||||
newURL := config.Server.BaseURL + "public/api/preview"
|
||||
if r.URL.RawQuery != "" {
|
||||
newURL += "?" + r.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, r, newURL, http.StatusMovedPermanently)
|
||||
})
|
||||
|
||||
// Mount the public API sub-router
|
||||
publicRoutes.Handle("/api/", http.StripPrefix("/api", publicAPI))
|
||||
|
||||
@@ -281,6 +256,9 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
|
||||
<-ctx.Done()
|
||||
logger.Info("Shutting down HTTP server...")
|
||||
|
||||
// Close all SSE sessions
|
||||
events.Shutdown()
|
||||
|
||||
// Persist in-memory state before shutting down the HTTP server
|
||||
if store != nil {
|
||||
if store.Share != nil {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/ffmpeg"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/indexing"
|
||||
)
|
||||
|
||||
// subtitlesHandler handles subtitle extraction requests
|
||||
// @Summary Extract embedded subtitles
|
||||
// @Description Extracts embedded subtitle content from video files by stream index and returns raw WebVTT content
|
||||
// @Tags Subtitles
|
||||
// @Accept json
|
||||
// @Produce text/vtt
|
||||
// @Param path query string true "Index path to the video file"
|
||||
// @Param source query string true "Source name for the desired source"
|
||||
// @Param index query int false "Stream index for embedded subtitle extraction, defaults to 0"
|
||||
// @Success 200 {string} string "Raw WebVTT subtitle content"
|
||||
// @Failure 400 {object} map[string]string "Bad request"
|
||||
// @Failure 403 {object} map[string]string "Forbidden"
|
||||
// @Failure 404 {object} map[string]string "Resource not found"
|
||||
// @Failure 500 {object} map[string]string "Internal server error"
|
||||
// @Router /api/media/subtitles [get]
|
||||
func subtitlesHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
encodedPath := r.URL.Query().Get("path")
|
||||
source := r.URL.Query().Get("source")
|
||||
indexParam := r.URL.Query().Get("index")
|
||||
|
||||
if indexParam == "" {
|
||||
indexParam = "0" // default to first subtitle stream
|
||||
}
|
||||
|
||||
path, err := url.QueryUnescape(encodedPath)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
}
|
||||
source, err = url.QueryUnescape(source)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
|
||||
}
|
||||
|
||||
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
|
||||
idx := indexing.GetIndex(source)
|
||||
if idx == nil {
|
||||
return http.StatusNotFound, fmt.Errorf("source %s not found", source)
|
||||
}
|
||||
realPath, _, err := idx.GetRealPath(userscope, path)
|
||||
if err != nil {
|
||||
return http.StatusNotFound, fmt.Errorf("file not found: %v", err)
|
||||
}
|
||||
metadata, exists := idx.GetMetadataInfo(userscope, true)
|
||||
if !exists {
|
||||
return http.StatusNotFound, fmt.Errorf("file not found: %v", err)
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(indexParam)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid index parameter: %v", err)
|
||||
}
|
||||
parentDir := filepath.Dir(realPath)
|
||||
subtitle, err := ffmpeg.ExtractSingleSubtitle(realPath, parentDir, index, metadata.ModTime)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, fmt.Errorf("failed to extract subtitle: %v", err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/vtt; charset=utf-8")
|
||||
w.Header().Set("Content-Disposition", "inline")
|
||||
w.Header().Set("Cache-Control", "private")
|
||||
http.ServeContent(w, r, fmt.Sprintf("%s-%d.vtt", subtitle.Name, index),
|
||||
time.Now(), bytes.NewReader([]byte(subtitle.Content)))
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/gtsteffaniak/filebrowser/backend/adapters/fs/files"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/auth"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/database/share"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/database/users"
|
||||
@@ -90,11 +91,16 @@ func withHashFileHelper(fn handleFunc) handleFunc {
|
||||
Expand: true,
|
||||
Content: getContent,
|
||||
})
|
||||
file.Token = link.Token
|
||||
if err != nil {
|
||||
logger.Errorf("error fetching file info for share. hash=%v path=%v error=%v", hash, path, err)
|
||||
return errToStatus(err), fmt.Errorf("error fetching share from server")
|
||||
}
|
||||
file.Token = link.Token
|
||||
file.Source = ""
|
||||
file.Hash = link.Hash
|
||||
if !link.EnableOnlyOffice || !link.DisableFileViewer {
|
||||
file.OnlyOfficeId = ""
|
||||
}
|
||||
if getContent && file.Content != "" {
|
||||
link.Mu.Lock()
|
||||
link.Downloads++
|
||||
@@ -102,7 +108,7 @@ func withHashFileHelper(fn handleFunc) handleFunc {
|
||||
}
|
||||
file.Path = "/" + strings.TrimPrefix(strings.TrimPrefix(file.Path, link.Path), "/")
|
||||
// Set the file info in the `data` object
|
||||
data.fileInfo = file
|
||||
data.fileInfo = *file
|
||||
// Call the next handler with the data
|
||||
return fn(w, r, data)
|
||||
})
|
||||
@@ -180,7 +186,8 @@ func withOrWithoutUserHelper(fn handleFunc) handleFunc {
|
||||
}
|
||||
// Only fall back to anonymous if authentication actually failed
|
||||
if status == http.StatusUnauthorized || status == http.StatusForbidden {
|
||||
data.user = &users.AnonymousUser
|
||||
data.user = &users.User{Username: "anonymous"}
|
||||
settings.ApplyUserDefaults(data.user)
|
||||
// If user authentication failed, call the handler without user context
|
||||
// Clear any user data that might have been partially set
|
||||
data.token = ""
|
||||
|
||||
@@ -43,8 +43,8 @@ func mockFileInfoFaster(t *testing.T) {
|
||||
t.Cleanup(func() { FileInfoFasterFunc = originalFileInfoFaster })
|
||||
|
||||
// Mock the function to skip execution
|
||||
FileInfoFasterFunc = func(opts iteminfo.FileOptions) (iteminfo.ExtendedFileInfo, error) {
|
||||
return iteminfo.ExtendedFileInfo{
|
||||
FileInfoFasterFunc = func(opts iteminfo.FileOptions) (*iteminfo.ExtendedFileInfo, error) {
|
||||
return &iteminfo.ExtendedFileInfo{
|
||||
FileInfo: iteminfo.FileInfo{
|
||||
Path: opts.Path,
|
||||
ItemInfo: iteminfo.ItemInfo{
|
||||
|
||||
+222
-100
@@ -38,68 +38,100 @@ func onlyofficeClientConfigGetHandler(w http.ResponseWriter, r *http.Request, d
|
||||
if settings.Config.Integrations.OnlyOffice.Url == "" {
|
||||
return http.StatusInternalServerError, errors.New("only-office integration must be configured in settings")
|
||||
}
|
||||
encodedUrl := r.URL.Query().Get("url")
|
||||
// Decode the URL-encoded path
|
||||
givenUrl, err := url.QueryUnescape(encodedUrl)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
|
||||
// Extract clean parameters from request
|
||||
source := r.URL.Query().Get("source")
|
||||
path := r.URL.Query().Get("path")
|
||||
|
||||
// Validate required parameters
|
||||
if (path == "" || source == "") && d.fileInfo.Hash == "" {
|
||||
logger.Errorf("OnlyOffice callback missing required parameters: source=%s, path=%s", source, path)
|
||||
return http.StatusBadRequest, errors.New("missing required parameters: path + source/hash are required")
|
||||
}
|
||||
// get path from url
|
||||
pathParts := strings.Split(givenUrl, "/api/raw?files=")
|
||||
origPathParts := strings.Split(encodedUrl, "/api/raw?files=")
|
||||
encodedPath := origPathParts[len(origPathParts)-1]
|
||||
sourceFile := pathParts[len(pathParts)-1]
|
||||
sourceSplit := strings.Split(sourceFile, "::")
|
||||
if len(sourceSplit) != 2 {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid url path %v", givenUrl)
|
||||
}
|
||||
source := sourceSplit[0]
|
||||
path := sourceSplit[1]
|
||||
urlFirst := pathParts[0]
|
||||
if settings.Config.Server.InternalUrl != "" {
|
||||
urlFirst = settings.Config.Server.InternalUrl + settings.Config.Server.BaseURL
|
||||
whatToReplace := strings.Split(givenUrl, "/api/raw")[0] + "/"
|
||||
givenUrl = strings.Replace(givenUrl, whatToReplace, urlFirst, 1)
|
||||
}
|
||||
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
fileInfo, err := files.FileInfoFaster(iteminfo.FileOptions{
|
||||
Path: utils.JoinPathAsUnix(userscope, path),
|
||||
Modify: d.user.Permissions.Modify,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Debugf("onlyofficeClientConfigGetHandler: failed to get file info for file source %v, path %v: %v", source, path, err)
|
||||
return errToStatus(err), err
|
||||
}
|
||||
id, err := getOnlyOfficeId(source, fileInfo.Path)
|
||||
if err != nil {
|
||||
logger.Debugf("getOnlyOfficeId failed for file source %v, path %v: %v", source, fileInfo.Path, err)
|
||||
return http.StatusNotFound, err
|
||||
}
|
||||
split := strings.Split(fileInfo.Name, ".")
|
||||
fileType := split[len(split)-1]
|
||||
theme := "light"
|
||||
if d.user.DarkMode {
|
||||
theme = "dark"
|
||||
themeMode := utils.Ternary(d.user.DarkMode, "dark", "light")
|
||||
|
||||
if d.fileInfo.Hash == "" {
|
||||
// Build file info based on whether this is a share or regular request
|
||||
// Regular user request - need to resolve scope
|
||||
userScope, scopeErr := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if scopeErr != nil {
|
||||
logger.Errorf("OnlyOffice: source %s not available for user %s: %v", source, d.user.Username, scopeErr)
|
||||
return http.StatusForbidden, fmt.Errorf("source %s is not available", source)
|
||||
}
|
||||
resolvedPath := utils.JoinPathAsUnix(userScope, path)
|
||||
logger.Debugf("OnlyOffice user request: resolved path=%s", resolvedPath)
|
||||
|
||||
fileInfo, err := files.FileInfoFaster(iteminfo.FileOptions{
|
||||
Path: resolvedPath,
|
||||
Modify: d.user.Permissions.Modify,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Errorf("OnlyOffice: failed to get file info for source=%s, path=%s: %v", source, resolvedPath, err)
|
||||
return errToStatus(err), err
|
||||
}
|
||||
d.fileInfo = *fileInfo
|
||||
} else {
|
||||
// is a share, use the file info from the share middleware
|
||||
sourceInfo, ok := settings.Config.Server.SourceMap[d.share.Source]
|
||||
if !ok {
|
||||
logger.Error("OnlyOffice: source from share not found")
|
||||
return http.StatusInternalServerError, fmt.Errorf("source not found for share")
|
||||
}
|
||||
source = sourceInfo.Name
|
||||
// path is index path, so we build from share path
|
||||
path = utils.JoinPathAsUnix(d.share.Path, path)
|
||||
if d.share.EnforceDarkLightMode == "dark" {
|
||||
themeMode = "dark"
|
||||
}
|
||||
if d.share.EnforceDarkLightMode == "light" {
|
||||
themeMode = "light"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Determine file type and editing permissions
|
||||
fileType := getFileExtension(d.fileInfo.Name)
|
||||
canEdit := iteminfo.CanEditOnlyOffice(d.user.Permissions.Modify, fileType)
|
||||
mode := "view"
|
||||
if canEdit {
|
||||
mode = "edit"
|
||||
canEditMode := utils.Ternary(canEdit, "edit", "view")
|
||||
if d.fileInfo.Hash != "" {
|
||||
if d.share.EnableOnlyOfficeEditing {
|
||||
canEditMode = "edit"
|
||||
}
|
||||
}
|
||||
callbackURL := fmt.Sprintf("%v/api/onlyoffice/callback?files=%v&auth=%v", strings.TrimSuffix(urlFirst, "/"), encodedPath, d.token)
|
||||
// For shares, we need to keep track of the original relative path for the callback URL
|
||||
var callbackPath string
|
||||
if d.fileInfo.Hash != "" {
|
||||
// For shares, use the original path parameter (relative to share)
|
||||
callbackPath = r.URL.Query().Get("path")
|
||||
} else {
|
||||
// For regular requests, use the processed path
|
||||
callbackPath = path
|
||||
}
|
||||
|
||||
// Generate document ID for OnlyOffice
|
||||
documentId, err := getOnlyOfficeId(source, path)
|
||||
if err != nil {
|
||||
logger.Errorf("OnlyOffice: failed to generate document ID for source=%s, path=%s: %v", source, path, err)
|
||||
return http.StatusNotFound, fmt.Errorf("failed to generate document ID: %v", err)
|
||||
}
|
||||
|
||||
// Build download URL that OnlyOffice server will use
|
||||
downloadURL := buildOnlyOfficeDownloadURL(source, callbackPath, d.fileInfo.Hash, d.token)
|
||||
|
||||
// Build callback URL for OnlyOffice to notify us of changes
|
||||
callbackURL := buildOnlyOfficeCallbackURL(source, callbackPath, d.fileInfo.Hash, d.token)
|
||||
|
||||
// Build OnlyOffice client configuration
|
||||
clientConfig := map[string]interface{}{
|
||||
"document": map[string]interface{}{
|
||||
"fileType": fileType,
|
||||
"key": id,
|
||||
"title": fileInfo.Name,
|
||||
"url": givenUrl + "&auth=" + d.token,
|
||||
"key": documentId,
|
||||
"title": d.fileInfo.Name,
|
||||
"url": downloadURL,
|
||||
"permissions": map[string]interface{}{
|
||||
"edit": canEdit,
|
||||
"edit": canEditMode,
|
||||
"download": true,
|
||||
"print": true,
|
||||
},
|
||||
@@ -113,78 +145,194 @@ func onlyofficeClientConfigGetHandler(w http.ResponseWriter, r *http.Request, d
|
||||
"customization": map[string]interface{}{
|
||||
"autosave": true,
|
||||
"forcesave": true,
|
||||
"uiTheme": theme,
|
||||
"uiTheme": themeMode,
|
||||
},
|
||||
"lang": d.user.Locale,
|
||||
"mode": mode,
|
||||
"mode": canEditMode,
|
||||
},
|
||||
}
|
||||
|
||||
// Sign configuration with JWT if secret is configured
|
||||
if settings.Config.Integrations.OnlyOffice.Secret != "" {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(clientConfig))
|
||||
signature, err := token.SignedString([]byte(settings.Config.Integrations.OnlyOffice.Secret))
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, fmt.Errorf("failed to sign JWT")
|
||||
logger.Errorf("OnlyOffice: failed to sign JWT: %v", err)
|
||||
return http.StatusInternalServerError, fmt.Errorf("failed to sign configuration")
|
||||
}
|
||||
clientConfig["token"] = signature
|
||||
}
|
||||
|
||||
return renderJSON(w, r, clientConfig)
|
||||
}
|
||||
|
||||
// getFileExtension extracts file extension from filename
|
||||
func getFileExtension(filename string) string {
|
||||
parts := strings.Split(filename, ".")
|
||||
if len(parts) < 2 {
|
||||
return ""
|
||||
}
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
// buildOnlyOfficeDownloadURL constructs the download URL that OnlyOffice server will use to fetch the file
|
||||
func buildOnlyOfficeDownloadURL(source, path, hash, token string) string {
|
||||
// Determine base URL (internal URL takes priority for OnlyOffice server communication)
|
||||
baseURL := settings.Config.Server.BaseURL
|
||||
if settings.Config.Server.InternalUrl != "" {
|
||||
baseURL = settings.Config.Server.InternalUrl + settings.Config.Server.BaseURL
|
||||
}
|
||||
|
||||
var downloadURL string
|
||||
if hash != "" {
|
||||
// Share download URL - don't expose source name, just use the path relative to share
|
||||
filesParam := url.QueryEscape(path)
|
||||
downloadURL = fmt.Sprintf("%s/public/api/raw?files=%s&hash=%s&token=%s&auth=%s",
|
||||
strings.TrimSuffix(baseURL, "/"), filesParam, hash, token, token)
|
||||
} else {
|
||||
// Regular download URL - include source for non-share requests
|
||||
filesParam := url.QueryEscape(source + "::" + path)
|
||||
downloadURL = fmt.Sprintf("%s/api/raw?files=%s&auth=%s",
|
||||
strings.TrimSuffix(baseURL, "/"), filesParam, token)
|
||||
}
|
||||
|
||||
return downloadURL
|
||||
}
|
||||
|
||||
// buildOnlyOfficeCallbackURL constructs the callback URL that OnlyOffice server will use to notify us of changes
|
||||
func buildOnlyOfficeCallbackURL(source, path, hash, token string) string {
|
||||
baseURL := settings.Config.Server.BaseURL
|
||||
if settings.Config.Server.InternalUrl != "" {
|
||||
baseURL = settings.Config.Server.InternalUrl + settings.Config.Server.BaseURL
|
||||
}
|
||||
|
||||
var callbackURL string
|
||||
if hash != "" {
|
||||
// Share callback URL - use public API and don't expose source, use path relative to share
|
||||
params := url.Values{}
|
||||
params.Set("hash", hash)
|
||||
params.Set("path", path) // This should be the path relative to the share, not the full filesystem path
|
||||
params.Set("auth", token)
|
||||
|
||||
callbackURL = fmt.Sprintf("%s/public/api/onlyoffice/callback?%s",
|
||||
strings.TrimSuffix(baseURL, "/"), params.Encode())
|
||||
} else {
|
||||
// Regular callback URL - include source for non-share requests
|
||||
params := url.Values{}
|
||||
params.Set("source", source)
|
||||
params.Set("path", path)
|
||||
params.Set("auth", token)
|
||||
|
||||
callbackURL = fmt.Sprintf("%s/api/onlyoffice/callback?%s",
|
||||
strings.TrimSuffix(baseURL, "/"), params.Encode())
|
||||
}
|
||||
|
||||
return callbackURL
|
||||
}
|
||||
|
||||
func onlyofficeCallbackHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
// Parse OnlyOffice callback data
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
var data OnlyOfficeCallback
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
logger.Errorf("OnlyOffice callback: failed to read request body: %v", err)
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
encodedPath := r.URL.Query().Get("files")
|
||||
pathParts := strings.Split(encodedPath, "::")
|
||||
if len(pathParts) < 2 {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
}
|
||||
source := pathParts[0]
|
||||
// Decode the URL-encoded path
|
||||
path, err := url.QueryUnescape(pathParts[1])
|
||||
var data OnlyOfficeCallback
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
logger.Errorf("OnlyOffice callback: failed to parse JSON: %v", err)
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
// Extract clean parameters from query string
|
||||
source := r.URL.Query().Get("source")
|
||||
path := r.URL.Query().Get("path")
|
||||
|
||||
// Validate required parameters
|
||||
if (path == "" || source == "") && d.fileInfo.Hash == "" {
|
||||
logger.Errorf("OnlyOffice callback missing required parameters: source=%s, path=%s", source, path)
|
||||
return http.StatusBadRequest, errors.New("missing required parameters: path + source/hash are required")
|
||||
}
|
||||
|
||||
if d.fileInfo.Hash == "" {
|
||||
// Regular user request - need to resolve scope
|
||||
userScope, scopeErr := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if scopeErr != nil {
|
||||
logger.Errorf("OnlyOffice callback: source %s not available for user %s: %v", source, d.user.Username, scopeErr)
|
||||
return http.StatusForbidden, fmt.Errorf("source %s is not available", source)
|
||||
}
|
||||
path = utils.JoinPathAsUnix(userScope, path)
|
||||
} else {
|
||||
// is a share, use the file info from the share middleware
|
||||
sourceInfo, ok := settings.Config.Server.SourceMap[d.share.Source]
|
||||
if !ok {
|
||||
logger.Error("OnlyOffice: source from share not found")
|
||||
return http.StatusInternalServerError, fmt.Errorf("source not found for share")
|
||||
}
|
||||
source = sourceInfo.Name
|
||||
// path is index path, so we build from share path
|
||||
path = utils.JoinPathAsUnix(d.share.Path, path)
|
||||
}
|
||||
// Handle document closure - clean up document key cache
|
||||
if data.Status == onlyOfficeStatusDocumentClosedWithChanges ||
|
||||
data.Status == onlyOfficeStatusDocumentClosedWithNoChanges {
|
||||
// Refer to only-office documentation
|
||||
// Refer to OnlyOffice documentation:
|
||||
// - https://api.onlyoffice.com/editors/coedit
|
||||
// - https://api.onlyoffice.com/editors/callback
|
||||
//
|
||||
// When the document is fully closed by all editors,
|
||||
// then the document key should no longer be re-used.
|
||||
// the document key should no longer be re-used.
|
||||
logger.Debugf("OnlyOffice: document closed, cleaning up document ID for source=%s, path=%s", source, path)
|
||||
deleteOfficeId(source, path)
|
||||
}
|
||||
|
||||
// Handle document save operations
|
||||
if data.Status == onlyOfficeStatusDocumentClosedWithChanges ||
|
||||
data.Status == onlyOfficeStatusForceSaveWhileDocumentStillOpen {
|
||||
|
||||
// Verify user has modify permissions
|
||||
if !d.user.Permissions.Modify {
|
||||
logger.Warningf("OnlyOffice callback: user %s lacks modify permissions for source=%s, path=%s",
|
||||
d.user.Username, source, path)
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
// Download the updated document from OnlyOffice server
|
||||
doc, err := http.Get(data.URL)
|
||||
if err != nil {
|
||||
logger.Errorf("OnlyOffice callback: failed to download updated document: %v", err)
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer doc.Body.Close()
|
||||
|
||||
// Resolve file path for writing (same logic as in config handler)
|
||||
var resolvedPath string
|
||||
if d.fileInfo.Hash == "" {
|
||||
// Regular user request - need to resolve scope
|
||||
userScope, scopeErr := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if scopeErr != nil {
|
||||
logger.Errorf("OnlyOffice callback: source %s not available for user %s: %v",
|
||||
source, d.user.Username, scopeErr)
|
||||
return http.StatusForbidden, fmt.Errorf("source %s is not available", source)
|
||||
}
|
||||
resolvedPath = utils.JoinPathAsUnix(userScope, path)
|
||||
}
|
||||
|
||||
// Write the updated document
|
||||
fileOpts := iteminfo.FileOptions{
|
||||
Path: path,
|
||||
Path: resolvedPath,
|
||||
Source: source,
|
||||
}
|
||||
writeErr := files.WriteFile(fileOpts, doc.Body)
|
||||
if writeErr != nil {
|
||||
logger.Errorf("OnlyOffice callback: failed to write updated document: %v", writeErr)
|
||||
return http.StatusInternalServerError, writeErr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return success response to OnlyOffice server
|
||||
resp := map[string]int{
|
||||
"error": 0,
|
||||
}
|
||||
@@ -215,29 +363,3 @@ func deleteOfficeId(source, path string) {
|
||||
realpath, _, _ := idx.GetRealPath(path)
|
||||
utils.OnlyOfficeCache.Delete(realpath)
|
||||
}
|
||||
|
||||
func onlyofficeGetTokenHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
// get config from body
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var payload map[string]interface{}
|
||||
// marshall to struct
|
||||
err = json.Unmarshal(body, &payload)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
if settings.Config.Integrations.OnlyOffice.Secret != "" {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(payload))
|
||||
ss, err := token.SignedString([]byte(settings.Config.Integrations.OnlyOffice.Secret))
|
||||
if err != nil {
|
||||
return 500, errors.New("could not generate a new jwt")
|
||||
}
|
||||
return renderJSON(w, r, map[string]string{"token": ss})
|
||||
}
|
||||
return 400, fmt.Errorf("bad request")
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func previewHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
d.fileInfo = fileInfo
|
||||
d.fileInfo = *fileInfo
|
||||
return previewHelperFunc(w, r, d)
|
||||
}
|
||||
|
||||
|
||||
+13
-7
@@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
|
||||
@@ -37,12 +38,19 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
d.share.Mu.Unlock()
|
||||
encodedFiles := r.URL.Query().Get("files")
|
||||
|
||||
// Decode the URL-encoded path
|
||||
f, err := url.QueryUnescape(encodedFiles)
|
||||
// Decode the URL-encoded path - use PathUnescape to preserve + as literal character
|
||||
f, err := url.PathUnescape(encodedFiles)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
}
|
||||
|
||||
// Get the actual source name from the share's source mapping
|
||||
sourceInfo, ok := settings.Config.Server.SourceMap[d.share.Source]
|
||||
if !ok {
|
||||
return http.StatusInternalServerError, fmt.Errorf("source not found for share")
|
||||
}
|
||||
actualSourceName := sourceInfo.Name
|
||||
|
||||
fileList := []string{}
|
||||
for _, file := range strings.Split(f, "||") {
|
||||
// Check if file already contains source prefix (source::path format)
|
||||
@@ -57,12 +65,12 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
} else {
|
||||
// Fallback: treat as plain path
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, file)
|
||||
fileList = append(fileList, d.fileInfo.Source+"::"+filePath)
|
||||
fileList = append(fileList, actualSourceName+"::"+filePath)
|
||||
}
|
||||
} else {
|
||||
// Plain path without source prefix
|
||||
// Plain path without source prefix - use the actual source name from share
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, file)
|
||||
fileList = append(fileList, d.fileInfo.Source+"::"+filePath)
|
||||
fileList = append(fileList, actualSourceName+"::"+filePath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +84,6 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
}
|
||||
|
||||
func publicShareHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
// disable onlyoffice for public share
|
||||
d.fileInfo.OnlyOfficeId = ""
|
||||
return renderJSON(w, r, d.fileInfo)
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ func resourceDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestCon
|
||||
}
|
||||
|
||||
// delete thumbnails
|
||||
preview.DelThumbs(r.Context(), fileInfo)
|
||||
preview.DelThumbs(r.Context(), *fileInfo)
|
||||
|
||||
err = files.DeleteFiles(source, fileInfo.RealPath, filepath.Dir(fileInfo.RealPath))
|
||||
if err != nil {
|
||||
@@ -269,7 +269,7 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
}
|
||||
// On the first chunk, check for conflicts or handle override
|
||||
if offset == 0 {
|
||||
var fileInfo iteminfo.ExtendedFileInfo
|
||||
var fileInfo *iteminfo.ExtendedFileInfo
|
||||
fileInfo, err = files.FileInfoFaster(fileOpts)
|
||||
if err == nil { // File exists
|
||||
if r.URL.Query().Get("override") != "true" {
|
||||
@@ -278,7 +278,7 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
return http.StatusConflict, nil
|
||||
}
|
||||
// If overriding, delete existing thumbnails
|
||||
preview.DelThumbs(r.Context(), fileInfo)
|
||||
preview.DelThumbs(r.Context(), *fileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
return http.StatusConflict, nil
|
||||
}
|
||||
|
||||
preview.DelThumbs(r.Context(), fileInfo)
|
||||
preview.DelThumbs(r.Context(), *fileInfo)
|
||||
}
|
||||
err = files.WriteFile(fileOpts, r.Body)
|
||||
if err != nil {
|
||||
@@ -571,7 +571,7 @@ func patchAction(ctx context.Context, params patchActionParams) error {
|
||||
}
|
||||
|
||||
// delete thumbnails
|
||||
preview.DelThumbs(ctx, fileInfo)
|
||||
preview.DelThumbs(ctx, *fileInfo)
|
||||
return files.MoveResource(params.isSrcDir, params.isDstDir, params.srcIndex, params.dstIndex, params.src, params.dst, store.Share)
|
||||
default:
|
||||
return fmt.Errorf("unsupported action %s: %w", params.action, errors.ErrInvalidRequestParams)
|
||||
|
||||
@@ -64,6 +64,9 @@ func searchHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (i
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
}
|
||||
if len(query) < settings.Config.Server.MinSearchLength {
|
||||
return http.StatusBadRequest, fmt.Errorf("query is too short, minimum length is %d", settings.Config.Server.MinSearchLength)
|
||||
}
|
||||
searchScope := strings.TrimPrefix(unencodedScope, ".")
|
||||
// Retrieve the User-Agent and X-Auth headers from the request
|
||||
sessionId := r.Header.Get("SessionId")
|
||||
|
||||
@@ -2,6 +2,8 @@ package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
|
||||
)
|
||||
|
||||
// settingsGetHandler retrieves the current system settings.
|
||||
@@ -34,3 +36,36 @@ func settingsGetHandler(w http.ResponseWriter, r *http.Request, d *requestContex
|
||||
}
|
||||
return renderJSON(w, r, config)
|
||||
}
|
||||
|
||||
// settingsConfigHandler returns the current system settings as YAML.
|
||||
// @Summary Get system settings as YAML
|
||||
// @Description Returns the current running configuration settings as YAML format with optional comments and filtering.
|
||||
// @Tags Settings
|
||||
// @Accept json
|
||||
// @Produce text/plain
|
||||
// @Param full query boolean false "Show all values (true) or only non-default values (false, default)"
|
||||
// @Param comments query boolean false "Include comments in YAML (true) or plain YAML (false, default)"
|
||||
// @Success 200 {string} string "System settings in YAML format"
|
||||
// @Router /api/settings/config [get]
|
||||
func settingsConfigHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
// Parse query parameters
|
||||
fullParam := r.URL.Query().Get("full")
|
||||
commentsParam := r.URL.Query().Get("comments")
|
||||
|
||||
showFull := fullParam == "true"
|
||||
showComments := commentsParam == "true"
|
||||
|
||||
// Generate YAML using the existing generator function (don't filter deprecated fields in API)
|
||||
yamlOutput, err := settings.GenerateConfigYaml(config, showComments, showFull, false)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
// Set content type and write response
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
if _, err := w.Write([]byte(yamlOutput)); err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
return http.StatusOK, nil
|
||||
}
|
||||
+39
-6
@@ -17,10 +17,19 @@ var templateRenderer *TemplateRenderer
|
||||
|
||||
type TemplateRenderer struct {
|
||||
templates *template.Template
|
||||
devMode bool
|
||||
}
|
||||
|
||||
// Render renders a template document with headers and data
|
||||
func (t *TemplateRenderer) Render(w http.ResponseWriter, name string, data interface{}) error {
|
||||
// If in dev mode, reload templates on every render.
|
||||
if t.devMode {
|
||||
var err error
|
||||
t.templates, err = t.templates.ParseFS(assetFs, "public/index.html")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reloading template: %w", err)
|
||||
}
|
||||
}
|
||||
// Set headers
|
||||
w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
@@ -39,28 +48,37 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
|
||||
}
|
||||
userSelectedTheme := ""
|
||||
if d.user != nil {
|
||||
theme, ok := config.Frontend.Styling.CustomThemes[d.user.CustomTheme]
|
||||
theme, ok := config.Frontend.Styling.CustomThemeOptions[d.user.CustomTheme]
|
||||
if ok {
|
||||
userSelectedTheme = theme.CSS
|
||||
userSelectedTheme = theme.CssRaw
|
||||
}
|
||||
}
|
||||
|
||||
defaultThemeColor := "#455a64"
|
||||
staticURL := config.Server.BaseURL + "static"
|
||||
publicStaticURL := config.Server.BaseURL + "public/static"
|
||||
|
||||
// Use custom favicon if configured and validated, otherwise fall back to default
|
||||
var favicon string
|
||||
if config.Frontend.Favicon != "" {
|
||||
favicon = staticURL + "/favicon"
|
||||
} else {
|
||||
// Default favicon
|
||||
favicon = staticURL + "/img/icons/favicon-256x256.png"
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"title": config.Frontend.Name,
|
||||
"customCSS": config.Frontend.Styling.CustomCSS,
|
||||
"customCSS": config.Frontend.Styling.CustomCSSRaw,
|
||||
"userSelectedTheme": userSelectedTheme,
|
||||
"lightBackground": config.Frontend.Styling.LightBackground,
|
||||
"darkBackground": config.Frontend.Styling.DarkBackground,
|
||||
"staticURL": staticURL,
|
||||
"baseURL": config.Server.BaseURL,
|
||||
"favicon": staticURL + "/img/icons/favicon-256x256.png",
|
||||
"favicon": favicon,
|
||||
"color": defaultThemeColor,
|
||||
"winIcon": staticURL + "/img/icons/mstile-144x144.png",
|
||||
"appIcon": staticURL + "/img/icons/android-chrome-256x256.png",
|
||||
"description": "FileBrowser Quantum is a file manager for the web which can be used to manage files on your server",
|
||||
"description": config.Frontend.Description,
|
||||
}
|
||||
shareProps := map[string]interface{}{
|
||||
"isShare": false,
|
||||
@@ -98,6 +116,9 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
|
||||
shareProps["disableSidebar"] = d.share.DisableSidebar
|
||||
shareProps["isPasswordProtected"] = d.share.PasswordHash != ""
|
||||
shareProps["downloadURL"] = getDownloadURL(r, d.share.Hash)
|
||||
shareProps["enforceDarkLightMode"] = d.share.EnforceDarkLightMode
|
||||
shareProps["enableOnlyOffice"] = d.share.EnableOnlyOffice
|
||||
shareProps["enableOnlyOfficeEditing"] = d.share.EnableOnlyOfficeEditing
|
||||
if d.share.Favicon != "" {
|
||||
if strings.HasPrefix(d.share.Favicon, "http") {
|
||||
data["favicon"] = d.share.Favicon
|
||||
@@ -115,13 +136,19 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
|
||||
|
||||
// base url could be different for routes behind proxy
|
||||
data["staticURL"] = publicStaticURL
|
||||
data["favicon"] = publicStaticURL + "/img/icons/favicon-256x256.png"
|
||||
// Use custom favicon for shares too if configured
|
||||
if config.Frontend.Favicon != "" {
|
||||
data["favicon"] = publicStaticURL + "/favicon"
|
||||
} else {
|
||||
data["favicon"] = publicStaticURL + "/img/icons/favicon-256x256.png"
|
||||
}
|
||||
data["winIcon"] = publicStaticURL + "/img/icons/mstile-144x144.png"
|
||||
data["appIcon"] = publicStaticURL + "/img/icons/android-chrome-256x256.png"
|
||||
}
|
||||
// variables consumed by frontend as json
|
||||
data["globalVars"] = map[string]interface{}{
|
||||
"name": config.Frontend.Name,
|
||||
"minSearchLength": config.Server.MinSearchLength,
|
||||
"disableExternal": config.Frontend.DisableDefaultLinks,
|
||||
"darkMode": settings.Config.UserDefaults.DarkMode,
|
||||
"baseURL": config.Server.BaseURL,
|
||||
@@ -162,6 +189,12 @@ func staticFilesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))
|
||||
w.Header().Set("Content-Security-Policy", `default-src 'self'; style-src 'unsafe-inline';`)
|
||||
|
||||
// Handle custom favicon if configured and requested
|
||||
if r.URL.Path == "favicon" && config.Frontend.Favicon != "" {
|
||||
http.ServeFile(w, r, config.Frontend.Favicon)
|
||||
return
|
||||
}
|
||||
|
||||
adjustedCompressed := r.URL.Path + ".gz"
|
||||
if strings.HasSuffix(r.URL.Path, ".js") {
|
||||
w.Header().Set("Content-Type", "application/javascript; charset=utf-8") // Set the correct MIME type for JavaScript files
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/database/access"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/ffmpeg"
|
||||
)
|
||||
|
||||
type ItemInfo struct {
|
||||
@@ -28,13 +29,14 @@ type FileInfo struct {
|
||||
// extra calculated fields can be added here
|
||||
type ExtendedFileInfo struct {
|
||||
FileInfo
|
||||
Content string `json:"content,omitempty"` // text content of a file, if requested
|
||||
Subtitles []string `json:"subtitles,omitempty"` // subtitles for video files
|
||||
Checksums map[string]string `json:"checksums,omitempty"` // checksums for the file
|
||||
Token string `json:"token,omitempty"` // token for the file -- used for sharing
|
||||
OnlyOfficeId string `json:"onlyOfficeId,omitempty"` // id for onlyoffice files
|
||||
Source string `json:"source"` // associated index source for the file
|
||||
RealPath string `json:"-"`
|
||||
Content string `json:"content,omitempty"` // text content of a file, if requested
|
||||
Subtitles []ffmpeg.SubtitleTrack `json:"subtitles,omitempty"` // subtitles for video files
|
||||
Checksums map[string]string `json:"checksums,omitempty"` // checksums for the file
|
||||
Token string `json:"token,omitempty"` // token for the file -- used for sharing
|
||||
OnlyOfficeId string `json:"onlyOfficeId,omitempty"` // id for onlyoffice files
|
||||
Source string `json:"source,omitempty"` // associated index source for the file
|
||||
Hash string `json:"hash,omitempty"` // hash for the file -- used for sharing
|
||||
RealPath string `json:"-"`
|
||||
}
|
||||
|
||||
// FileOptions are the options when getting a file info.
|
||||
|
||||
@@ -8,29 +8,43 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/backend/ffmpeg"
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
)
|
||||
|
||||
type SubtitleTrack struct {
|
||||
Name string `json:"name"` // filename for external, or descriptive name for embedded
|
||||
Language string `json:"language,omitempty"` // language code
|
||||
Title string `json:"title,omitempty"` // title/description
|
||||
Index *int `json:"index,omitempty"` // stream index for embedded subtitles (nil for external)
|
||||
Codec string `json:"codec,omitempty"` // codec name for embedded subtitles
|
||||
IsFile bool `json:"isFile"` // true for external files, false for embedded
|
||||
}
|
||||
|
||||
type FFProbeOutput struct {
|
||||
Streams []struct {
|
||||
Index int `json:"index"`
|
||||
CodecType string `json:"codec_type"`
|
||||
CodecName string `json:"codec_name"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
Disposition map[string]int `json:"disposition,omitempty"`
|
||||
} `json:"streams"`
|
||||
}
|
||||
|
||||
// detects subtitles for video files.
|
||||
func (i *ExtendedFileInfo) DetectSubtitles(parentInfo *FileInfo) {
|
||||
if !strings.HasPrefix(i.Type, "video") {
|
||||
logger.Debug("subtitles are not supported for this file : " + i.Name)
|
||||
return
|
||||
}
|
||||
ext := filepath.Ext(i.Name)
|
||||
baseWithoutExt := strings.TrimSuffix(i.Name, ext)
|
||||
for _, f := range parentInfo.Files {
|
||||
baseName := strings.TrimSuffix(i.Name, ext)
|
||||
if baseName != baseWithoutExt {
|
||||
continue
|
||||
}
|
||||
// Use unified subtitle detection that finds both embedded and external
|
||||
parentDir := filepath.Dir(i.RealPath)
|
||||
i.Subtitles = ffmpeg.DetectAllSubtitles(i.RealPath, parentDir, i.ModTime)
|
||||
}
|
||||
|
||||
for _, subtitleExt := range SubtitleExts {
|
||||
if strings.HasSuffix(f.Name, subtitleExt) {
|
||||
i.Subtitles = append(i.Subtitles, f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
// LoadSubtitleContent loads the actual content for all detected subtitle tracks
|
||||
func (i *ExtendedFileInfo) LoadSubtitleContent() error {
|
||||
return ffmpeg.LoadAllSubtitleContent(i.RealPath, i.Subtitles, i.ModTime)
|
||||
}
|
||||
|
||||
func (info *FileInfo) SortItems() {
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
package preview
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
"github.com/gtsteffaniak/filebrowser/backend/ffmpeg"
|
||||
)
|
||||
|
||||
// GenerateVideoPreview generates a single preview image from a video using ffmpeg.
|
||||
@@ -21,69 +15,8 @@ func (s *Service) GenerateVideoPreview(videoPath, outputPath string, percentageS
|
||||
return nil, err
|
||||
}
|
||||
defer s.release()
|
||||
// Step 1: Get video duration from the container format
|
||||
probeCmd := exec.Command(
|
||||
s.ffprobePath,
|
||||
"-v", "error",
|
||||
// Use format=duration for better compatibility
|
||||
"-show_entries", "format=duration",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1",
|
||||
videoPath,
|
||||
)
|
||||
|
||||
var probeOut bytes.Buffer
|
||||
probeCmd.Stdout = &probeOut
|
||||
if s.debug {
|
||||
probeCmd.Stderr = os.Stderr
|
||||
}
|
||||
if err := probeCmd.Run(); err != nil {
|
||||
logger.Errorf("ffprobe command failed on file '%v' : %v", videoPath, err)
|
||||
return nil, fmt.Errorf("ffprobe failed: %w", err)
|
||||
}
|
||||
|
||||
durationStr := strings.TrimSpace(probeOut.String())
|
||||
if durationStr == "" || durationStr == "N/A" {
|
||||
logger.Errorf("could not determine video duration for file '%v' using duration info '%v'", videoPath, durationStr)
|
||||
return nil, fmt.Errorf("could not determine video duration")
|
||||
}
|
||||
|
||||
durationFloat, err := strconv.ParseFloat(durationStr, 64)
|
||||
if err != nil {
|
||||
// The original error you saw would be caught here if "N/A" was still the output
|
||||
return nil, fmt.Errorf("invalid duration: %v", err)
|
||||
}
|
||||
|
||||
if durationFloat <= 0 {
|
||||
return nil, fmt.Errorf("video duration must be positive")
|
||||
}
|
||||
|
||||
// The rest of your function remains the same...
|
||||
// Step 2: Get the duration of the video in whole seconds
|
||||
duration := int(durationFloat)
|
||||
|
||||
// Step 3: Calculate seek time based on percentageSeek (percentage value)
|
||||
seekSeconds := duration * percentageSeek / 100
|
||||
|
||||
// Step 4: Convert seekSeconds to string for ffmpeg command
|
||||
seekTime := strconv.Itoa(seekSeconds)
|
||||
// Step 5: Extract frame at seek time
|
||||
cmd := exec.Command(
|
||||
s.ffmpegPath,
|
||||
"-ss", seekTime,
|
||||
"-i", videoPath,
|
||||
"-frames:v", "1",
|
||||
"-q:v", "10",
|
||||
"-y", // overwrite output
|
||||
outputPath,
|
||||
)
|
||||
|
||||
if s.debug {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ffmpeg command failed on file '%v' : %w", videoPath, err)
|
||||
}
|
||||
return os.ReadFile(outputPath)
|
||||
// Create a temporary video service for this operation
|
||||
videoSvc := ffmpeg.NewVideoService(s.ffmpegPath, s.ffprobePath, 1, s.debug)
|
||||
return videoSvc.GenerateVideoPreview(videoPath, outputPath, percentageSeek)
|
||||
}
|
||||
+178
-11
@@ -854,6 +854,87 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/media/subtitles": {
|
||||
"get": {
|
||||
"description": "Extracts embedded subtitle content from video files by stream index and returns raw WebVTT content",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/vtt"
|
||||
],
|
||||
"tags": [
|
||||
"Subtitles"
|
||||
],
|
||||
"summary": "Extract embedded subtitles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Index path to the video file",
|
||||
"name": "path",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Source name for the desired source",
|
||||
"name": "source",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Stream index for embedded subtitle extraction, defaults to 0",
|
||||
"name": "index",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Raw WebVTT subtitle content",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Resource not found",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/preview": {
|
||||
"get": {
|
||||
"description": "Returns a preview image based on the requested path and size.",
|
||||
@@ -1616,6 +1697,43 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/settings/config": {
|
||||
"get": {
|
||||
"description": "Returns the current running configuration settings as YAML format with optional comments and filtering.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"summary": "Get system settings as YAML",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Show all values (true) or only non-default values (false, default)",
|
||||
"name": "full",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Include comments in YAML (true) or plain YAML (false, default)",
|
||||
"name": "comments",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "System settings in YAML format",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/share": {
|
||||
"get": {
|
||||
"description": "Retrieves all share links associated with a specific resource path for the current user.",
|
||||
@@ -2486,26 +2604,26 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"adminPassword": {
|
||||
"description": "the password of the admin user. If not set, the default is \"admin\".",
|
||||
"description": "secret: the password of the admin user. If not set, the default is \"admin\".",
|
||||
"type": "string"
|
||||
},
|
||||
"adminUsername": {
|
||||
"description": "the username of the admin user. If not set, the default is \"admin\".",
|
||||
"description": "secret: the username of the admin user. If not set, the default is \"admin\".",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "the key used to sign the JWT tokens. If not set, a random key will be generated.",
|
||||
"description": "secret: the key used to sign the JWT tokens. If not set, a random key will be generated.",
|
||||
"type": "string"
|
||||
},
|
||||
"methods": {
|
||||
"$ref": "#/definitions/settings.LoginMethods"
|
||||
},
|
||||
"tokenExpirationHours": {
|
||||
"description": "the number of hours until the token expires. Default is 2 hours.",
|
||||
"description": "time in hours each web UI session token is valid for. Default is 2 hours.",
|
||||
"type": "integer"
|
||||
},
|
||||
"totpSecret": {
|
||||
"description": "secret used to encrypt TOTP secrets",
|
||||
"description": "secret: secret used to encrypt TOTP secrets",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2602,6 +2720,10 @@ const docTemplate = `{
|
||||
"settings.Frontend": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "description that shows up in html head meta description",
|
||||
"type": "string"
|
||||
},
|
||||
"disableDefaultLinks": {
|
||||
"description": "disable default links in the sidebar",
|
||||
"type": "boolean"
|
||||
@@ -2620,6 +2742,10 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/settings.ExternalLink"
|
||||
}
|
||||
},
|
||||
"favicon": {
|
||||
"description": "path to a favicon to use for the frontend",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "display name",
|
||||
"type": "string"
|
||||
@@ -2723,11 +2849,11 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"clientId": {
|
||||
"description": "client id of the OIDC application",
|
||||
"description": "secret: client id of the OIDC application",
|
||||
"type": "string"
|
||||
},
|
||||
"clientSecret": {
|
||||
"description": "client secret of the OIDC application",
|
||||
"description": "secret: client secret of the OIDC application",
|
||||
"type": "string"
|
||||
},
|
||||
"createUser": {
|
||||
@@ -2776,6 +2902,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"secret": {
|
||||
"description": "secret: authentication key for OnlyOffice integration",
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
@@ -2885,6 +3012,10 @@ const docTemplate = `{
|
||||
"description": "disable type detection by header, useful if filesystem is slow.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableUpdateCheck": {
|
||||
"description": "disables backend update check service",
|
||||
"type": "boolean"
|
||||
},
|
||||
"externalUrl": {
|
||||
"description": "used by share links if set (eg. http://mydomain.com)",
|
||||
"type": "string"
|
||||
@@ -2903,6 +3034,10 @@ const docTemplate = `{
|
||||
"description": "max pre-archive combined size of files/folder that are allowed to be archived (in GB)",
|
||||
"type": "integer"
|
||||
},
|
||||
"minSearchLength": {
|
||||
"description": "minimum length of search query to begin searching (default: 3)",
|
||||
"type": "integer"
|
||||
},
|
||||
"numImageProcessors": {
|
||||
"description": "number of concurrent image processing jobs used to create previews, default is number of cpu cores available.",
|
||||
"type": "integer"
|
||||
@@ -3073,6 +3208,10 @@ const docTemplate = `{
|
||||
"description": "when false, the date is relative, when true, the date is an exact timestamp",
|
||||
"type": "boolean"
|
||||
},
|
||||
"debugOffice": {
|
||||
"description": "debug onlyoffice editor",
|
||||
"type": "boolean"
|
||||
},
|
||||
"deleteWithoutConfirming": {
|
||||
"description": "delete files without confirmation",
|
||||
"type": "boolean"
|
||||
@@ -3086,7 +3225,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"disablePreviewExt": {
|
||||
"description": "comma separated list of file extensions to disable preview for",
|
||||
"description": "space separated list of file extensions to disable preview for",
|
||||
"type": "string"
|
||||
},
|
||||
"disableQuickToggles": {
|
||||
@@ -3106,7 +3245,7 @@ const docTemplate = `{
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableViewingExt": {
|
||||
"description": "comma separated list of file extensions to disable viewing for",
|
||||
"description": "space separated list of file extensions to disable viewing for",
|
||||
"type": "string"
|
||||
},
|
||||
"editorQuickSave": {
|
||||
@@ -3217,6 +3356,16 @@ const docTemplate = `{
|
||||
"downloadsLimit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enableOnlyOffice": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableOnlyOfficeEditing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enforceDarkLightMode": {
|
||||
"description": "\"dark\" or \"light\"",
|
||||
"type": "string"
|
||||
},
|
||||
"expires": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3308,6 +3457,16 @@ const docTemplate = `{
|
||||
"downloadsLimit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enableOnlyOffice": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableOnlyOfficeEditing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enforceDarkLightMode": {
|
||||
"description": "\"dark\" or \"light\"",
|
||||
"type": "string"
|
||||
},
|
||||
"expire": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -3434,6 +3593,10 @@ const docTemplate = `{
|
||||
"description": "autoplay media files in preview",
|
||||
"type": "boolean"
|
||||
},
|
||||
"defaultMediaPlayer": {
|
||||
"description": "disable html5 media player and use the default media player",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableHideSidebar": {
|
||||
"description": "disable the hide sidebar preview for previews and editors",
|
||||
"type": "boolean"
|
||||
@@ -3507,6 +3670,10 @@ const docTemplate = `{
|
||||
"description": "when false, the date is relative, when true, the date is an exact timestamp",
|
||||
"type": "boolean"
|
||||
},
|
||||
"debugOffice": {
|
||||
"description": "debug onlyoffice editor",
|
||||
"type": "boolean"
|
||||
},
|
||||
"deleteWithoutConfirming": {
|
||||
"description": "delete files without confirmation",
|
||||
"type": "boolean"
|
||||
@@ -3520,7 +3687,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"disablePreviewExt": {
|
||||
"description": "comma separated list of file extensions to disable preview for",
|
||||
"description": "space separated list of file extensions to disable preview for",
|
||||
"type": "string"
|
||||
},
|
||||
"disableQuickToggles": {
|
||||
@@ -3539,7 +3706,7 @@ const docTemplate = `{
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableViewingExt": {
|
||||
"description": "comma separated list of file extensions to disable viewing for",
|
||||
"description": "space separated list of file extensions to disable viewing for",
|
||||
"type": "string"
|
||||
},
|
||||
"editorQuickSave": {
|
||||
|
||||
@@ -843,6 +843,87 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/media/subtitles": {
|
||||
"get": {
|
||||
"description": "Extracts embedded subtitle content from video files by stream index and returns raw WebVTT content",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/vtt"
|
||||
],
|
||||
"tags": [
|
||||
"Subtitles"
|
||||
],
|
||||
"summary": "Extract embedded subtitles",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Index path to the video file",
|
||||
"name": "path",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Source name for the desired source",
|
||||
"name": "source",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Stream index for embedded subtitle extraction, defaults to 0",
|
||||
"name": "index",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Raw WebVTT subtitle content",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Resource not found",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/preview": {
|
||||
"get": {
|
||||
"description": "Returns a preview image based on the requested path and size.",
|
||||
@@ -1605,6 +1686,43 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/settings/config": {
|
||||
"get": {
|
||||
"description": "Returns the current running configuration settings as YAML format with optional comments and filtering.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"Settings"
|
||||
],
|
||||
"summary": "Get system settings as YAML",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Show all values (true) or only non-default values (false, default)",
|
||||
"name": "full",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Include comments in YAML (true) or plain YAML (false, default)",
|
||||
"name": "comments",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "System settings in YAML format",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/share": {
|
||||
"get": {
|
||||
"description": "Retrieves all share links associated with a specific resource path for the current user.",
|
||||
@@ -2475,26 +2593,26 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"adminPassword": {
|
||||
"description": "the password of the admin user. If not set, the default is \"admin\".",
|
||||
"description": "secret: the password of the admin user. If not set, the default is \"admin\".",
|
||||
"type": "string"
|
||||
},
|
||||
"adminUsername": {
|
||||
"description": "the username of the admin user. If not set, the default is \"admin\".",
|
||||
"description": "secret: the username of the admin user. If not set, the default is \"admin\".",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "the key used to sign the JWT tokens. If not set, a random key will be generated.",
|
||||
"description": "secret: the key used to sign the JWT tokens. If not set, a random key will be generated.",
|
||||
"type": "string"
|
||||
},
|
||||
"methods": {
|
||||
"$ref": "#/definitions/settings.LoginMethods"
|
||||
},
|
||||
"tokenExpirationHours": {
|
||||
"description": "the number of hours until the token expires. Default is 2 hours.",
|
||||
"description": "time in hours each web UI session token is valid for. Default is 2 hours.",
|
||||
"type": "integer"
|
||||
},
|
||||
"totpSecret": {
|
||||
"description": "secret used to encrypt TOTP secrets",
|
||||
"description": "secret: secret used to encrypt TOTP secrets",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2591,6 +2709,10 @@
|
||||
"settings.Frontend": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"description": "description that shows up in html head meta description",
|
||||
"type": "string"
|
||||
},
|
||||
"disableDefaultLinks": {
|
||||
"description": "disable default links in the sidebar",
|
||||
"type": "boolean"
|
||||
@@ -2609,6 +2731,10 @@
|
||||
"$ref": "#/definitions/settings.ExternalLink"
|
||||
}
|
||||
},
|
||||
"favicon": {
|
||||
"description": "path to a favicon to use for the frontend",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "display name",
|
||||
"type": "string"
|
||||
@@ -2712,11 +2838,11 @@
|
||||
"type": "string"
|
||||
},
|
||||
"clientId": {
|
||||
"description": "client id of the OIDC application",
|
||||
"description": "secret: client id of the OIDC application",
|
||||
"type": "string"
|
||||
},
|
||||
"clientSecret": {
|
||||
"description": "client secret of the OIDC application",
|
||||
"description": "secret: client secret of the OIDC application",
|
||||
"type": "string"
|
||||
},
|
||||
"createUser": {
|
||||
@@ -2765,6 +2891,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"secret": {
|
||||
"description": "secret: authentication key for OnlyOffice integration",
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
@@ -2874,6 +3001,10 @@
|
||||
"description": "disable type detection by header, useful if filesystem is slow.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableUpdateCheck": {
|
||||
"description": "disables backend update check service",
|
||||
"type": "boolean"
|
||||
},
|
||||
"externalUrl": {
|
||||
"description": "used by share links if set (eg. http://mydomain.com)",
|
||||
"type": "string"
|
||||
@@ -2892,6 +3023,10 @@
|
||||
"description": "max pre-archive combined size of files/folder that are allowed to be archived (in GB)",
|
||||
"type": "integer"
|
||||
},
|
||||
"minSearchLength": {
|
||||
"description": "minimum length of search query to begin searching (default: 3)",
|
||||
"type": "integer"
|
||||
},
|
||||
"numImageProcessors": {
|
||||
"description": "number of concurrent image processing jobs used to create previews, default is number of cpu cores available.",
|
||||
"type": "integer"
|
||||
@@ -3062,6 +3197,10 @@
|
||||
"description": "when false, the date is relative, when true, the date is an exact timestamp",
|
||||
"type": "boolean"
|
||||
},
|
||||
"debugOffice": {
|
||||
"description": "debug onlyoffice editor",
|
||||
"type": "boolean"
|
||||
},
|
||||
"deleteWithoutConfirming": {
|
||||
"description": "delete files without confirmation",
|
||||
"type": "boolean"
|
||||
@@ -3075,7 +3214,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"disablePreviewExt": {
|
||||
"description": "comma separated list of file extensions to disable preview for",
|
||||
"description": "space separated list of file extensions to disable preview for",
|
||||
"type": "string"
|
||||
},
|
||||
"disableQuickToggles": {
|
||||
@@ -3095,7 +3234,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableViewingExt": {
|
||||
"description": "comma separated list of file extensions to disable viewing for",
|
||||
"description": "space separated list of file extensions to disable viewing for",
|
||||
"type": "string"
|
||||
},
|
||||
"editorQuickSave": {
|
||||
@@ -3206,6 +3345,16 @@
|
||||
"downloadsLimit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enableOnlyOffice": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableOnlyOfficeEditing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enforceDarkLightMode": {
|
||||
"description": "\"dark\" or \"light\"",
|
||||
"type": "string"
|
||||
},
|
||||
"expires": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3297,6 +3446,16 @@
|
||||
"downloadsLimit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enableOnlyOffice": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableOnlyOfficeEditing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enforceDarkLightMode": {
|
||||
"description": "\"dark\" or \"light\"",
|
||||
"type": "string"
|
||||
},
|
||||
"expire": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -3423,6 +3582,10 @@
|
||||
"description": "autoplay media files in preview",
|
||||
"type": "boolean"
|
||||
},
|
||||
"defaultMediaPlayer": {
|
||||
"description": "disable html5 media player and use the default media player",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableHideSidebar": {
|
||||
"description": "disable the hide sidebar preview for previews and editors",
|
||||
"type": "boolean"
|
||||
@@ -3496,6 +3659,10 @@
|
||||
"description": "when false, the date is relative, when true, the date is an exact timestamp",
|
||||
"type": "boolean"
|
||||
},
|
||||
"debugOffice": {
|
||||
"description": "debug onlyoffice editor",
|
||||
"type": "boolean"
|
||||
},
|
||||
"deleteWithoutConfirming": {
|
||||
"description": "delete files without confirmation",
|
||||
"type": "boolean"
|
||||
@@ -3509,7 +3676,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"disablePreviewExt": {
|
||||
"description": "comma separated list of file extensions to disable preview for",
|
||||
"description": "space separated list of file extensions to disable preview for",
|
||||
"type": "string"
|
||||
},
|
||||
"disableQuickToggles": {
|
||||
@@ -3528,7 +3695,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableViewingExt": {
|
||||
"description": "comma separated list of file extensions to disable viewing for",
|
||||
"description": "space separated list of file extensions to disable viewing for",
|
||||
"type": "string"
|
||||
},
|
||||
"editorQuickSave": {
|
||||
|
||||
@@ -140,22 +140,25 @@ definitions:
|
||||
settings.Auth:
|
||||
properties:
|
||||
adminPassword:
|
||||
description: the password of the admin user. If not set, the default is "admin".
|
||||
description: 'secret: the password of the admin user. If not set, the default
|
||||
is "admin".'
|
||||
type: string
|
||||
adminUsername:
|
||||
description: the username of the admin user. If not set, the default is "admin".
|
||||
description: 'secret: the username of the admin user. If not set, the default
|
||||
is "admin".'
|
||||
type: string
|
||||
key:
|
||||
description: the key used to sign the JWT tokens. If not set, a random key
|
||||
will be generated.
|
||||
description: 'secret: the key used to sign the JWT tokens. If not set, a random
|
||||
key will be generated.'
|
||||
type: string
|
||||
methods:
|
||||
$ref: '#/definitions/settings.LoginMethods'
|
||||
tokenExpirationHours:
|
||||
description: the number of hours until the token expires. Default is 2 hours.
|
||||
description: time in hours each web UI session token is valid for. Default
|
||||
is 2 hours.
|
||||
type: integer
|
||||
totpSecret:
|
||||
description: secret used to encrypt TOTP secrets
|
||||
description: 'secret: secret used to encrypt TOTP secrets'
|
||||
type: string
|
||||
type: object
|
||||
settings.CustomTheme:
|
||||
@@ -229,6 +232,9 @@ definitions:
|
||||
type: object
|
||||
settings.Frontend:
|
||||
properties:
|
||||
description:
|
||||
description: description that shows up in html head meta description
|
||||
type: string
|
||||
disableDefaultLinks:
|
||||
description: disable default links in the sidebar
|
||||
type: boolean
|
||||
@@ -242,6 +248,9 @@ definitions:
|
||||
items:
|
||||
$ref: '#/definitions/settings.ExternalLink'
|
||||
type: array
|
||||
favicon:
|
||||
description: path to a favicon to use for the frontend
|
||||
type: string
|
||||
name:
|
||||
description: display name
|
||||
type: string
|
||||
@@ -316,10 +325,10 @@ definitions:
|
||||
description: if set, users in this group will be granted admin privileges.
|
||||
type: string
|
||||
clientId:
|
||||
description: client id of the OIDC application
|
||||
description: 'secret: client id of the OIDC application'
|
||||
type: string
|
||||
clientSecret:
|
||||
description: client secret of the OIDC application
|
||||
description: 'secret: client secret of the OIDC application'
|
||||
type: string
|
||||
createUser:
|
||||
description: create user if not exists
|
||||
@@ -357,6 +366,7 @@ definitions:
|
||||
to bypass proxy.
|
||||
type: string
|
||||
secret:
|
||||
description: 'secret: authentication key for OnlyOffice integration'
|
||||
type: string
|
||||
url:
|
||||
description: The URL to the OnlyOffice Document Server, needs to be accessible
|
||||
@@ -440,6 +450,9 @@ definitions:
|
||||
disableTypeDetectionByHeader:
|
||||
description: disable type detection by header, useful if filesystem is slow.
|
||||
type: boolean
|
||||
disableUpdateCheck:
|
||||
description: disables backend update check service
|
||||
type: boolean
|
||||
externalUrl:
|
||||
description: used by share links if set (eg. http://mydomain.com)
|
||||
type: string
|
||||
@@ -455,6 +468,10 @@ definitions:
|
||||
description: max pre-archive combined size of files/folder that are allowed
|
||||
to be archived (in GB)
|
||||
type: integer
|
||||
minSearchLength:
|
||||
description: 'minimum length of search query to begin searching (default:
|
||||
3)'
|
||||
type: integer
|
||||
numImageProcessors:
|
||||
description: number of concurrent image processing jobs used to create previews,
|
||||
default is number of cpu cores available.
|
||||
@@ -585,6 +602,9 @@ definitions:
|
||||
description: when false, the date is relative, when true, the date is an exact
|
||||
timestamp
|
||||
type: boolean
|
||||
debugOffice:
|
||||
description: debug onlyoffice editor
|
||||
type: boolean
|
||||
deleteWithoutConfirming:
|
||||
description: delete files without confirmation
|
||||
type: boolean
|
||||
@@ -595,7 +615,7 @@ definitions:
|
||||
description: list of file extensions to disable onlyoffice editor for
|
||||
type: string
|
||||
disablePreviewExt:
|
||||
description: comma separated list of file extensions to disable preview for
|
||||
description: space separated list of file extensions to disable preview for
|
||||
type: string
|
||||
disableQuickToggles:
|
||||
description: disable the quick toggles in the sidebar
|
||||
@@ -610,7 +630,7 @@ definitions:
|
||||
description: disable update notifications banner for admin users
|
||||
type: boolean
|
||||
disableViewingExt:
|
||||
description: comma separated list of file extensions to disable viewing for
|
||||
description: space separated list of file extensions to disable viewing for
|
||||
type: string
|
||||
editorQuickSave:
|
||||
description: show quick save button in editor
|
||||
@@ -690,6 +710,13 @@ definitions:
|
||||
type: string
|
||||
downloadsLimit:
|
||||
type: integer
|
||||
enableOnlyOffice:
|
||||
type: boolean
|
||||
enableOnlyOfficeEditing:
|
||||
type: boolean
|
||||
enforceDarkLightMode:
|
||||
description: '"dark" or "light"'
|
||||
type: string
|
||||
expires:
|
||||
type: string
|
||||
favicon:
|
||||
@@ -751,6 +778,13 @@ definitions:
|
||||
type: integer
|
||||
downloadsLimit:
|
||||
type: integer
|
||||
enableOnlyOffice:
|
||||
type: boolean
|
||||
enableOnlyOfficeEditing:
|
||||
type: boolean
|
||||
enforceDarkLightMode:
|
||||
description: '"dark" or "light"'
|
||||
type: string
|
||||
expire:
|
||||
type: integer
|
||||
favicon:
|
||||
@@ -839,6 +873,9 @@ definitions:
|
||||
autoplayMedia:
|
||||
description: autoplay media files in preview
|
||||
type: boolean
|
||||
defaultMediaPlayer:
|
||||
description: disable html5 media player and use the default media player
|
||||
type: boolean
|
||||
disableHideSidebar:
|
||||
description: disable the hide sidebar preview for previews and editors
|
||||
type: boolean
|
||||
@@ -891,6 +928,9 @@ definitions:
|
||||
description: when false, the date is relative, when true, the date is an exact
|
||||
timestamp
|
||||
type: boolean
|
||||
debugOffice:
|
||||
description: debug onlyoffice editor
|
||||
type: boolean
|
||||
deleteWithoutConfirming:
|
||||
description: delete files without confirmation
|
||||
type: boolean
|
||||
@@ -901,7 +941,7 @@ definitions:
|
||||
description: deprecated
|
||||
type: string
|
||||
disablePreviewExt:
|
||||
description: comma separated list of file extensions to disable preview for
|
||||
description: space separated list of file extensions to disable preview for
|
||||
type: string
|
||||
disableQuickToggles:
|
||||
description: disable the quick toggles in the sidebar
|
||||
@@ -915,7 +955,7 @@ definitions:
|
||||
description: disable update notifications
|
||||
type: boolean
|
||||
disableViewingExt:
|
||||
description: comma separated list of file extensions to disable viewing for
|
||||
description: space separated list of file extensions to disable viewing for
|
||||
type: string
|
||||
editorQuickSave:
|
||||
description: show quick save button in editor
|
||||
@@ -1554,6 +1594,61 @@ paths:
|
||||
summary: User login
|
||||
tags:
|
||||
- Auth
|
||||
/api/media/subtitles:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Extracts embedded subtitle content from video files by stream index
|
||||
and returns raw WebVTT content
|
||||
parameters:
|
||||
- description: Index path to the video file
|
||||
in: query
|
||||
name: path
|
||||
required: true
|
||||
type: string
|
||||
- description: Source name for the desired source
|
||||
in: query
|
||||
name: source
|
||||
required: true
|
||||
type: string
|
||||
- description: Stream index for embedded subtitle extraction, defaults to 0
|
||||
in: query
|
||||
name: index
|
||||
type: integer
|
||||
produces:
|
||||
- text/vtt
|
||||
responses:
|
||||
"200":
|
||||
description: Raw WebVTT subtitle content
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"403":
|
||||
description: Forbidden
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"404":
|
||||
description: Resource not found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: Extract embedded subtitles
|
||||
tags:
|
||||
- Subtitles
|
||||
/api/preview:
|
||||
get:
|
||||
consumes:
|
||||
@@ -2077,6 +2172,31 @@ paths:
|
||||
summary: Get system settings
|
||||
tags:
|
||||
- Settings
|
||||
/api/settings/config:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Returns the current running configuration settings as YAML format
|
||||
with optional comments and filtering.
|
||||
parameters:
|
||||
- description: Show all values (true) or only non-default values (false, default)
|
||||
in: query
|
||||
name: full
|
||||
type: boolean
|
||||
- description: Include comments in YAML (true) or plain YAML (false, default)
|
||||
in: query
|
||||
name: comments
|
||||
type: boolean
|
||||
produces:
|
||||
- text/plain
|
||||
responses:
|
||||
"200":
|
||||
description: System settings in YAML format
|
||||
schema:
|
||||
type: string
|
||||
summary: Get system settings as YAML
|
||||
tags:
|
||||
- Settings
|
||||
/api/share:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
@@ -1,740 +0,0 @@
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index 92478a24..5085e058 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -11,17 +11,16 @@ All notable changes to this project will be documented in this file. For commit
|
||||
|
||||
**New Features**:
|
||||
- New access control system that works like shares. You can add new allow / deny rules for users/groups for specific paths on specific sources.
|
||||
- #- The concept of 'groups', either automatically from OIDC groups claim or defined https://github.com/gtsteffaniak/filebrowser/issues/545
|
||||
+ - The concept of 'groups', either automatically from OIDC groups claim or defined https://github.com/gtsteffaniak/filebrowser/issues/545
|
||||
- share view changes -- now aligns with the standard listing view. This means files can be viewed and edited (if permission allows) just like a normal listing.
|
||||
- many share links customization enhancements
|
||||
- #- only share to certain authenticated users https://github.com/gtsteffaniak/filebrowser/issues/656 https://github.com/gtsteffaniak/filebrowser/issues/985
|
||||
- #- one-time download links
|
||||
- #- customize share theme, banner image, and icons https://github.com/gtsteffaniak/filebrowser/issues/827 https://github.com/gtsteffaniak/filebrowser/issues/1029
|
||||
- #- share link info customization https://github.com/gtsteffaniak/filebrowser/issues/792 https://github.com/gtsteffaniak/filebrowser/issues/841 https://github.com/gtsteffaniak/filebrowser/issues/520
|
||||
- #- "shared with me" and "share history" https://github.com/gtsteffaniak/filebrowser/issues/943
|
||||
+ - only share to certain authenticated users https://github.com/gtsteffaniak/filebrowser/issues/656 https://github.com/gtsteffaniak/filebrowser/issues/985
|
||||
+ - one-time download links
|
||||
+ - customize share theme, banner image, and icons https://github.com/gtsteffaniak/filebrowser/issues/827 https://github.com/gtsteffaniak/filebrowser/issues/1029
|
||||
+ - share link info customization https://github.com/gtsteffaniak/filebrowser/issues/792 https://github.com/gtsteffaniak/filebrowser/issues/841 https://github.com/gtsteffaniak/filebrowser/issues/520
|
||||
+ - "shared with me" and "share history" https://github.com/gtsteffaniak/filebrowser/issues/943
|
||||
- upload to share links https://github.com/gtsteffaniak/filebrowser/issues/661
|
||||
- share link public changes https://github.com/gtsteffaniak/filebrowser/issues/473
|
||||
- -
|
||||
- public/private folder logic https://github.com/gtsteffaniak/filebrowser/issues/505
|
||||
|
||||
**BugFixes**:
|
||||
diff --git a/backend/http/middleware.go b/backend/http/middleware.go
|
||||
index 5ba397fe..595f00e1 100644
|
||||
--- a/backend/http/middleware.go
|
||||
+++ b/backend/http/middleware.go
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
- "net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -44,14 +43,8 @@ type handleFunc func(w http.ResponseWriter, r *http.Request, data *requestContex
|
||||
// Middleware to handle file requests by hash and pass it to the handler
|
||||
func withHashFileHelper(fn handleFunc) handleFunc {
|
||||
return withOrWithoutUserHelper(func(w http.ResponseWriter, r *http.Request, data *requestContext) (int, error) {
|
||||
+ path := r.URL.Query().Get("path")
|
||||
hash := r.URL.Query().Get("hash")
|
||||
- encodedPath := r.URL.Query().Get("path")
|
||||
- // Decode the URL-encoded path
|
||||
- path, err := url.QueryUnescape(encodedPath)
|
||||
- if err != nil {
|
||||
- return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
- }
|
||||
- data.path = path
|
||||
// Get the file link by hash
|
||||
link, err := store.Share.GetByHash(hash)
|
||||
if err != nil {
|
||||
@@ -71,6 +64,11 @@ func withHashFileHelper(fn handleFunc) handleFunc {
|
||||
return status, fmt.Errorf("could not authenticate share request")
|
||||
}
|
||||
}
|
||||
+ data.path = strings.TrimSuffix(link.Path, "/") + "/" + strings.TrimPrefix(path, "/")
|
||||
+ if path == "" || path == "/" {
|
||||
+ data.path = link.Path
|
||||
+ }
|
||||
+
|
||||
source, ok := config.Server.SourceMap[link.Source]
|
||||
if !ok {
|
||||
return http.StatusNotFound, fmt.Errorf("source not found")
|
||||
diff --git a/backend/http/middleware_test.go b/backend/http/middleware_test.go
|
||||
index ff7c173e..eb257951 100644
|
||||
--- a/backend/http/middleware_test.go
|
||||
+++ b/backend/http/middleware_test.go
|
||||
@@ -155,20 +155,6 @@ func TestPublicShareHandlerAuthentication(t *testing.T) {
|
||||
},
|
||||
expectedStatusCode: 0, // zero means 200 on helpers
|
||||
},
|
||||
- {
|
||||
- name: "Private share, valid password when token exists",
|
||||
- share: &share.Link{
|
||||
- Hash: "pw_and_token_hash",
|
||||
- UserID: 1,
|
||||
- PasswordHash: passwordBcrypt,
|
||||
- Token: "some_random_token",
|
||||
- Source: "/srv",
|
||||
- },
|
||||
- extraHeaders: map[string]string{
|
||||
- "X-SHARE-PASSWORD": "password",
|
||||
- },
|
||||
- expectedStatusCode: 0, // zero means 200 on helpers
|
||||
- },
|
||||
{
|
||||
name: "Private share, no auth provided",
|
||||
share: &share.Link{
|
||||
diff --git a/backend/http/public.go b/backend/http/public.go
|
||||
index ef65dfa6..0e641fbd 100644
|
||||
--- a/backend/http/public.go
|
||||
+++ b/backend/http/public.go
|
||||
@@ -7,6 +7,9 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
+ "github.com/gtsteffaniak/filebrowser/backend/adapters/fs/files"
|
||||
+ "github.com/gtsteffaniak/filebrowser/backend/common/utils"
|
||||
+ "github.com/gtsteffaniak/filebrowser/backend/indexing/iteminfo"
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
|
||||
_ "github.com/gtsteffaniak/filebrowser/backend/swagger/docs"
|
||||
@@ -49,7 +52,7 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
}
|
||||
|
||||
func publicShareHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
|
||||
- d.fileInfo.Path = "/" + strings.TrimPrefix(d.fileInfo.Path, d.share.Path)
|
||||
+ d.fileInfo.Path = strings.TrimPrefix(d.fileInfo.Path, d.share.Path)
|
||||
return renderJSON(w, r, d.fileInfo)
|
||||
}
|
||||
|
||||
@@ -91,5 +94,24 @@ func publicPreviewHandler(w http.ResponseWriter, r *http.Request, d *requestCont
|
||||
if config.Server.DisablePreviews {
|
||||
return http.StatusNotImplemented, fmt.Errorf("preview is disabled")
|
||||
}
|
||||
+ path := r.URL.Query().Get("path")
|
||||
+ var err error
|
||||
+ if path == "" {
|
||||
+ return http.StatusBadRequest, fmt.Errorf("invalid request path")
|
||||
+ }
|
||||
+ fileInfo, err := files.FileInfoFaster(iteminfo.FileOptions{
|
||||
+ Path: utils.JoinPathAsUnix(d.share.Path, path),
|
||||
+ Modify: false, // TODO make this configurable
|
||||
+ Source: d.fileInfo.Source,
|
||||
+ Expand: true,
|
||||
+ })
|
||||
+ if err != nil {
|
||||
+ logger.Debugf("public preview handler: error getting file info: %v", err)
|
||||
+ return 400, fmt.Errorf("file not found")
|
||||
+ }
|
||||
+ if fileInfo.Type == "directory" {
|
||||
+ return http.StatusBadRequest, fmt.Errorf("can't create preview for directory")
|
||||
+ }
|
||||
+ d.fileInfo = fileInfo
|
||||
return previewHelperFunc(w, r, d)
|
||||
}
|
||||
diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js
|
||||
index 9399a292..2ef5f38e 100644
|
||||
--- a/frontend/src/api/index.js
|
||||
+++ b/frontend/src/api/index.js
|
||||
@@ -7,3 +7,6 @@ import search from "./search";
|
||||
|
||||
// Note: shareApi has been consolidated into publicApi
|
||||
export { filesApi, publicApi, usersApi, settingsApi, search, accessApi };
|
||||
+
|
||||
+// For backward compatibility, export publicApi as shareApi as well
|
||||
+export { publicApi as shareApi };
|
||||
diff --git a/frontend/src/api/public.js b/frontend/src/api/public.js
|
||||
index 1dcb4125..d94713c5 100644
|
||||
--- a/frontend/src/api/public.js
|
||||
+++ b/frontend/src/api/public.js
|
||||
@@ -2,7 +2,6 @@ import { fetchURL, fetchJSON, adjustedData } from "./utils";
|
||||
import { notify } from "@/notify";
|
||||
import { getApiPath, removePrefix } from "@/utils/url.js";
|
||||
import { externalUrl, baseURL } from "@/utils/constants";
|
||||
-import { state } from "@/store";
|
||||
|
||||
// ============================================================================
|
||||
// PUBLIC API ENDPOINTS (hash-based authentication)
|
||||
@@ -13,8 +12,7 @@ export async function fetchPub(path, hash, password = "", content = false) {
|
||||
const params = {
|
||||
path,
|
||||
hash,
|
||||
- ...(content && { content: 'true' }),
|
||||
- ...(state.share.token && { token: state.share.token })
|
||||
+ ...(content && { content: 'true' })
|
||||
}
|
||||
const apiPath = getApiPath("public/api/shared", params);
|
||||
const response = await fetch(apiPath, {
|
||||
@@ -24,17 +22,7 @@ export async function fetchPub(path, hash, password = "", content = false) {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
- const error = new Error(response.statusText);
|
||||
- // attempt to marshal json response
|
||||
- let data = null;
|
||||
- try {
|
||||
- data = await response.json()
|
||||
- } catch (e) {
|
||||
- // ignore
|
||||
- }
|
||||
- if (data) {
|
||||
- error.message = data.message;
|
||||
- }
|
||||
+ const error = new Error("Failed to connect to the server.");
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
}
|
||||
@@ -57,14 +45,13 @@ export function getDownloadURL(share, files) {
|
||||
}
|
||||
|
||||
// Generate a preview URL for public shares
|
||||
-export function getPreviewURL(path) {
|
||||
+export function getPreviewURL(hash, path) {
|
||||
try {
|
||||
const params = {
|
||||
path: encodeURIComponent(path),
|
||||
size: 'small',
|
||||
- hash: state.share.hash,
|
||||
- inline: 'true',
|
||||
- ...(state.share.token && { token: state.share.token })
|
||||
+ hash: hash,
|
||||
+ inline: 'true'
|
||||
}
|
||||
const apiPath = getApiPath('public/api/preview', params)
|
||||
return window.origin + apiPath
|
||||
diff --git a/frontend/src/api/share.js b/frontend/src/api/share.js
|
||||
new file mode 100644
|
||||
index 00000000..729b7599
|
||||
--- /dev/null
|
||||
+++ b/frontend/src/api/share.js
|
||||
@@ -0,0 +1,66 @@
|
||||
+import { fetchURL, fetchJSON, adjustedData } from "./utils";
|
||||
+import { notify } from "@/notify";
|
||||
+import { getApiPath } from "@/utils/url.js";
|
||||
+import { externalUrl } from "@/utils/constants";
|
||||
+
|
||||
+export async function list() {
|
||||
+ const apiPath = getApiPath("api/shares");
|
||||
+ return fetchJSON(apiPath);
|
||||
+}
|
||||
+
|
||||
+export async function get(path, source) {
|
||||
+ try {
|
||||
+ const params = { path: encodeURIComponent(path), source: encodeURIComponent(source) };
|
||||
+ const apiPath = getApiPath("api/share",params);
|
||||
+ let data = fetchJSON(apiPath);
|
||||
+ return adjustedData(data, path);
|
||||
+ } catch (err) {
|
||||
+ notify.showError(err.message || "Error fetching data");
|
||||
+ throw err;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+export async function remove(hash) {
|
||||
+ const params = { hash };
|
||||
+ const apiPath = getApiPath("api/share",params);
|
||||
+ await fetchURL(apiPath, {
|
||||
+ method: "DELETE",
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+export async function create(path, source, password = "", expires = "", unit = "hours") {
|
||||
+ const params = { path: encodeURIComponent(path), source: encodeURIComponent(source) };
|
||||
+ const apiPath = getApiPath("api/share",params);
|
||||
+ let body = "{}";
|
||||
+ if (password != "" || expires !== "" || unit !== "hours") {
|
||||
+ body = JSON.stringify({ password: password, expires: expires, unit: unit });
|
||||
+ }
|
||||
+ return fetchJSON(apiPath, {
|
||||
+ method: "POST",
|
||||
+ body: body,
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+export function getShareURL(share) {
|
||||
+ if (externalUrl) {
|
||||
+ const apiPath = getApiPath(`share/${share.hash}`)
|
||||
+ return externalUrl + apiPath
|
||||
+ }
|
||||
+ return window.origin+getApiPath(`share/${share.hash}`);
|
||||
+}
|
||||
+
|
||||
+export function getPreviewURL(hash, path) {
|
||||
+ try {
|
||||
+ const params = {
|
||||
+ path: encodeURIComponent(path),
|
||||
+ size: 'small',
|
||||
+ hash: hash,
|
||||
+ inline: 'true'
|
||||
+ }
|
||||
+ const apiPath = getApiPath('api/public/preview', params)
|
||||
+ return window.origin + apiPath
|
||||
+ } catch (err) {
|
||||
+ notify.showError(err.message || 'Error getting preview URL')
|
||||
+ throw err
|
||||
+ }
|
||||
+}
|
||||
diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue
|
||||
index 29c2749f..93848c1c 100644
|
||||
--- a/frontend/src/components/files/ListingItem.vue
|
||||
+++ b/frontend/src/components/files/ListingItem.vue
|
||||
@@ -67,7 +67,7 @@ import { enableThumbs } from "@/utils/constants";
|
||||
import downloadFiles from "@/utils/download";
|
||||
|
||||
import { getHumanReadableFilesize } from "@/utils/filesizes";
|
||||
-import { filesApi,publicApi } from "@/api";
|
||||
+import { filesApi, shareApi } from "@/api";
|
||||
import * as upload from "@/utils/upload";
|
||||
import { state, getters, mutations } from "@/store"; // Import your custom store
|
||||
import { url } from "@/utils";
|
||||
@@ -162,8 +162,8 @@ export default {
|
||||
// @ts-ignore
|
||||
let path = url.removeTrailingSlash(state.req.path) + "/" + this.name;
|
||||
if (getters.isShare()) {
|
||||
- let urlPath = getters.getSharePath(this.name) ;
|
||||
- return publicApi.getPreviewURL(urlPath);
|
||||
+ let urlPath = getters.getSharePath() + "/" + this.name;
|
||||
+ return shareApi.getPreviewURL(state.share.hash, urlPath, state.req.modified);
|
||||
}
|
||||
// @ts-ignore
|
||||
return filesApi.getPreviewURL(state.req.source, path, this.modified);
|
||||
@@ -226,7 +226,7 @@ export default {
|
||||
},
|
||||
getUrl() {
|
||||
if (this.hash) {
|
||||
- return baseURL + "public/share/" + this.hash + "/" + url.encodedPath(this.path);
|
||||
+ return baseURL + "share/" + this.hash + url.encodedPath(this.path);
|
||||
}
|
||||
if (serverHasMultipleSources) {
|
||||
return baseURL + "files/" + encodeURIComponent(this.source) + url.encodedPath(this.path);
|
||||
diff --git a/frontend/src/components/prompts/ActionApi.vue b/frontend/src/components/prompts/ActionApi.vue
|
||||
index f2ebaa3f..1a381565 100644
|
||||
--- a/frontend/src/components/prompts/ActionApi.vue
|
||||
+++ b/frontend/src/components/prompts/ActionApi.vue
|
||||
@@ -90,3 +90,20 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
+
|
||||
+<style>
|
||||
+/* Basic styling for the prompt */
|
||||
+.card.floating {
|
||||
+ padding: 20px;
|
||||
+}
|
||||
+.card-content {
|
||||
+ margin-bottom: 16px;
|
||||
+}
|
||||
+.card-action {
|
||||
+ display: flex;
|
||||
+ justify-content: flex-end;
|
||||
+}
|
||||
+.card-action .button {
|
||||
+ margin-left: 8px;
|
||||
+}
|
||||
+</style>
|
||||
diff --git a/frontend/src/components/prompts/Share.vue b/frontend/src/components/prompts/Share.vue
|
||||
index be1ad5ad..8689100d 100644
|
||||
--- a/frontend/src/components/prompts/Share.vue
|
||||
+++ b/frontend/src/components/prompts/Share.vue
|
||||
@@ -2,11 +2,10 @@
|
||||
<div class="card-title">
|
||||
<h2>{{ $t("buttons.share") }}</h2>
|
||||
</div>
|
||||
- <div class="card-content">
|
||||
- <div aria-label="share-path" class="searchContext"> {{ $t('search.path') }} {{ subpath }}</div>
|
||||
- <p> {{ $t('share.notice') }} </p>
|
||||
-
|
||||
- <div v-if="listing">
|
||||
+ <div aria-label="share-path" class="searchContext"> {{ $t('search.path') }} {{ subpath }}</div>
|
||||
+ <p> {{ $t('share.notice') }} </p>
|
||||
+ <template v-if="listing">
|
||||
+ <div class="card-content">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -46,7 +45,20 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
- <div v-else>
|
||||
+ <div class="card-action">
|
||||
+ <button class="button button--flat button--grey" @click="closeHovers" :aria-label="$t('buttons.close')"
|
||||
+ :title="$t('buttons.close')">
|
||||
+ {{ $t("buttons.close") }}
|
||||
+ </button>
|
||||
+ <button class="button button--flat button--blue" @click="() => switchListing()" :aria-label="$t('buttons.new')"
|
||||
+ :title="$t('buttons.new')">
|
||||
+ {{ $t("buttons.new") }}
|
||||
+ </button>
|
||||
+ </div>
|
||||
+ </template>
|
||||
+
|
||||
+ <template v-else>
|
||||
+ <div class="card-content">
|
||||
<p>{{ $t("settings.shareDuration") }}</p>
|
||||
<div class="form-flex-group">
|
||||
<input class="form-grow input flat-right" v-focus type="number" max="2147483647" min="1" @keyup.enter="submit" v-model.trim="time" />
|
||||
@@ -58,7 +70,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<p>{{ $t("prompts.optionalPassword") }}</p>
|
||||
- <input class="input" type="password" autocomplete="off" v-model.trim="password" />
|
||||
+ <input class="input" type="password" v-model.trim="password" />
|
||||
|
||||
<div class="settings-items">
|
||||
<ToggleSwitch class="item" v-model="readOnly" :name="'Read-only access'" />
|
||||
@@ -80,32 +92,23 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
- </div>
|
||||
|
||||
- <div class="card-action">
|
||||
- <button v-if="listing" class="button button--flat button--grey" @click="closeHovers" :aria-label="$t('buttons.close')"
|
||||
- :title="$t('buttons.close')">
|
||||
- {{ $t("buttons.close") }}
|
||||
- </button>
|
||||
- <button v-if="listing" class="button button--flat button--blue" @click="() => switchListing()" :aria-label="$t('buttons.new')"
|
||||
- :title="$t('buttons.new')">
|
||||
- {{ $t("buttons.new") }}
|
||||
- </button>
|
||||
-
|
||||
- <button v-if="!listing" class="button button--flat button--grey" @click="() => switchListing()" :aria-label="$t('buttons.cancel')"
|
||||
- :title="$t('buttons.cancel')">
|
||||
- {{ $t("buttons.cancel") }}
|
||||
- </button>
|
||||
- <button v-if="!listing" class="button button--flat button--blue" @click="submit" aria-label="Share-Confirm"
|
||||
- :title="$t('buttons.share')">
|
||||
- {{ $t("buttons.share") }}
|
||||
- </button>
|
||||
- </div>
|
||||
+ <div class="card-action">
|
||||
+ <button class="button button--flat button--grey" @click="() => switchListing()" :aria-label="$t('buttons.cancel')"
|
||||
+ :title="$t('buttons.cancel')">
|
||||
+ {{ $t("buttons.cancel") }}
|
||||
+ </button>
|
||||
+ <button class="button button--flat button--blue" @click="submit" aria-label="Share-Confirm"
|
||||
+ :title="$t('buttons.share')">
|
||||
+ {{ $t("buttons.share") }}
|
||||
+ </button>
|
||||
+ </div>
|
||||
+ </template>
|
||||
</template>
|
||||
<script>
|
||||
import { notify } from "@/notify";
|
||||
import { state, getters, mutations } from "@/store";
|
||||
-import { publicApi } from "@/api";
|
||||
+import { shareApi, publicApi } from "@/api";
|
||||
import Clipboard from "clipboard";
|
||||
import { fromNow } from "@/utils/moment";
|
||||
import { buildItemUrl } from "@/utils/url";
|
||||
@@ -188,7 +191,7 @@ export default {
|
||||
|
||||
try {
|
||||
// get last element of the path
|
||||
- const links = await publicApi.get(this.subpath, this.source);
|
||||
+ const links = await shareApi.get(this.subpath, this.source);
|
||||
this.links = links;
|
||||
} catch (err) {
|
||||
notify.showError(err);
|
||||
@@ -215,9 +218,9 @@ export default {
|
||||
let isPermanent = !this.time || this.time == 0;
|
||||
let res = null;
|
||||
if (isPermanent) {
|
||||
- res = await publicApi.create(this.subpath, this.source, this.password);
|
||||
+ res = await shareApi.create(this.subpath, this.source, this.password);
|
||||
} else {
|
||||
- res = await publicApi.create(
|
||||
+ res = await shareApi.create(
|
||||
this.subpath,
|
||||
this.source,
|
||||
this.password,
|
||||
@@ -241,7 +244,7 @@ export default {
|
||||
async deleteLink(event, link) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
- await publicApi.remove(link.hash);
|
||||
+ await shareApi.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
if (this.links.length === 0) {
|
||||
this.listing = false;
|
||||
@@ -254,7 +257,7 @@ export default {
|
||||
return fromNow(time, state.user.locale)
|
||||
},
|
||||
buildLink(share) {
|
||||
- return publicApi.getShareURL(share);
|
||||
+ return shareApi.getShareURL(share);
|
||||
},
|
||||
hasDownloadLink() {
|
||||
if (state.isSearchActive) {
|
||||
diff --git a/frontend/src/components/sidebar/General.vue b/frontend/src/components/sidebar/General.vue
|
||||
index 58f02c10..0e030aeb 100644
|
||||
--- a/frontend/src/components/sidebar/General.vue
|
||||
+++ b/frontend/src/components/sidebar/General.vue
|
||||
@@ -159,7 +159,7 @@ export default {
|
||||
isStickySidebar: () => getters.isStickySidebar(),
|
||||
isMobile: () => getters.isMobile(),
|
||||
isListingView: () => getters.currentView() == "listingView",
|
||||
- user: () => (state.user),
|
||||
+ user: () => (console.log(state.user), state.user),
|
||||
isDarkMode: () => getters.isDarkMode(),
|
||||
showSources: () => !getters.isShare(),
|
||||
currentPrompt: () => getters.currentPrompt(),
|
||||
diff --git a/frontend/src/css/dashboard.css b/frontend/src/css/dashboard.css
|
||||
index 2e1fbc8f..77da5938 100644
|
||||
--- a/frontend/src/css/dashboard.css
|
||||
+++ b/frontend/src/css/dashboard.css
|
||||
@@ -226,10 +226,6 @@ body.rtl .card-title>*:first-child {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
-.card-content h3 {
|
||||
- text-align: center;
|
||||
-}
|
||||
-
|
||||
.card h2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
diff --git a/frontend/src/store/getters.js b/frontend/src/store/getters.js
|
||||
index cb062702..d97e7eed 100644
|
||||
--- a/frontend/src/store/getters.js
|
||||
+++ b/frontend/src/store/getters.js
|
||||
@@ -201,9 +201,10 @@ export const getters = {
|
||||
sharePathBase: () => {
|
||||
return '/public/share/' + getters.shareHash() + '/'
|
||||
},
|
||||
- getSharePath: (subPath = "") => {
|
||||
+ getSharePath: () => {
|
||||
let urlPath = getters.routePath('public/share')
|
||||
- return "/" + removeLeadingSlash(urlPath.split(state.share.hash)[1]) + "/" + subPath
|
||||
+ let parts = urlPath.split('/')
|
||||
+ return "/" + removeLeadingSlash(parts.slice(2).join('/'))
|
||||
},
|
||||
currentView: () => {
|
||||
let listingView = ''
|
||||
@@ -288,6 +289,7 @@ export const getters = {
|
||||
},
|
||||
|
||||
currentPromptName: () => {
|
||||
+ console.log("currentPromptName", state.prompts);
|
||||
// Ensure state.prompts is an array
|
||||
if (!Array.isArray(state.prompts) || state.prompts.length === 0) {
|
||||
return ""
|
||||
diff --git a/frontend/src/store/mutations.js b/frontend/src/store/mutations.js
|
||||
index 8dc0b19f..b2b13461 100644
|
||||
--- a/frontend/src/store/mutations.js
|
||||
+++ b/frontend/src/store/mutations.js
|
||||
@@ -191,6 +191,7 @@ export const mutations = {
|
||||
emitStateChanged();
|
||||
},
|
||||
showHover: (value) => {
|
||||
+ console.log("showHover", value);
|
||||
if (typeof value === "object") {
|
||||
state.prompts.push({
|
||||
name: value?.name,
|
||||
@@ -206,6 +207,7 @@ export const mutations = {
|
||||
props: value?.props,
|
||||
});
|
||||
}
|
||||
+ console.log("showHover", state.prompts);
|
||||
emitStateChanged();
|
||||
},
|
||||
setLoading: (loadType, status) => {
|
||||
@@ -221,6 +223,7 @@ export const mutations = {
|
||||
emitStateChanged();
|
||||
},
|
||||
setCurrentUser: (value) => {
|
||||
+ console.log('setCurrentUser', value);
|
||||
try {
|
||||
// If value is null or undefined, emit state change and exit early
|
||||
if (!value) {
|
||||
@@ -243,6 +246,7 @@ export const mutations = {
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
+ console.log('setCurrentUser', state.user);
|
||||
emitStateChanged();
|
||||
},
|
||||
setJWT: (value) => {
|
||||
@@ -303,6 +307,7 @@ export const mutations = {
|
||||
if (!state.user) {
|
||||
state.user = {};
|
||||
}
|
||||
+ console.log('updateCurrentUser', value);
|
||||
// Store previous state for comparison
|
||||
const previousUser = { ...state.user };
|
||||
|
||||
@@ -315,12 +320,14 @@ export const mutations = {
|
||||
i18n.default.locale = state.user.locale;
|
||||
localStorage.setItem("userLocale", state.user.locale);
|
||||
}
|
||||
+ console.log('updateCurrentUser2', state.user);
|
||||
// Update localStorage if stickySidebar exists
|
||||
if (state.user.stickySidebar && getters.currentView() == "listingView") {
|
||||
state.multiButtonState = "menu";
|
||||
} else if (state.showSidebar) {
|
||||
state.multiButtonState = "back";
|
||||
}
|
||||
+ console.log('updateCurrentUser3', state.user);
|
||||
// Update users if there's any change in state.user
|
||||
if (JSON.stringify(state.user) !== JSON.stringify(previousUser)) {
|
||||
// Only update the properties that were actually provided in the input
|
||||
diff --git a/frontend/src/views/Files.vue b/frontend/src/views/Files.vue
|
||||
index 4c69ecd7..6f8b892b 100644
|
||||
--- a/frontend/src/views/Files.vue
|
||||
+++ b/frontend/src/views/Files.vue
|
||||
@@ -9,9 +9,8 @@
|
||||
<div class="card-title">
|
||||
<h2>{{ $t("general.password") }}</h2>
|
||||
</div>
|
||||
- <div class="card-content form-flex-group">
|
||||
+ <div class="card-content">
|
||||
<input
|
||||
- class="input share-password"
|
||||
v-focus
|
||||
type="password"
|
||||
:placeholder="$t('general.password')"
|
||||
@@ -149,10 +148,8 @@ export default {
|
||||
});
|
||||
// Add glow effect
|
||||
element.classList.add('scroll-glow');
|
||||
- console.log("scrollToHash", element)
|
||||
// Remove glow effect after animation completes
|
||||
setTimeout(() => {
|
||||
- console.log("scrollToHash remove glow", element)
|
||||
element.classList.remove('scroll-glow');
|
||||
}, 1000);
|
||||
}
|
||||
@@ -211,15 +208,19 @@ export default {
|
||||
this.shareHash = parts[1]
|
||||
this.shareSubPath = "/" + parts.slice(2).join("/");
|
||||
|
||||
+ // Store share data in state for use by components
|
||||
+ mutations.setShareData({
|
||||
+ hash: this.shareHash,
|
||||
+ token: this.shareToken,
|
||||
+ subPath: this.shareSubPath,
|
||||
+ });
|
||||
+
|
||||
// Handle password
|
||||
- if (this.sharePassword === "") {
|
||||
+ if (this.sharePassword === "" || this.sharePassword === null) {
|
||||
this.sharePassword = localStorage.getItem("sharepass:" + this.shareHash);
|
||||
} else {
|
||||
localStorage.setItem("sharepass:" + this.shareHash, this.sharePassword);
|
||||
}
|
||||
- if (this.sharePassword === null) {
|
||||
- this.sharePassword = "";
|
||||
- }
|
||||
|
||||
mutations.resetSelected();
|
||||
mutations.setMultiple(false);
|
||||
@@ -229,15 +230,11 @@ export default {
|
||||
let file = await publicApi.fetchPub(this.shareSubPath, this.shareHash, this.sharePassword);
|
||||
file.hash = this.shareHash;
|
||||
this.shareToken = file.token;
|
||||
- // Store share data in state for use by components
|
||||
- mutations.setShareData({
|
||||
- hash: this.shareHash,
|
||||
- token: this.shareToken,
|
||||
- subPath: this.shareSubPath,
|
||||
- });
|
||||
+
|
||||
// If not a directory, fetch content for preview components
|
||||
if (file.type != "directory") {
|
||||
const content = !getters.fileViewingDisabled(file.name);
|
||||
+
|
||||
file = await publicApi.fetchPub(this.shareSubPath, this.shareHash, this.sharePassword, content);
|
||||
file.hash = this.shareHash;
|
||||
this.shareToken = file.token;
|
||||
@@ -324,7 +321,7 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
-<style>
|
||||
+<style scoped>
|
||||
.share__wrong__password {
|
||||
color: #ff4757;
|
||||
text-align: center;
|
||||
@@ -351,8 +348,4 @@ export default {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
-
|
||||
-.share-password {
|
||||
- width: 100%;
|
||||
-}
|
||||
</style>
|
||||
diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue
|
||||
index 4547e3d2..38d75dc1 100644
|
||||
--- a/frontend/src/views/Settings.vue
|
||||
+++ b/frontend/src/views/Settings.vue
|
||||
@@ -122,4 +122,7 @@ export default {
|
||||
background-color: var(--surfaceSecondary);
|
||||
}
|
||||
|
||||
+.card-content {
|
||||
+ padding: 1em;
|
||||
+}
|
||||
</style>
|
||||
diff --git a/frontend/src/views/settings/Profile.vue b/frontend/src/views/settings/Profile.vue
|
||||
index f65fe30f..e34e405a 100644
|
||||
--- a/frontend/src/views/settings/Profile.vue
|
||||
+++ b/frontend/src/views/settings/Profile.vue
|
||||
@@ -428,6 +428,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
+.card-content h3 {
|
||||
+ text-align: center;
|
||||
+}
|
||||
|
||||
#disablePreviews,
|
||||
#disableViewing,
|
||||
diff --git a/frontend/src/views/settings/Shares.vue b/frontend/src/views/settings/Shares.vue
|
||||
index cd28af70..6d39166e 100644
|
||||
--- a/frontend/src/views/settings/Shares.vue
|
||||
+++ b/frontend/src/views/settings/Shares.vue
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
<script>
|
||||
import { notify } from "@/notify";
|
||||
-import { publicApi } from "@/api";
|
||||
+import { shareApi } from "@/api";
|
||||
import { state, mutations, getters } from "@/store";
|
||||
import Clipboard from "clipboard";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
@@ -70,7 +70,7 @@ export default {
|
||||
async created() {
|
||||
mutations.setLoading("shares", true);
|
||||
try {
|
||||
- let links = await publicApi.list();
|
||||
+ let links = await shareApi.list();
|
||||
if (links.length === 0) {
|
||||
this.links = [];
|
||||
return;
|
||||
@@ -115,7 +115,7 @@ export default {
|
||||
mutations.closeHovers();
|
||||
|
||||
try {
|
||||
- publicApi.remove(link.hash);
|
||||
+ shareApi.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
notify.showSuccess(this.$t("settings.shareDeleted"));
|
||||
} catch (e) {
|
||||
@@ -128,7 +128,7 @@ export default {
|
||||
return fromNow(time);
|
||||
},
|
||||
buildLink(share) {
|
||||
- return publicApi.getShareURL(share);
|
||||
+ return shareApi.getShareURL(share);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
frontend
|
||||
@@ -8,11 +8,10 @@
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build && cp -r dist/* ../backend/http/embed",
|
||||
"build-windows": "vite build && robocopy dist ../backend/http/embed /e",
|
||||
"build": "vite build --outDir ../backend/http/dist && cp -r ../backend/http/dist/* ../backend/http/embed",
|
||||
"build-windows": "vite build --outDir ../backend/http/dist && robocopy ../backend/http/dist ../backend/http/embed /e",
|
||||
"build-docker": "vite build",
|
||||
"watch": "vite build --watch",
|
||||
"watch": "vite build --watch --outDir ../backend/http/dist",
|
||||
"typecheck": "vue-tsc -p ./tsconfig.json --noEmit",
|
||||
"lint": "eslint --ext .js,.vue,ts src",
|
||||
"lint:fix": "eslint --fix src/",
|
||||
@@ -22,6 +21,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@onlyoffice/document-editor-vue": "^1.4.0",
|
||||
"@skjnldsv/vue-plyr": "^7.5.0",
|
||||
"ace-builds": "^1.24.2",
|
||||
"axios": "^1.7.9",
|
||||
"clipboard": "^2.0.4",
|
||||
@@ -31,6 +31,7 @@
|
||||
"file-loader": "^6.2.0",
|
||||
"glob": "^9.3.5",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsmediatags": "github:Kurami32/jsmediatags",
|
||||
"mammoth": "^1.9.1",
|
||||
"marked": "^15.0.6",
|
||||
"normalize.css": "^8.0.1",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
server:
|
||||
minSearchLength: 3 # minimum length of search query to begin searching (default: 3)
|
||||
disableUpdateCheck: false # disables backend update check service
|
||||
numImageProcessors: 4 # number of concurrent image processing jobs used to create previews, default is number of cpu cores available.
|
||||
socket: "" # socket to listen on
|
||||
tlsKey: "" # path to TLS key
|
||||
@@ -48,7 +50,7 @@ server:
|
||||
cacheDir: "tmp" # path to the cache directory, used for thumbnails and other cached files
|
||||
maxArchiveSize: 50 # max pre-archive combined size of files/folder that are allowed to be archived (in GB)
|
||||
auth:
|
||||
tokenExpirationHours: 2 # the number of hours until the token expires. Default is 2 hours.
|
||||
tokenExpirationHours: 2 # time in hours each web UI session token is valid for. Default is 2 hours.
|
||||
methods:
|
||||
proxy: # validate:omitempty
|
||||
enabled: false
|
||||
@@ -67,8 +69,8 @@ auth:
|
||||
enforcedOtp: false # if set to true, TOTP is enforced for all password users users. Otherwise, users can choose to enable TOTP.
|
||||
oidc: # validate:omitempty
|
||||
enabled: false # whether to enable OIDC authentication
|
||||
clientId: "" # client id of the OIDC application
|
||||
clientSecret: "" # client secret of the OIDC application
|
||||
clientId: "**hidden**" # secret: client id of the OIDC application
|
||||
clientSecret: "**hidden**" # secret: client secret of the OIDC application
|
||||
issuerUrl: "" # authorization URL of the OIDC provider
|
||||
scopes: "openid email profile" # scopes to request from the OIDC provider
|
||||
userIdentifier: "preferred_username" # the field value to use as the username. Default is "preferred_username", can also be "email" or "username", or "phone"
|
||||
@@ -77,10 +79,10 @@ auth:
|
||||
createUser: false # create user if not exists
|
||||
adminGroup: "" # if set, users in this group will be granted admin privileges.
|
||||
groupsClaim: "groups" # the JSON field name to read groups from. Default is "groups"
|
||||
key: "" # the key used to sign the JWT tokens. If not set, a random key will be generated.
|
||||
adminUsername: "admin" # the username of the admin user. If not set, the default is "admin".
|
||||
adminPassword: "admin" # the password of the admin user. If not set, the default is "admin".
|
||||
totpSecret: "" # secret used to encrypt TOTP secrets
|
||||
key: "**hidden**" # secret: the key used to sign the JWT tokens. If not set, a random key will be generated.
|
||||
adminUsername: "**hidden**" # secret: the username of the admin user. If not set, the default is "admin".
|
||||
adminPassword: "**hidden**" # secret: the password of the admin user. If not set, the default is "admin".
|
||||
totpSecret: "**hidden**" # secret: secret used to encrypt TOTP secrets
|
||||
frontend:
|
||||
name: "FileBrowser Quantum" # display name
|
||||
disableDefaultLinks: false # disable default links in the sidebar
|
||||
@@ -91,19 +93,21 @@ frontend:
|
||||
url: "https://github.com/gtsteffaniak/filebrowser/releases/" # the url to link to validate:required
|
||||
- text: "Help" # the text to display on the link validate:required
|
||||
title: "" # the title to display on hover
|
||||
url: "https://github.com/gtsteffaniak/filebrowser/wiki" # the url to link to validate:required
|
||||
url: "help prompt" # the url to link to validate:required
|
||||
disableNavButtons: false # disable the nav buttons in the sidebar
|
||||
styling:
|
||||
customCSS: "" # if a valid path to a css file is provided, it will be applied for all users. (eg. "reduce-rounded-corners.css")
|
||||
lightBackground: "#f5f5f5" # specify a valid CSS color property value to use as the background color in light mode
|
||||
darkBackground: "#141D24" # Specify a valid CSS color property value to use as the background color in dark mode
|
||||
customThemes: # A list of custom css files that each user can select to override the default styling. if "default" is key name then it will be the default option.
|
||||
alternative:
|
||||
description: Reduce rounded corners
|
||||
css: reduce-rounded-corners.css
|
||||
default:
|
||||
description: The default theme
|
||||
css: ""
|
||||
description: "The default theme" # The description of the theme to display in the UI.
|
||||
css: "" # The css file path and filename to use for the theme.
|
||||
alternative:
|
||||
description: "Reduce rounded corners" # The description of the theme to display in the UI.
|
||||
css: "reduce-rounded-corners.css" # The css file path and filename to use for the theme.
|
||||
favicon: "" # path to a favicon to use for the frontend
|
||||
description: "FileBrowser Quantum is a file manager for the web which can be used to manage files on your server" # description that shows up in html head meta description
|
||||
userDefaults:
|
||||
editorQuickSave: false # show quick save button in editor
|
||||
hideSidebarFileActions: false # hide the file actions in the sidebar
|
||||
@@ -119,8 +123,8 @@ userDefaults:
|
||||
gallerySize: 3 # 0-9 - the size of the gallery thumbnails
|
||||
themeColor: "var(--blue)" # theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
|
||||
quickDownload: false # show icon to download in one click
|
||||
disablePreviewExt: "" # comma separated list of file extensions to disable preview for
|
||||
disableViewingExt: "" # comma separated list of file extensions to disable viewing for
|
||||
disablePreviewExt: "" # space separated list of file extensions to disable preview for
|
||||
disableViewingExt: "" # space separated list of file extensions to disable viewing for
|
||||
lockPassword: false # disable the user from changing their password
|
||||
disableSettings: false # disable the user from viewing the settings page
|
||||
preview:
|
||||
@@ -132,6 +136,7 @@ userDefaults:
|
||||
office: false
|
||||
popup: false
|
||||
autoplayMedia: false
|
||||
defaultMediaPlayer: false
|
||||
permissions:
|
||||
api: false
|
||||
admin: false
|
||||
@@ -144,14 +149,14 @@ userDefaults:
|
||||
fileLoading: # upload and download settings
|
||||
maxConcurrentUpload: 10
|
||||
uploadChunkSizeMb: 10
|
||||
disableOfficePreviewExt: "" # deprecated: use disablePreviewExt instead
|
||||
disableOnlyOfficeExt: "" # list of file extensions to disable onlyoffice editor for
|
||||
customTheme: "" # Name of theme to use chosen from custom themes config.
|
||||
showSelectMultiple: false # show select multiple files on desktop
|
||||
debugOffice: false # debug onlyoffice editor
|
||||
integrations:
|
||||
office: # validate:omitempty
|
||||
url: "" # The URL to the OnlyOffice Document Server, needs to be accessible to the user. validate:required
|
||||
internalUrl: "" # An optional internal address that the filebrowser server can use to communicate with the OnlyOffice Document Server, could be useful to bypass proxy.
|
||||
secret: "" # validate:required
|
||||
secret: "**hidden**" # secret: authentication key for OnlyOffice integration validate:required
|
||||
media: # validate:omitempty
|
||||
ffmpegPath: "" # path to ffmpeg directory with ffmpeg and ffprobe (eg. /usr/local/bin)
|
||||
@@ -50,9 +50,15 @@ async function translateText(text, targetLanguage, keyPath = '') {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (/\{[^}]+\}/.test(text)) {
|
||||
console.log(`Skipping translation for placeholder string: "${text}"`);
|
||||
return text;
|
||||
const hasPlaceholders = /\{[^}]+\}/.test(text);
|
||||
let textToTranslate = text;
|
||||
const options = {};
|
||||
|
||||
if (hasPlaceholders) {
|
||||
console.log(`Found placeholder in: "${text}". Wrapping for translation.`);
|
||||
textToTranslate = text.replace(/(\{[^}]+\})/g, '<ph>$1</ph>');
|
||||
options.tagHandling = 'xml';
|
||||
options.ignoreTags = ['ph'];
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -60,12 +66,19 @@ async function translateText(text, targetLanguage, keyPath = '') {
|
||||
|
||||
let deeplTargetLang = deeplLangMap[targetLanguage.toLowerCase()] || targetLanguage.toUpperCase();
|
||||
|
||||
const result = await translator.translateText(text, masterLanguageCode, deeplTargetLang);
|
||||
const result = await translator.translateText(textToTranslate, masterLanguageCode, deeplTargetLang, options);
|
||||
|
||||
// Delay to avoid rate-limiting
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
return result.text;
|
||||
let translatedText = result.text;
|
||||
|
||||
if (hasPlaceholders) {
|
||||
// Unwrap the placeholders. The translator might add spaces around tags.
|
||||
translatedText = translatedText.replace(/<ph>\s*(\{[^}]+\})\s*<\/ph>/g, '$1');
|
||||
}
|
||||
|
||||
return translatedText;
|
||||
|
||||
} catch (err) {
|
||||
console.error(`⚠️ Translation failed for "${text}" (${keyPath}):`, err?.message || err);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fetchURL, adjustedData } from './utils'
|
||||
import { getApiPath, doubleEncode } from '@/utils/url.js'
|
||||
import { getApiPath, doubleEncode, getPublicApiPath } from '@/utils/url.js'
|
||||
import { mutations } from '@/store'
|
||||
import { notify } from '@/notify'
|
||||
import { externalUrl } from '@/utils/constants'
|
||||
@@ -265,3 +265,17 @@ export async function sources() {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export async function GetOfficeConfig(req) {
|
||||
const params = {
|
||||
path: encodeURIComponent(req.path),
|
||||
...(req.hash && { hash: encodeURIComponent(req.hash) }),
|
||||
...(req.source && { source: encodeURIComponent(req.source) })
|
||||
}
|
||||
let apiPath = getApiPath('api/onlyoffice/config', params)
|
||||
if (req.hash) {
|
||||
apiPath = getPublicApiPath('onlyoffice/config', params)
|
||||
}
|
||||
const res = await fetchURL(apiPath)
|
||||
return await res.json()
|
||||
}
|
||||
@@ -3,7 +3,8 @@ import * as publicApi from "./public";
|
||||
import * as usersApi from "./users";
|
||||
import * as settingsApi from "./settings";
|
||||
import * as accessApi from "./access";
|
||||
import * as shareApi from "./share";
|
||||
import search from "./search";
|
||||
|
||||
// Note: shareApi has been consolidated into publicApi
|
||||
export { filesApi, publicApi, usersApi, settingsApi, search, accessApi };
|
||||
export { filesApi, publicApi, usersApi, settingsApi, shareApi, search, accessApi };
|
||||
@@ -1,4 +1,4 @@
|
||||
import { fetchURL, fetchJSON, adjustedData } from "./utils";
|
||||
import { adjustedData } from "./utils";
|
||||
import { notify } from "@/notify";
|
||||
import { getApiPath, getPublicApiPath } from "@/utils/url.js";
|
||||
import { externalUrl } from "@/utils/constants";
|
||||
@@ -57,8 +57,10 @@ export async function fetchPub(path, hash, password = "", content = false) {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getDownloadURL(share, files, inline=false) {
|
||||
// Join files array with || delimiter and then URL encode
|
||||
const filesParam = Array.isArray(files) ? files.join('||') : files;
|
||||
const params = {
|
||||
files: files,
|
||||
files: encodeURIComponent(filesParam),
|
||||
hash: share.hash,
|
||||
token: share.token,
|
||||
...(inline && { inline: 'true' })
|
||||
@@ -89,60 +91,6 @@ export function getPreviewURL(path,size="small") {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SHARE MANAGEMENT API (permission-based authentication)
|
||||
// ============================================================================
|
||||
|
||||
// List all shares
|
||||
export async function list() {
|
||||
const apiPath = getApiPath("public/shares");
|
||||
return fetchJSON(apiPath);
|
||||
}
|
||||
|
||||
// Get share information
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} source
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function get(path, source) {
|
||||
try {
|
||||
const params = { path: encodeURIComponent(path), source: encodeURIComponent(source) };
|
||||
const apiPath = getApiPath("public/share", params);
|
||||
let data = await fetchJSON(apiPath);
|
||||
return adjustedData(data);
|
||||
} catch (/** @type {any} */ err) {
|
||||
notify.showError(err.message || "Error fetching data");
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove/delete a share
|
||||
/**
|
||||
* @param {string} hash
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function remove(hash) {
|
||||
const params = { hash };
|
||||
const apiPath = getApiPath("public/share", params);
|
||||
await fetchURL(apiPath, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new share
|
||||
/**
|
||||
* @param {Record<string, any>} bodyObj
|
||||
* @returns {Promise<Share>}
|
||||
*/
|
||||
export async function create(bodyObj = {}) {
|
||||
const apiPath = getApiPath("public/share");
|
||||
return fetchJSON(apiPath, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(bodyObj || {}),
|
||||
});
|
||||
}
|
||||
|
||||
// Get the shareable URL for a share
|
||||
/**
|
||||
* @param {{ hash: string }} share
|
||||
@@ -155,21 +103,3 @@ export function getShareURL(share) {
|
||||
}
|
||||
return window.origin + getApiPath(`public/share/${share.hash}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} Share
|
||||
* @property {string} hash
|
||||
* @property {string} path
|
||||
* @property {string} source
|
||||
* @property {number} expire
|
||||
* @property {number} downloadsLimit
|
||||
* @property {number} maxBandwidth
|
||||
* @property {string} shareTheme
|
||||
* @property {boolean} disableAnonymous
|
||||
* @property {boolean} disableThumbnails
|
||||
* @property {boolean} keepAfterExpiration
|
||||
* @property {string[]} allowedUsernames
|
||||
* @property {string} viewMode
|
||||
* @property {string} token
|
||||
* @property {boolean} inline
|
||||
*/
|
||||
@@ -13,3 +13,11 @@ export async function update(settings) {
|
||||
body: JSON.stringify(settings),
|
||||
});
|
||||
}
|
||||
|
||||
export function getConfig(showFull = false, showComments = false) {
|
||||
const params = {};
|
||||
if (showFull) params.full = "true";
|
||||
if (showComments) params.comments = "true";
|
||||
const path = getApiPath("api/settings/config", params);
|
||||
return fetchURL(path);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { fetchURL, fetchJSON, adjustedData } from "./utils";
|
||||
import { notify } from "@/notify";
|
||||
import { getApiPath } from "@/utils/url.js";
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// SHARE MANAGEMENT API (permission-based authentication)
|
||||
// ============================================================================
|
||||
|
||||
// List all shares
|
||||
export async function list() {
|
||||
const apiPath = getApiPath("api/shares");
|
||||
return fetchJSON(apiPath);
|
||||
}
|
||||
|
||||
// Get share information
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {string} source
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function get(path, source) {
|
||||
try {
|
||||
const params = { path: encodeURIComponent(path), source: encodeURIComponent(source) };
|
||||
const apiPath = getApiPath("api/share", params);
|
||||
let data = await fetchJSON(apiPath);
|
||||
return adjustedData(data);
|
||||
} catch (/** @type {any} */ err) {
|
||||
notify.showError(err.message || "Error fetching data");
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove/delete a share
|
||||
/**
|
||||
* @param {string} hash
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function remove(hash) {
|
||||
const params = { hash };
|
||||
const apiPath = getApiPath("api/share", params);
|
||||
await fetchURL(apiPath, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new share
|
||||
/**
|
||||
* @param {Record<string, any>} bodyObj
|
||||
* @returns {Promise<Share>}
|
||||
*/
|
||||
export async function create(bodyObj = {}) {
|
||||
const apiPath = getApiPath("api/share");
|
||||
return fetchJSON(apiPath, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(bodyObj || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} Share
|
||||
* @property {string} hash
|
||||
* @property {string} path
|
||||
* @property {string} source
|
||||
* @property {number} expire
|
||||
* @property {number} downloadsLimit
|
||||
* @property {number} maxBandwidth
|
||||
* @property {string} shareTheme
|
||||
* @property {boolean} disableAnonymous
|
||||
* @property {boolean} disableThumbnails
|
||||
* @property {boolean} keepAfterExpiration
|
||||
* @property {string[]} allowedUsernames
|
||||
* @property {string} viewMode
|
||||
* @property {string} token
|
||||
* @property {boolean} inline
|
||||
*/
|
||||
@@ -14,15 +14,25 @@
|
||||
:class="{ 'dark-mode': isDarkMode, 'centered': centered }"
|
||||
:key="showCreate ? 'create-mode' : 'normal-mode'"
|
||||
>
|
||||
<div v-if="selectedCount > 0" class="button selected-count-header">
|
||||
<span>{{ selectedCount }} {{ $t("prompts.selected") }} </span>
|
||||
<div class="context-menu-header">
|
||||
<div
|
||||
class="action button clickable"
|
||||
v-if="!isSearchActive && userPerms.modify && !isShare"
|
||||
@click="toggleShowCreate"
|
||||
>
|
||||
<i v-if="!showCreate" class="material-icons">add</i>
|
||||
<i v-if="showCreate" class="material-icons">arrow_back</i>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedCount > 0"
|
||||
@mouseleave="hideTooltip"
|
||||
@mouseenter="showTooltip($event, $t('buttons.selectedCount'))"
|
||||
class="button selected-count-header"
|
||||
>
|
||||
<span>{{ selectedCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<action
|
||||
v-if="!showCreate && !isSearchActive && userPerms.modify && !isShare"
|
||||
icon="add"
|
||||
:label="$t('buttons.new')"
|
||||
@action="startShowCreate"
|
||||
/>
|
||||
<hr class="divider">
|
||||
<action
|
||||
v-if="showCreate && !isSearchActive && userPerms.modify"
|
||||
icon="create_new_folder"
|
||||
@@ -306,6 +316,19 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideTooltip() {
|
||||
mutations.hideTooltip();
|
||||
},
|
||||
showTooltip(event, text) {
|
||||
mutations.showTooltip({
|
||||
content: text,
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
});
|
||||
},
|
||||
toggleShowCreate() {
|
||||
this.showCreate = !this.showCreate;
|
||||
},
|
||||
shouldShowParentFolder() {
|
||||
return this.isPreview && state.req.path != "/";
|
||||
},
|
||||
@@ -538,4 +561,10 @@ export default {
|
||||
height: 0 !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.context-menu-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
@@ -50,7 +50,7 @@
|
||||
</select>
|
||||
|
||||
<!-- Formatted display of selected value -->
|
||||
<div class="searchContext">{{ contextText }}</div>
|
||||
<div class="searchContext">{{ $t("search.searchContext", { context: getContext }) }}</div>
|
||||
</div>
|
||||
|
||||
<div id="result-list">
|
||||
@@ -87,7 +87,7 @@
|
||||
v-model="smallerThan"
|
||||
type="number"
|
||||
min="0"
|
||||
placeholder="number"
|
||||
:placeholder="$t('search.number')"
|
||||
/><p>MB</p> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
</div>
|
||||
<div class="sizeInputWrapper">
|
||||
@@ -96,7 +96,7 @@
|
||||
class="sizeInput"
|
||||
v-model="largerThan"
|
||||
type="number"
|
||||
placeholder="number"
|
||||
:placeholder="$t('search.number')"
|
||||
/><p>MB</p> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,9 +146,8 @@ import { search } from "@/api";
|
||||
import { getters, mutations, state } from "@/store";
|
||||
import { getHumanReadableFilesize } from "@/utils/filesizes";
|
||||
import { url } from "@/utils/";
|
||||
|
||||
import Icon from "@/components/files/Icon.vue";
|
||||
import { serverHasMultipleSources, baseURL } from "@/utils/constants";
|
||||
import { serverHasMultipleSources, baseURL, minSearchLength } from "@/utils/constants";
|
||||
|
||||
var boxes = {
|
||||
folder: { label: "folders", icon: "folder" },
|
||||
@@ -170,22 +169,22 @@ export default {
|
||||
return {
|
||||
largerThan: "",
|
||||
smallerThan: "",
|
||||
noneMessage: "Start typing 3 or more characters to begin searching.",
|
||||
noneMessage: this.$t("search.typeToSearch", { minSearchLength: minSearchLength }),
|
||||
searchTypes: "",
|
||||
isTypeSelectDisabled: false,
|
||||
showHelp: false,
|
||||
folderSelect: [
|
||||
{ label: "Only Folders", value: "type:folder" },
|
||||
{ label: "Only Files", value: "type:file" },
|
||||
{ label: this.$t("search.onlyFolders"), value: "type:folder" },
|
||||
{ label: this.$t("search.onlyFiles"), value: "type:file" },
|
||||
],
|
||||
typeSelect: [
|
||||
{ label: "Photos", value: "type:image" },
|
||||
{ label: "Audio", value: "type:audio" },
|
||||
{ label: "Videos", value: "type:video" },
|
||||
{ label: "Documents", value: "type:doc" },
|
||||
{ label: "Archives", value: "type:archive" },
|
||||
{ label: this.$t("search.photos"), value: "type:image" },
|
||||
{ label: this.$t("search.audio"), value: "type:audio" },
|
||||
{ label: this.$t("search.videos"), value: "type:video" },
|
||||
{ label: this.$t("search.documents"), value: "type:doc" },
|
||||
{ label: this.$t("search.archives"), value: "type:archive" },
|
||||
],
|
||||
toggleOptionButton: [{ label: "Show Options" }],
|
||||
toggleOptionButton: [{ label: this.$t("search.showOptions") }],
|
||||
value: "",
|
||||
ongoing: false,
|
||||
results: [],
|
||||
@@ -295,9 +294,7 @@ export default {
|
||||
if (this.ongoing) {
|
||||
return "";
|
||||
}
|
||||
return this.value === ""
|
||||
? this.$t("search.typeToSearch")
|
||||
: this.$t("search.pressToSearch");
|
||||
return this.$t("search.typeToSearch", { minSearchLength: minSearchLength })
|
||||
},
|
||||
isRunning() {
|
||||
return this.ongoing;
|
||||
@@ -330,9 +327,6 @@ export default {
|
||||
return "/"; // if searching on non-current source, search the whole thing
|
||||
}
|
||||
},
|
||||
contextText() {
|
||||
return `Search Context: ${this.getContext}`; // FIX: Use `this.getContext` as a property, not a function call
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openContext(event) {
|
||||
@@ -489,9 +483,9 @@ export default {
|
||||
if (event != undefined) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (this.value === "" || this.value.length < 3) {
|
||||
if (this.value === "" || this.value.length < minSearchLength) {
|
||||
this.ongoing = false;
|
||||
this.noneMessage = "Not enough characters to search (min 3)";
|
||||
this.noneMessage = this.$t("search.notEnoughCharacters", { minSearchLength: minSearchLength });
|
||||
return;
|
||||
}
|
||||
let searchTypesFull = this.searchTypes;
|
||||
@@ -510,7 +504,7 @@ export default {
|
||||
|
||||
this.ongoing = false;
|
||||
if (this.results.length == 0) {
|
||||
this.noneMessage = "No results found in indexed search.";
|
||||
this.noneMessage = this.$t("search.noResults");
|
||||
}
|
||||
},
|
||||
toggleHelp() {
|
||||
@@ -872,4 +866,72 @@ body.rtl #search .boxes h3 {
|
||||
padding-right: 0.5em;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
#search {
|
||||
min-width: unset;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
#search.active {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#search-input {
|
||||
transition: 1s ease all;
|
||||
}
|
||||
|
||||
#search.active #search-input {
|
||||
border-bottom: 3px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(6px);
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
#search.active>div {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
#search.active #result {
|
||||
height: 100vh;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#search.active #result>p>i {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#search.active #result ul li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .3em 0;
|
||||
margin-right: .3em;
|
||||
}
|
||||
|
||||
#search-input>.action,
|
||||
#search-input>i {
|
||||
margin-right: 0.3em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#result-list {
|
||||
width:100vw !important;
|
||||
max-width: 100vw !important;
|
||||
left: 0;
|
||||
top: 4em;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
overflow: scroll;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -3,14 +3,14 @@
|
||||
ref="tooltipRef"
|
||||
v-show="tooltip.show"
|
||||
class="floating-tooltip fb-shadow"
|
||||
:class="{ 'dark-mode': isDarkMode }"
|
||||
:class="{ 'dark-mode': isDarkMode, 'pointer-enabled': tooltip.pointerEvents }"
|
||||
:style="tooltipStyle"
|
||||
v-html="tooltip.content"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { state } from "@/store";
|
||||
import { state,getters } from "@/store";
|
||||
|
||||
export default {
|
||||
name: "Tooltip",
|
||||
@@ -26,13 +26,27 @@ export default {
|
||||
return state.tooltip;
|
||||
},
|
||||
isDarkMode() {
|
||||
return state.user.darkMode;
|
||||
return getters.isDarkMode();
|
||||
},
|
||||
tooltipStyle() {
|
||||
return {
|
||||
const style = {
|
||||
top: `${this.adjustedY}px`,
|
||||
left: `${this.adjustedX}px`,
|
||||
};
|
||||
|
||||
// Add custom width if specified
|
||||
if (this.tooltip.width) {
|
||||
style.maxWidth = this.tooltip.width;
|
||||
style.width = this.tooltip.width;
|
||||
}
|
||||
|
||||
// Add max height and scrolling for viewport overflow
|
||||
if (this.tooltip.width || this.tooltip.pointerEvents) {
|
||||
style.maxHeight = '80vh';
|
||||
style.overflowY = 'auto';
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@@ -53,6 +67,10 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {number} x - X coordinate
|
||||
* @param {number} y - Y coordinate
|
||||
*/
|
||||
updatePosition(x, y) {
|
||||
this.$nextTick(() => {
|
||||
const tooltipEl = this.$refs.tooltipRef;
|
||||
@@ -101,6 +119,16 @@ export default {
|
||||
white-space: normal;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.floating-tooltip.pointer-enabled {
|
||||
pointer-events: auto;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.floating-tooltip.pointer-enabled a {
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tooltip-info-icon {
|
||||
font-size: 1em !important;
|
||||
padding: 0.1em !important;
|
||||
|
||||
@@ -465,19 +465,16 @@ export default {
|
||||
) {
|
||||
mutations.resetSelected();
|
||||
}
|
||||
// @ts-ignore
|
||||
mutations.addSelected(this.index);
|
||||
mutations.setLastSelectedIndex(this.index);
|
||||
},
|
||||
open() {
|
||||
if (this.hash) {
|
||||
const shareHash = this.hash;
|
||||
url.goToItem(this.source, this.path, "", shareHash);
|
||||
return;
|
||||
}
|
||||
// @ts-ignore
|
||||
const previousHash = state.req.items[this.index].name;
|
||||
url.goToItem(this.source, this.path, previousHash);
|
||||
const previousHistoryItem = {
|
||||
name: state.req.items[this.index].name,
|
||||
source: state.req.source,
|
||||
path: state.req.path,
|
||||
};
|
||||
url.goToItem(this.source, this.path, previousHistoryItem);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t("help.description") }} <a class="link" href="https://github.com/gtsteffaniak/filebrowser/wiki" target="_blank">{{ $t("help.wiki") }}</a></p>
|
||||
<ul>
|
||||
<li><strong>F1</strong> - {{ $t("help.f1") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>F2</strong> - {{ $t("help.f2") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>DEL</strong> - {{ $t("help.del") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>ESC</strong> - {{ $t("help.esc") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>CTRL + S</strong> - {{ $t("help.ctrl.s") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>CTRL + F</strong> - {{ $t("help.ctrl.f") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>CTRL + Click</strong> - {{ $t("help.ctrl.click") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>Click</strong> - {{ $t("help.click") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>Double click</strong> - {{ $t("help.doubleClick") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
<li><strong>CTRL + D</strong> - {{ $t("help.ctrl.d") }}</li> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t("prompts.renameMessage", { filename: oldName }) }}</p>
|
||||
<input class="input" :class="{ 'form-invalid': !validateFileName(name) }" v-focus type="text"
|
||||
@keyup.enter="submit" v-model.trim="name" />
|
||||
<p v-if="!validateFileName(name) && name.length > 0" class="validation-error">
|
||||
{{ $t("prompts.invalidName") }}
|
||||
<p>{{ $t("prompts.renameMessage") }}</p>
|
||||
<input class="input" :class="{ 'form-invalid': !validation.valid }" v-focus type="text" @keyup.enter="submit"
|
||||
v-model.trim="name" />
|
||||
<p v-if="!validation.valid && name.length > 0" class="validation-error">
|
||||
<span v-if="validation.reason === 'conflict'">
|
||||
{{ $t("prompts.renameMessageConflict", { filename: name }) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t("prompts.renameMessageInvalid") }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +22,7 @@
|
||||
:title="$t('buttons.cancel')">
|
||||
{{ $t("buttons.cancel") }}
|
||||
</button>
|
||||
<button @click="submit" class="button button--flat" :disabled="!validateFileName(name) || name.length === 0"
|
||||
<button @click="submit" class="button button--flat" :disabled="!validation.valid || name.length === 0"
|
||||
type="submit" :aria-label="$t('buttons.rename')" :title="$t('buttons.rename')">
|
||||
{{ $t("buttons.rename") }}
|
||||
</button>
|
||||
@@ -25,7 +30,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { filesApi } from "@/api";
|
||||
import { mutations } from "@/store";
|
||||
import { mutations, state } from "@/store";
|
||||
import { notify } from "@/notify";
|
||||
|
||||
export default {
|
||||
@@ -49,6 +54,9 @@ export default {
|
||||
oldName() {
|
||||
return this.item?.name || "";
|
||||
},
|
||||
validation() {
|
||||
return this.validateFileName(this.name);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
@@ -56,24 +64,51 @@ export default {
|
||||
*/
|
||||
validateFileName(value) {
|
||||
if (value === "") {
|
||||
return false;
|
||||
return { valid: true };
|
||||
}
|
||||
// Check for forbidden characters: forward slash and backslash
|
||||
const forbiddenChars = /[/\\]/;
|
||||
return !forbiddenChars.test(value);
|
||||
|
||||
const isFolder = this.item.type === "directory";
|
||||
|
||||
// Check for forbidden characters in file names
|
||||
if (!isFolder) {
|
||||
const forbiddenChars = /[/\\]/;
|
||||
if (forbiddenChars.test(value)) {
|
||||
return { valid: false, reason: 'invalidChar' };
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the item already exists
|
||||
for (const item of state.req.items) {
|
||||
if (item.name.toLowerCase() === value.toLowerCase()) {
|
||||
if (isFolder === (item.type === "directory")) {
|
||||
return { valid: false, reason: 'conflict' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
},
|
||||
async submit() {
|
||||
// Validate before submitting
|
||||
if (!this.validateFileName(this.name) || this.name.length === 0) {
|
||||
notify.showError(this.$t("prompts.invalidName"));
|
||||
// remove trailing slashes
|
||||
if (this.name.endsWith("/") || this.name.endsWith("\\")) {
|
||||
this.name = this.name.substring(0, this.name.length - 1);
|
||||
}
|
||||
if (!this.validation.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newPath = this.item.path.substring(0, this.item.path.lastIndexOf("/"));
|
||||
newPath = `${newPath}/${this.name}`;
|
||||
try {
|
||||
if (this.name === this.item.name) {
|
||||
mutations.closeHovers();
|
||||
return;
|
||||
}
|
||||
|
||||
const items = [{
|
||||
from: this.item.path,
|
||||
fromSource: this.item.source,
|
||||
to: this.item.path.replace(/[^/]+$/, this.name),
|
||||
to: newPath,
|
||||
toSource: this.item.source,
|
||||
}];
|
||||
|
||||
|
||||
@@ -89,6 +89,25 @@
|
||||
/>
|
||||
<ToggleSwitch class="item" v-model="disableAnonymous" :name="$t('share.disableAnonymous')" :description="$t('share.disableAnonymousDescription')" />
|
||||
<ToggleSwitch class="item" v-model="enableAllowedUsernames" :name="$t('share.enableAllowedUsernames')" :description="$t('share.enableAllowedUsernamesDescription')" />
|
||||
|
||||
<ToggleSwitch v-if="onlyOfficeAvailable" class="item" v-model="enableOnlyOffice" :name="$t('share.enableOnlyOffice')" :description="$t('share.enableOnlyOfficeDescription')" />
|
||||
<ToggleSwitch v-if="onlyOfficeAvailable" class="item" v-model="enableOnlyOfficeEditing" :name="$t('share.enableOnlyOfficeEditing')" :description="$t('share.enableOnlyOfficeEditingDescription')" />
|
||||
<p>
|
||||
{{ $t("share.enforceDarkLightMode") }}
|
||||
<i
|
||||
class="no-select material-symbols-outlined tooltip-info-icon"
|
||||
@mouseenter="showTooltip($event, $t('share.enforceDarkLightModeDescription'))"
|
||||
@mouseleave="hideTooltip"
|
||||
>
|
||||
help
|
||||
</i>
|
||||
</p>
|
||||
<select class="input" v-model="enforceDarkLightMode">
|
||||
<option value="default">{{ $t("share.default") }}</option>
|
||||
<option value="dark">{{ $t("share.dark") }}</option>
|
||||
<option value="light">{{ $t("share.light") }}</option>
|
||||
</select>
|
||||
|
||||
<div v-if="enableAllowedUsernames" class="item">
|
||||
<input class="input" type="text" v-model.trim="allowedUsernames" :placeholder="$t('share.allowedUsernamesPlaceholder')" />
|
||||
</div>
|
||||
@@ -111,26 +130,7 @@
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="advanced-toggle">
|
||||
<button
|
||||
class="button button--flat button--block"
|
||||
@click="showAdvanced = !showAdvanced"
|
||||
:aria-expanded="showAdvanced ? 'true' : 'false'"
|
||||
aria-controls="advanced-settings"
|
||||
:aria-label="showAdvanced ? $t('buttons.showLess') : $t('buttons.showMore')"
|
||||
:title="showAdvanced ? $t('buttons.showLess') : $t('buttons.showMore')"
|
||||
>
|
||||
{{ showAdvanced ? $t('buttons.showLess') : $t('buttons.showMore') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<transition
|
||||
name="expand"
|
||||
@before-enter="beforeEnter"
|
||||
@enter="enter"
|
||||
@leave="leave"
|
||||
>
|
||||
<div id="advanced-settings" v-show="showAdvanced">
|
||||
<SettingsItem :title="$t('buttons.showMore')" :collapsable="true" :start-collapsed="true">
|
||||
<div class="settings-items">
|
||||
<ToggleSwitch class="item" v-model="keepAfterExpiration" :name="$t('share.keepAfterExpiration')" :description="$t('share.keepAfterExpirationDescription')" />
|
||||
<ToggleSwitch class="item" v-model="disableThumbnails" :name="$t('share.disableThumbnails')" :description="$t('share.disableThumbnailsDescription')" />
|
||||
@@ -222,8 +222,7 @@
|
||||
</i>
|
||||
</p>
|
||||
<input class="input" type="text" v-model.trim="favicon" />
|
||||
</div>
|
||||
</transition>
|
||||
</SettingsItem>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -250,12 +249,13 @@
|
||||
<script>
|
||||
import { notify } from "@/notify";
|
||||
import { state, getters, mutations } from "@/store";
|
||||
import { publicApi } from "@/api";
|
||||
import { publicApi, shareApi } from "@/api";
|
||||
import Clipboard from "clipboard";
|
||||
import { fromNow } from "@/utils/moment";
|
||||
import { buildItemUrl, fixDownloadURL } from "@/utils/url";
|
||||
import ToggleSwitch from "@/components/settings/ToggleSwitch.vue";
|
||||
import { userSelectableThemes, externalUrl } from "@/utils/constants";
|
||||
import SettingsItem from "@/components/settings/SettingsItem.vue";
|
||||
import { userSelectableThemes, externalUrl, onlyOfficeUrl } from "@/utils/constants";
|
||||
import { eventBus } from "@/store/eventBus";
|
||||
//import ViewMode from "@/components/settings/ViewMode.vue";
|
||||
|
||||
@@ -263,6 +263,7 @@ export default {
|
||||
name: "share",
|
||||
components: {
|
||||
ToggleSwitch,
|
||||
SettingsItem,
|
||||
//ViewMode,
|
||||
},
|
||||
props: {
|
||||
@@ -310,15 +311,20 @@ export default {
|
||||
title: "",
|
||||
description: "",
|
||||
favicon: "",
|
||||
showAdvanced: false,
|
||||
quickDownload: false,
|
||||
disableNavButtons: false,
|
||||
disableShareCard: false,
|
||||
disableSidebar: false,
|
||||
enforceDarkLightMode: "default",
|
||||
enableOnlyOffice: false,
|
||||
enableOnlyOfficeEditing: false,
|
||||
//viewMode: "normal",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
onlyOfficeAvailable() {
|
||||
return onlyOfficeUrl !== "";
|
||||
},
|
||||
availableThemes() {
|
||||
return userSelectableThemes || {};
|
||||
},
|
||||
@@ -390,6 +396,9 @@ export default {
|
||||
this.disableNavButtons = this.link.hideNavButtons || false;
|
||||
this.disableShareCard = this.link.disableShareCard || false;
|
||||
this.disableSidebar = this.link.disableSidebar || false;
|
||||
this.enforceDarkLightMode = this.link.enforceDarkLightMode || "default";
|
||||
this.enableOnlyOffice = this.link.enableOnlyOffice || false;
|
||||
this.enableOnlyOfficeEditing = this.link.enableOnlyOfficeEditing || false;
|
||||
//this.viewMode = this.link.viewMode || "normal";
|
||||
}
|
||||
},
|
||||
@@ -401,7 +410,7 @@ export default {
|
||||
}
|
||||
try {
|
||||
// get last element of the path
|
||||
const links = await publicApi.get(this.item.path, this.item.source);
|
||||
const links = await shareApi.get(this.item.path, this.item.source);
|
||||
this.links = links;
|
||||
} catch (err) {
|
||||
notify.showError(err);
|
||||
@@ -420,45 +429,6 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {Element} el
|
||||
*/
|
||||
beforeEnter(el) {
|
||||
const element = /** @type {HTMLElement} */ (el);
|
||||
element.style.height = '0';
|
||||
element.style.opacity = '0';
|
||||
},
|
||||
/**
|
||||
* @param {Element} el
|
||||
* @param {() => void} done
|
||||
*/
|
||||
enter(el, done) {
|
||||
const element = /** @type {HTMLElement} */ (el);
|
||||
element.style.transition = '';
|
||||
element.style.height = '0';
|
||||
element.style.opacity = '0';
|
||||
void element.offsetHeight;
|
||||
element.style.transition = 'height 0.3s, opacity 0.3s';
|
||||
element.style.height = element.scrollHeight + 'px';
|
||||
element.style.opacity = '1';
|
||||
setTimeout(() => {
|
||||
element.style.height = 'auto';
|
||||
done();
|
||||
}, 300);
|
||||
},
|
||||
/**
|
||||
* @param {Element} el
|
||||
* @param {() => void} done
|
||||
*/
|
||||
leave(el, done) {
|
||||
const element = /** @type {HTMLElement} */ (el);
|
||||
element.style.transition = 'height 0.3s, opacity 0.3s';
|
||||
element.style.height = element.scrollHeight + 'px';
|
||||
void element.offsetHeight;
|
||||
element.style.height = '0';
|
||||
element.style.opacity = '0';
|
||||
setTimeout(done, 300);
|
||||
},
|
||||
/**
|
||||
* @param {MouseEvent} event
|
||||
* @param {string} text
|
||||
@@ -507,12 +477,15 @@ export default {
|
||||
hideNavButtons: this.disableNavButtons,
|
||||
disableShareCard: this.disableShareCard,
|
||||
disableSidebar: this.disableSidebar,
|
||||
enforceDarkLightMode: this.enforceDarkLightMode,
|
||||
enableOnlyOffice: this.enableOnlyOffice,
|
||||
enableOnlyOfficeEditing: this.enableOnlyOfficeEditing,
|
||||
};
|
||||
if (this.isEditMode) {
|
||||
payload.hash = this.link.hash;
|
||||
}
|
||||
|
||||
const res = await publicApi.create(payload);
|
||||
const res = await shareApi.create(payload);
|
||||
|
||||
if (!this.isEditMode) {
|
||||
this.links.push(res);
|
||||
@@ -539,7 +512,7 @@ export default {
|
||||
async deleteLink(event, link) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
await publicApi.remove(link.hash);
|
||||
await shareApi.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
// emit event to reload shares in settings view
|
||||
eventBus.emit('sharesChanged');
|
||||
@@ -599,9 +572,6 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
|
||||
.advanced-toggle {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.setting-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -619,15 +589,4 @@ export default {
|
||||
.input {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.expand-enter-active,
|
||||
.expand-leave-active {
|
||||
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.expand-enter,
|
||||
.expand-leave-to {
|
||||
height: 0 !important;
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title button" :class="{ 'unclickable': !collapsable }"
|
||||
@click="collapsable ? toggleCollapse() : null">
|
||||
<h3>{{ title }}</h3>
|
||||
<i v-if="collapsable" class="material-symbols-outlined collapse-icon" :class="{ 'rotated': !actuallyCollapsed }">
|
||||
expand_more
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<transition name="expand" @before-enter="beforeEnter" @enter="enter" @leave="leave">
|
||||
<div v-show="!actuallyCollapsed" class="settings-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SettingsItem',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
collapsable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
startCollapsed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
forceCollapsed: {
|
||||
type: Boolean,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: this.startCollapsed
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
actuallyCollapsed() {
|
||||
// If forceCollapsed is explicitly set, use that, otherwise use internal state
|
||||
return this.forceCollapsed !== null ? this.forceCollapsed : this.isCollapsed;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
forceCollapsed(newVal) {
|
||||
if (newVal !== null) {
|
||||
this.isCollapsed = newVal;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleCollapse() {
|
||||
if (this.forceCollapsed !== null) {
|
||||
// Emit event for parent to handle accordion logic
|
||||
this.$emit('toggle', this.title);
|
||||
} else {
|
||||
// Handle internally as before
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {Element} el
|
||||
*/
|
||||
beforeEnter(el) {
|
||||
const element = /** @type {HTMLElement} */ (el);
|
||||
element.style.height = '0';
|
||||
element.style.opacity = '0';
|
||||
},
|
||||
/**
|
||||
* @param {Element} el
|
||||
* @param {() => void} done
|
||||
*/
|
||||
enter(el, done) {
|
||||
const element = /** @type {HTMLElement} */ (el);
|
||||
element.style.transition = '';
|
||||
element.style.height = '0';
|
||||
element.style.opacity = '0';
|
||||
void element.offsetHeight;
|
||||
element.style.transition = 'height 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
||||
element.style.height = element.scrollHeight + 'px';
|
||||
element.style.opacity = '1';
|
||||
setTimeout(() => {
|
||||
element.style.height = 'auto';
|
||||
done();
|
||||
}, 300);
|
||||
},
|
||||
/**
|
||||
* @param {Element} el
|
||||
* @param {() => void} done
|
||||
*/
|
||||
leave(el, done) {
|
||||
const element = /** @type {HTMLElement} */ (el);
|
||||
element.style.transition = 'height 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
||||
element.style.height = element.scrollHeight + 'px';
|
||||
void element.offsetHeight;
|
||||
element.style.height = '0';
|
||||
element.style.opacity = '0';
|
||||
setTimeout(done, 300);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.settings-group-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 1em;
|
||||
background: var(--alt-background) !important;
|
||||
color: var(--textPrimary) !important;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.settings-group-title h3 {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
.collapse-icon.rotated {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
overflow: hidden;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.expand-enter-active,
|
||||
.expand-leave-active {
|
||||
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.expand-enter,
|
||||
.expand-leave-to {
|
||||
height: 0 !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.settings-group-title.unclickable {
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
/* Prevent content height issues during animation */
|
||||
.settings-content .input,
|
||||
.settings-content .settings-items {
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="card-wrapper user-card">
|
||||
<div
|
||||
v-if="settingsAllowed && user.username !== 'anonymous'"
|
||||
@click="navigateTo('/settings#profile-main')"
|
||||
@click="navigateTo('/settings','#profile-main')"
|
||||
class="inner-card"
|
||||
>
|
||||
<button
|
||||
@@ -53,6 +53,7 @@
|
||||
<i class="material-icons">ads_click</i>
|
||||
</div>
|
||||
<div
|
||||
v-if="darkModeTogglePossible"
|
||||
class="clickable"
|
||||
:class="{ active: user?.darkMode }"
|
||||
@click="toggleDarkMode"
|
||||
@@ -178,6 +179,7 @@ export default {
|
||||
sourceInfo: () => state.sources.info,
|
||||
activeSource: () => state.sources.current,
|
||||
realtimeActive: () => state.realtimeActive,
|
||||
darkModeTogglePossible: () => shareInfo.enforceDarkLightMode != "dark" && shareInfo.enforceDarkLightMode != "light",
|
||||
},
|
||||
watch: {
|
||||
route() {
|
||||
@@ -218,17 +220,13 @@ export default {
|
||||
}
|
||||
mutations.updateCurrentUser({ stickySidebar: !state.user.stickySidebar });
|
||||
},
|
||||
navigateTo(path) {
|
||||
const hashIndex = path.indexOf("#");
|
||||
if (hashIndex !== -1) {
|
||||
// Extract the hash
|
||||
const hash = path.substring(hashIndex);
|
||||
// Remove the hash from the path
|
||||
const cleanPath = path.substring(0, hashIndex);
|
||||
this.$router.push({ path: cleanPath, hash: hash }, () => {});
|
||||
} else {
|
||||
this.$router.push({ path: path }, () => {});
|
||||
}
|
||||
navigateTo(path,hash) {
|
||||
mutations.setPreviousHistoryItem({
|
||||
name: state.req.name,
|
||||
source: state.req.source,
|
||||
path: state.req.path,
|
||||
});
|
||||
this.$router.push({ path: path, hash: hash });
|
||||
mutations.closeHovers();
|
||||
},
|
||||
navigateToLogin() {
|
||||
@@ -406,6 +404,7 @@ button.action {
|
||||
.quick-toggles .active {
|
||||
background-color: var(--primaryColor) !important;
|
||||
border-radius: 10em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.inner-card {
|
||||
|
||||
@@ -14,7 +14,16 @@
|
||||
<div class="buffer"></div>
|
||||
<div v-if="!isSettings" class="credits">
|
||||
<span v-for="item in externalLinks" :key="item.title">
|
||||
<a :href="item.url" target="_blank" :title="item.title">{{ item.text }}</a>
|
||||
<a
|
||||
v-if="item.url === 'help prompt'"
|
||||
href="#"
|
||||
@click.prevent="help"
|
||||
:title="$t('sidebar.help')"
|
||||
>{{ $t("sidebar.help") }}</a
|
||||
>
|
||||
<a v-else :href="item.url" target="_blank" :title="item.title">{{
|
||||
item.text
|
||||
}}</a>
|
||||
</span>
|
||||
<span v-if="name != ''">
|
||||
<h4 style="margin: 0">{{ name }}</h4>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
:root {
|
||||
--blue: #2196f3;
|
||||
--dark-blue: #1E88E5;
|
||||
--red: #F44336;
|
||||
--red: #f44336;
|
||||
--dark-red: #D32F2F;
|
||||
--moon-grey: #f2f2f2;
|
||||
|
||||
--icon-red: #da4453;
|
||||
--icon-orange: #f47750;
|
||||
--icon-yellow: #fdbc4b;
|
||||
|
||||
@@ -50,68 +50,7 @@
|
||||
body.rtl .dashboard .row {
|
||||
margin-right: unset;
|
||||
}
|
||||
#search {
|
||||
min-width: unset;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
#search.active {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
#search-input {
|
||||
transition: 1s ease all;
|
||||
}
|
||||
#search.active #search-input {
|
||||
border-bottom: 3px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(6px);
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
#search.active>div {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
#search.active #result {
|
||||
height: 100vh;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#search.active #result>p>i {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#search.active #result ul li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .3em 0;
|
||||
margin-right: .3em;
|
||||
}
|
||||
|
||||
#search-input>.action,
|
||||
#search-input>i {
|
||||
margin-right: 0.3em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#result-list {
|
||||
width:100vw !important;
|
||||
max-width: 100vw !important;
|
||||
left: 0;
|
||||
top: 4em;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
overflow: scroll;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+200
-189
@@ -8,52 +8,52 @@
|
||||
@import "./listing.css";
|
||||
@import "./dashboard.css";
|
||||
@import "./login.css";
|
||||
@import './mobile.css';
|
||||
@import "./mobile.css";
|
||||
|
||||
.link {
|
||||
color: var(--blue);
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
#main .spinner {
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 0;
|
||||
padding: 1em 0;
|
||||
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;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
margin: 0 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;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
#main .spinner .bounce2 {
|
||||
animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
.delayed {
|
||||
animation: delayed linear 100ms;
|
||||
animation: delayed linear 100ms;
|
||||
}
|
||||
|
||||
@keyframes delayed {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
@@ -61,223 +61,222 @@
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.action {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease all;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
color: #546E7A;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease all;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
color: #546e7a;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.action.disabled {
|
||||
opacity: 0.2;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.2;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.action i {
|
||||
padding: 0.4em;
|
||||
transition: .1s ease-in-out all;
|
||||
border-radius: 50%;
|
||||
padding: 0.4em;
|
||||
transition: 0.1s ease-in-out all;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.action:not(:disabled):hover {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.action ul {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
color: #7d7d7d;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
color: #7d7d7d;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.action ul li {
|
||||
line-height: 1;
|
||||
padding: .7em;
|
||||
transition: .1s ease background-color;
|
||||
line-height: 1;
|
||||
padding: 0.7em;
|
||||
transition: 0.1s ease background-color;
|
||||
}
|
||||
|
||||
.action ul li:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.action .counter {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: var(--primaryColor);
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: .75em;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
text-align: center;
|
||||
line-height: 1.55em;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: var(--primaryColor);
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: 0.75em;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
text-align: center;
|
||||
line-height: 1.55em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* PREVIEWER */
|
||||
|
||||
#previewer {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#previewer header {
|
||||
background: none;
|
||||
color: #fff;
|
||||
background: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#previewer header > .action i {
|
||||
color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
#previewer header #dropdown .action i {
|
||||
color: #fff;
|
||||
}
|
||||
#previewer header #dropdown .action i {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
#previewer header .action:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3)
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
#previewer header .action span {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#previewer .preview {
|
||||
text-align: center;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#previewer .preview pre {
|
||||
text-align: left;
|
||||
overflow: auto;
|
||||
text-align: left;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#previewer .preview pre,
|
||||
#previewer .preview video,
|
||||
#previewer .preview img {
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#previewer .preview video {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#previewer .preview .info {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.5em;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.5em;
|
||||
color: #fff;
|
||||
}
|
||||
#previewer .preview .info .title {
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
#previewer .preview .info .title i {
|
||||
display: block;
|
||||
margin-bottom: .1em;
|
||||
font-size: 4em;
|
||||
display: block;
|
||||
margin-bottom: 0.1em;
|
||||
font-size: 4em;
|
||||
}
|
||||
#previewer .preview .info .button {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
#previewer .preview .info .button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2)
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
#previewer .preview .info .button i {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1.3em;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
#previewer .pdf {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#previewer h2.message {
|
||||
color: rgba(255, 255, 255, 0.5)
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
#previewer>button {
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
top: calc(50% + 1.85em);
|
||||
transform: translateY(-50%);
|
||||
background-color: rgba(80, 80, 80, .5);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
transition: 0.2s ease all;
|
||||
#previewer > button {
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
top: calc(50% + 1.85em);
|
||||
transform: translateY(-50%);
|
||||
background-color: rgba(80, 80, 80, 0.5);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
transition: 0.2s ease all;
|
||||
}
|
||||
|
||||
#previewer>button.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
#previewer > button.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#previewer>button>i {
|
||||
padding: 0.4em;
|
||||
#previewer > button > i {
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
#previewer>button:last-of-type {
|
||||
right: 0.5em;
|
||||
#previewer > button:last-of-type {
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
#previewer .spinner {
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: calc(50% + 1.85em);
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
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;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* EDITOR */
|
||||
|
||||
#editor-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#editor-container #editor {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
@@ -285,9 +284,9 @@
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
@@ -295,31 +294,31 @@
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.rules > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: .5rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.rules input[type="checkbox"] {
|
||||
margin-right: .2rem;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.rules input[type="text"] {
|
||||
border: 1px solid#ddd;
|
||||
padding: .2rem;
|
||||
border: 1px solid#ddd;
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
.rules label {
|
||||
margin-right: .5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.rules button {
|
||||
margin-left: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.rules button.delete {
|
||||
padding: .2rem .5rem;
|
||||
margin-left: .5rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
@@ -327,78 +326,90 @@
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
body.rtl .card-content textarea {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
body.rtl .card-content .small + input {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
body.rtl .card.floating .card-content .file-list {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.fb-shadow {
|
||||
box-shadow: 0px 0px 15px 0px rgb(100 100 100 / 75%);
|
||||
-webkit-box-shadow: 0px 0px 15px 0px rgb(100 100 100 / 75%);
|
||||
box-shadow: 0px 0px 15px 0px rgb(100 100 100 / 75%);
|
||||
-webkit-box-shadow: 0px 0px 15px 0px rgb(100 100 100 / 75%);
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clickable:hover,
|
||||
.plyr .plyr__control:hover,
|
||||
button:hover,
|
||||
.action:hover,
|
||||
.listing-item.drag-hover {
|
||||
box-shadow: inset 0 -3em 3em rgba(217, 217, 217, 0.211), 0 0 0 2px var(--alt-background) !important;
|
||||
/* Adjust shadow values as needed */
|
||||
transform: scale(1.02);
|
||||
/* Slightly enlarges the element */
|
||||
box-shadow:
|
||||
inset 0 -3em 3em rgba(217, 217, 217, 0.211),
|
||||
0 0 0 2px var(--alt-background) !important;
|
||||
/* Adjust shadow values as needed */
|
||||
transform: scale(1.02);
|
||||
/* Slightly enlarges the element */
|
||||
}
|
||||
|
||||
.flat-right {
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
.flat-left {
|
||||
border-top-left-radius: 0 !important;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
}
|
||||
|
||||
.form-grow {
|
||||
flex-grow: 1;
|
||||
width: inherit;
|
||||
min-width: 1em;
|
||||
flex-grow: 1;
|
||||
width: inherit;
|
||||
min-width: 1em;
|
||||
}
|
||||
|
||||
.form-flex-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-flex-group > .form-compact {
|
||||
min-height: 2em;
|
||||
width: unset !important;
|
||||
min-height: 2em;
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
.form-button {
|
||||
height: auto !important
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.form-invalid {
|
||||
border-color: red !important;
|
||||
border-color: red !important;
|
||||
}
|
||||
|
||||
.form-compact {
|
||||
padding: 0em;
|
||||
padding: 0em;
|
||||
}
|
||||
|
||||
.form-dropdown {
|
||||
width: unset !important;
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-color: var(--divider);
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.tooltip-close-button {
|
||||
max-width: 4em !important;
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "اكتمل المسح",
|
||||
"showMore": "اعرض المزيد",
|
||||
"showLess": "اعرض أقل",
|
||||
"openParentFolder": "فتح المجلد الأصل"
|
||||
"openParentFolder": "فتح المجلد الأصل",
|
||||
"selectedCount": "عدد العناصر المحددة لتنفيذ إجراء ما"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "حدد الملف أو المجلد",
|
||||
"ctrl": {
|
||||
"click": "حدد أكثر من ملف او مجلد",
|
||||
"f": "إبدأ البحث",
|
||||
"s": "حمل الملف او المجلد في هذا المكان"
|
||||
"d": "تنزيل عناصر مختارة"
|
||||
},
|
||||
"del": "حذف البيانات المحددة",
|
||||
"doubleClick": "فتح المجلد او الملف",
|
||||
"esc": "مسح التحديد وإغلاق النافذة المنبثقة",
|
||||
"f1": "هذه المعلومات",
|
||||
"f2": "إعادة تسمية الملف",
|
||||
"help": "مساعدة"
|
||||
"help": "مساعدة",
|
||||
"f1": "إظهار موجه المساعدة",
|
||||
"description": "يمكنك عرض خيارات التنقل الأساسية أدناه. للحصول على معلومات إضافية، يرجى زيارة",
|
||||
"wiki": "ويكي متصفح الملفات الكمي"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "نسخ",
|
||||
"copyMessage": "رجاء حدد المكان لنسخ ملفاتك فيه:",
|
||||
"deleteMessageMultiple": "هل تريد بالتأكيد حذف {count} ملف؟",
|
||||
"deleteMessageSingle": "هل تريد بالتأكيد حذف هذا الملف/المجلد؟",
|
||||
"deleteTitle": "حذف الملفات",
|
||||
"displayName": "الإسم:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "حدد إمتداد الملف المراد تحميله.",
|
||||
"error": "لقد حدث خطأ ما",
|
||||
"fileInfo": "معلومات الملف",
|
||||
"lastModified": "آخر تعديل{suffix}",
|
||||
"move": "نقل",
|
||||
"moveMessage": "إختر مكان جديد للملفات أو المجلدات المراد نقلها:",
|
||||
"newArchetype": "إنشاء منشور من المنشور الأصلي. الملف سيتم انشاءه في مجلد المحتويات.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "رجاء أدخل اسم المجلد الجديد.",
|
||||
"newFile": "ملف جديد",
|
||||
"newFileMessage": "رجاء ادخل اسم الملف الجديد.",
|
||||
"numberDirs": "عدد المجلدات{suffix}",
|
||||
"numberFiles": "عدد الملفات{suffix}",
|
||||
"rename": "إعادة تسمية",
|
||||
"renameMessage": "إدراج اسم جديد لـ",
|
||||
"replace": "إستبدال",
|
||||
"replaceMessage": "أحد الملفات التي تحاول رفعها يتعارض مع ملف موجود بنفس الإسم. هل تريد إستبدال الملف الموجود؟\n",
|
||||
"schedule": "جدولة",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "عرض",
|
||||
"size": "الحجم",
|
||||
"upload": "التحميل",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "هل أنت متأكد من رغبتك في حذف هذا المستخدم؟",
|
||||
"filesSelected": "الملفات المحددة",
|
||||
"optionalPassword": "كلمة مرور اختيارية",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "سحب الملفات وإفلاتها هنا",
|
||||
"completed": "مكتمل",
|
||||
"conflictsDetected": "التعارضات المكتشفة",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "الاسم المقدم غير صالح: لا يمكن أن يحتوي على \"/\" أو \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "حد عدد التنزيلات",
|
||||
"maxBandwidth": "أقصى نطاق ترددي للتنزيل (كيلو بايت/ثانية)",
|
||||
"shareTheme": "سمة المشاركة",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "مشاركة عنوان URL للشعار",
|
||||
"shareFavicon": "مشاركة عنوان URL المفضل",
|
||||
"optionalPasswordHelp": "إذا تم تعيينها، يجب على المستخدمين إدخال كلمة المرور هذه لعرض المحتوى المشترك.",
|
||||
"viewMode": "وضع العرض"
|
||||
"viewMode": "وضع العرض",
|
||||
"renameMessage": "أدخل اسماً جديداً:",
|
||||
"renameMessageInvalid": "لا يمكن أن يحتوي الملف على \"/\" أو \"\\\"",
|
||||
"source": "المصدر{suffix}",
|
||||
"deleteMessageMultiple": "هل تريد بالتأكيد حذف الملف (الملفات) {count} ؟",
|
||||
"deleteMessageShare": "هل أنت متأكد أنك تريد حذف هذه المشاركة؟ {path} ?",
|
||||
"lastModified": "آخر تعديل{suffix}",
|
||||
"numberDirs": "عدد الدلائل{suffix}",
|
||||
"numberFiles": "عدد الملفات{suffix}",
|
||||
"renameMessageConflict": "عنصر باسم \"{filename}\" موجود بالفعل!",
|
||||
"uploadSettingsChunked": "الحد الأقصى للتحميلات المتزامنة: {maxConcurrentUpload} ، حجم القطعة: {uploadChunkSizeMb} ميغابايت",
|
||||
"uploadSettingsNoChunk": "الحد الأقصى للتحميلات المتزامنة: {maxConcurrentUpload} ، تم تعطيل التحميل المقطّع"
|
||||
},
|
||||
"search": {
|
||||
"images": "الصور",
|
||||
"music": "الموسيقى",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Press enter to search...",
|
||||
"search": "البحث...",
|
||||
"typeToSearch": "Type to search...",
|
||||
"types": "الأنواع",
|
||||
"video": "فيديوهات",
|
||||
"path": "المسار:",
|
||||
"smallerThan": "أصغر من:",
|
||||
"largerThan": "أكبر من:",
|
||||
"helpText1": "يحدث البحث على كل حرف تكتبه (3 أحرف كحد أدنى لمصطلحات البحث).",
|
||||
"helpText2": "الفهرس: يستخدم البحث الفهرس الذي يتم تحديثه تلقائيًا على الفاصل الزمني الذي تم تكوينه (افتراضيًا: 5 دقائق). قد يؤدي البحث عندما يكون البرنامج قد بدأ للتو إلى نتائج غير مكتملة."
|
||||
"helpText2": "الفهرس: يستخدم البحث الفهرس الذي يتم تحديثه تلقائيًا على الفاصل الزمني الذي تم تكوينه (افتراضيًا: 5 دقائق). قد يؤدي البحث عندما يكون البرنامج قد بدأ للتو إلى نتائج غير مكتملة.",
|
||||
"noResults": "لم يتم العثور على نتائج في البحث المفهرس.",
|
||||
"onlyFolders": "المجلدات فقط",
|
||||
"onlyFiles": "الملفات فقط",
|
||||
"showOptions": "عرض الخيارات",
|
||||
"photos": "الصور",
|
||||
"audio": "الصوت",
|
||||
"videos": "مقاطع الفيديو",
|
||||
"documents": "المستندات",
|
||||
"archives": "المحفوظات",
|
||||
"number": "العدد",
|
||||
"searchContext": "سياق البحث: {context}",
|
||||
"notEnoughCharacters": "أحرف غير كافية للبحث (الحد الأدنى {minSearchLength})",
|
||||
"typeToSearch": "ابدأ الكتابة {minSearchLength} أو أكثر لبدء البحث."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(أتركه فارغاً إن لم ترد تغييره)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Branding directory path",
|
||||
"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}.",
|
||||
"changePassword": "تغيير كلمة المرور",
|
||||
"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}.",
|
||||
"commandsUpdated": "تم تحديث الأوامر",
|
||||
"createUserDir": "Auto create user home dir while adding new user",
|
||||
"customStylesheet": "ستايل مخصص",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "خيارات المستخدم المسؤول",
|
||||
"shareDuration": "تنتهي صلاحيتها",
|
||||
"searchOptions": "خيارات البحث",
|
||||
"downloads": "التنزيلات"
|
||||
"downloads": "التنزيلات",
|
||||
"systemAdmin": "النظام والإدارة",
|
||||
"configViewer": "عارض التكوين",
|
||||
"configViewerShowFull": "إظهار التكوين الكامل",
|
||||
"configViewerShowComments": "إظهار التعليقات",
|
||||
"configViewerLoadConfig": "تكوين التحميل",
|
||||
"brandingHelp": "يمكنك تخصيص شكل ومظهر مثيل متصفح الملفات الخاص بك عن طريق تغيير اسمه، واستبدال الشعار، وإضافة أنماط مخصصة، وحتى تعطيل الروابط الخارجية إلى GitHub.\nلمزيد من المعلومات حول العلامة التجارية المخصصة، يرجى الاطلاع على {0}.",
|
||||
"commandRunnerHelp": "هنا يمكنك تعيين الأوامر التي يتم تنفيذها في الأحداث المسماة. يجب عليك كتابة أمر واحد لكل سطر. سيكون متغيرا البيئة {0} و {1} متاحين، حيث {0} بالنسبة إلى {1}. لمزيد من المعلومات حول هذه الميزة ومتغيرات البيئة المتاحة، يرجى قراءة {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "مساعدة",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "إخفاء أزرار التنقل",
|
||||
"hideNavButtonsDescription": "قم بإخفاء أزرار التنقل على شريط التنقل في المشاركة لإضفاء مظهر بسيط.",
|
||||
"viewModeDescription": "التخطيط الافتراضي للصفحة المشتركة.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "تم إرسال مشاركة إليك لعرضها أو تنزيلها.",
|
||||
"disableShareCard": "تعطيل بطاقة المشاركة المعطلة",
|
||||
"disableShareCardDescription": "قم بتعطيل بطاقة المشاركة على الصفحة المشتركة في الشريط الجانبي أو في أعلى الصفحة على الهاتف المحمول.",
|
||||
"disableSidebar": "تعطيل الشريط الجانبي",
|
||||
"disableSidebarDescription": "تعطيل الشريط الجانبي في الصفحة المشتركة."
|
||||
"disableSidebarDescription": "تعطيل الشريط الجانبي في الصفحة المشتركة.",
|
||||
"enforceDarkLightMode": "فرض وضع السمة",
|
||||
"enforceDarkLightModeDescription": "فرض وضع سمة معينة (داكنة أو فاتحة) لهذه المشاركة، وتجاوز تفضيلات المستخدم.",
|
||||
"default": "لا تفرض وضعاً",
|
||||
"dark": "داكن",
|
||||
"light": "خفيف",
|
||||
"enableOnlyOffice": "تمكين عارض OnlyOffice فقط",
|
||||
"enableOnlyOfficeDescription": "السماح بعرض ملفات المكتب باستخدام OnlyOffice في هذه المشاركة.",
|
||||
"enableOnlyOfficeEditing": "تمكين التحرير في المكتب فقط",
|
||||
"enableOnlyOfficeEditingDescription": "السماح بتحرير الملفات المكتبية باستخدام OnlyOffice في هذه الحصة.",
|
||||
"titleDefault": "الملفات المشتركة - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "مفاتيح واجهة برمجة التطبيقات (API):",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "مرفوض",
|
||||
"totalAllowed": "المسموح به",
|
||||
"all": "رفض الكل",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "يوضح هذا ما يحدث عندما يحاول المستخدم الوصول إلى مسار لا توجد فيه قواعد وصول محددة. تعني \"السماح\" أنه يمكن للمستخدمين الوصول بشكل افتراضي، وتعني \"الرفض\" أنه يتم حظر المستخدمين بشكل افتراضي ما لم يُسمح لهم صراحةً."
|
||||
"defaultBehaviorDescription": "يوضح هذا ما يحدث عندما يحاول المستخدم الوصول إلى مسار لا توجد فيه قواعد وصول محددة. تعني \"السماح\" أنه يمكن للمستخدمين الوصول بشكل افتراضي، وتعني \"الرفض\" أنه يتم حظر المستخدمين بشكل افتراضي ما لم يُسمح لهم صراحةً.",
|
||||
"defaultBehavior": "الإجراء الافتراضي{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "التحميلات والتنزيلات",
|
||||
@@ -506,7 +533,13 @@
|
||||
"defaultThemeDescription": "افتراضي - السمة الافتراضية",
|
||||
"customTheme": "اختر المظهر الخاص بك",
|
||||
"showSelectMultiple": "إظهار تحديد متعدد في قائمة السياق على سطح المكتب",
|
||||
"showSelectMultipleDescription": "بشكل افتراضي، لا تحتوي قائمة سياق سطح المكتب على خيار \"تحديد متعدد\" مثل الهاتف المحمول. يظهر هذا الخيار دائمًا في قائمة السياق."
|
||||
"showSelectMultipleDescription": "بشكل افتراضي، لا تحتوي قائمة سياق سطح المكتب على خيار \"تحديد متعدد\" مثل الهاتف المحمول. يظهر هذا الخيار دائمًا في قائمة السياق.",
|
||||
"defaultMediaPlayer": "استخدم مشغل الوسائط الأصلي في متصفحك",
|
||||
"defaultMediaPlayerDescription": "استخدم مشغل الوسائط الأصلي في متصفحك لملفات الفيديو والصوت بدلاً من مشغل الوسائط \"vue-plyr\" المضمن في متصفح FileBrowser.",
|
||||
"debugOfficeEditor": "تمكين وضع التصحيح فقط",
|
||||
"debugOfficeEditorDescription": "إظهار نافذة منبثقة للتصحيح مع معلومات إضافية في محرر OnlyOffice.",
|
||||
"fileViewerOptions": "خيارات عارض الملفات",
|
||||
"themeAndLanguage": "الموضوع واللغة"
|
||||
},
|
||||
"colors": {
|
||||
"red": "أحمر",
|
||||
@@ -521,7 +554,11 @@
|
||||
"saveFailed": "فشل حفظ الملف. يرجى المحاولة مرة أخرى.",
|
||||
"saveAborted": "تم إجهاض الحفظ. اسم الملف غير مطابق للملف النشط.",
|
||||
"saveDisabled": "حفظ الملفات المشتركة غير مدعوم.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "تم إجهاض عملية الحفظ. الملف النشط للتطبيق ({activeFile}) لا يتطابق مع الملف الذي تحاول حفظه ({tryingToSave}).",
|
||||
"syncFailed": "فشل مزامنة الحالة مع مسار \"{filename}\" بعد 5 محاولات. إجهاض إعداد المحرر لمنع تلف البيانات."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "تم تمكين الحلقة",
|
||||
"LoopDisabled": "حلقة معطلة"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Vymazání dokončeno",
|
||||
"showMore": "Zobrazit více",
|
||||
"showLess": "Zobrazit méně",
|
||||
"openParentFolder": "Otevření nadřazené složky"
|
||||
"openParentFolder": "Otevření nadřazené složky",
|
||||
"selectedCount": "Počet položek vybraných k provedení akce"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stáhnout soubor",
|
||||
@@ -87,15 +88,15 @@
|
||||
"click": "označit soubor nebo složku",
|
||||
"ctrl": {
|
||||
"click": "označit více souborů nebo složek",
|
||||
"f": "otevře hledání",
|
||||
"s": "uložit soubor nebo stáhnout aktuální složku"
|
||||
"d": "stáhnout vybrané položky"
|
||||
},
|
||||
"del": "smazat označené položky",
|
||||
"doubleClick": "otevřít soubor nebo složku",
|
||||
"esc": "zrušit označení a/nebo zavřít dialog",
|
||||
"f1": "tato nápověda",
|
||||
"f2": "přejmenovat soubor",
|
||||
"help": "Nápověda"
|
||||
"help": "Nápověda",
|
||||
"f1": "zobrazit výzvu nápovědy",
|
||||
"description": "Základní možnosti navigace si můžete prohlédnout níže. Další informace naleznete na adrese",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -161,19 +162,26 @@
|
||||
"hideNavButtons": "Skrytí navigačních tlačítek",
|
||||
"hideNavButtonsDescription": "Skryjte navigační tlačítka na panelu navigace ve sdíleném okně, abyste vytvořili minimalistický vzhled.",
|
||||
"viewModeDescription": "Výchozí rozložení sdílené stránky.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Byl vám zaslán podíl, který si můžete prohlédnout nebo stáhnout.",
|
||||
"disableShareCard": "Zakázat sdílenou kartu",
|
||||
"disableShareCardDescription": "Zakázat kartu sdílení na sdílené stránce v postranním panelu nebo v horní části stránky na mobilním telefonu.",
|
||||
"disableSidebar": "Zakázat postranní panel",
|
||||
"disableSidebarDescription": "Zakázat postranní panel na sdílené stránce."
|
||||
"disableSidebarDescription": "Zakázat postranní panel na sdílené stránce.",
|
||||
"enforceDarkLightMode": "Vynucení režimu tématu",
|
||||
"enforceDarkLightModeDescription": "Vynucení určitého režimu motivu (tmavého nebo světlého) pro tuto sdílenou složku, který je nadřazen uživatelským preferencím.",
|
||||
"default": "Nevynucujte režim",
|
||||
"dark": "Dark",
|
||||
"light": "Světlo",
|
||||
"enableOnlyOffice": "Povolení prohlížeče OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Povolit prohlížení kancelářských souborů pomocí OnlyOffice v této sdílené složce.",
|
||||
"enableOnlyOfficeEditing": "Povolení úprav OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Povolit úpravy kancelářských souborů pomocí OnlyOffice v této sdílené složce.",
|
||||
"titleDefault": "Sdílené soubory - {title}"
|
||||
},
|
||||
"prompts": {
|
||||
"copy": "Kopírovat",
|
||||
"copyMessage": "Vyberte místo, kam chcete soubory kopírovat:",
|
||||
"deleteMessageMultiple": "Opravdu chcete smazat {count} soubor(ů)?",
|
||||
"deleteMessageSingle": "Opravdu chcete smazat tento soubor/složku?",
|
||||
"deleteMessageShare": "Opravdu chcete smazat toto sdílení: {path} ?",
|
||||
"deleteUserMessage": "Opravdu chcete tohoto uživatele smazat?",
|
||||
"deleteTitle": "Smazat soubory",
|
||||
"displayName": "Zobrazované jméno:",
|
||||
@@ -183,7 +191,6 @@
|
||||
"fileInfo": "Informace o souboru",
|
||||
"selected": "označeno",
|
||||
"filesSelected": "souborů vybráno",
|
||||
"lastModified": "Poslední změna",
|
||||
"move": "Přesunout",
|
||||
"moveMessage": "Vyberte nové umístění pro soubor(y)/složku(y):",
|
||||
"newArchetype": "Vytvořit nový příspěvek podle archetypu. Váš soubor bude vytvořen ve složce content.",
|
||||
@@ -191,10 +198,7 @@
|
||||
"newDirMessage": "Zadejte název nové složky.",
|
||||
"newFile": "Nový soubor",
|
||||
"newFileMessage": "Zadejte název nového souboru.",
|
||||
"numberDirs": "Počet složek{suffix}",
|
||||
"numberFiles": "Počet souborů{suffix}",
|
||||
"rename": "Přejmenovat",
|
||||
"renameMessage": "Zadejte nový název pro",
|
||||
"replace": "Nahradit",
|
||||
"replaceMessage": "Jeden ze souborů, které se snažíte nahrát, má konflikt kvůli stejnému názvu. Chcete pokračovat v nahrávání nebo nahradit existující?",
|
||||
"schedule": "Naplánovat",
|
||||
@@ -209,10 +213,6 @@
|
||||
"dragAndDrop": "Přetáhněte sem soubory",
|
||||
"completed": "Dokončeno",
|
||||
"conflictsDetected": "Zjištěné konflikty",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nesprávný zadaný název: nelze použít kontejner \"/\" nebo \"\\\".",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limit počtu stažení",
|
||||
"maxBandwidth": "Maximální šířka pásma stahování (kbps)",
|
||||
"shareTheme": "Sdílet téma",
|
||||
@@ -223,22 +223,44 @@
|
||||
"shareBanner": "Sdílet adresu URL banneru",
|
||||
"shareFavicon": "Sdílet adresu URL favicon",
|
||||
"optionalPasswordHelp": "Pokud je nastaveno, musí uživatelé toto heslo zadat, aby mohli sdílený obsah zobrazit.",
|
||||
"viewMode": "Režim zobrazení"
|
||||
"viewMode": "Režim zobrazení",
|
||||
"renameMessage": "Zadejte nový název:",
|
||||
"renameMessageInvalid": "Soubor nemůže obsahovat \"/\" nebo \"\\\".",
|
||||
"source": "Zdroj:{suffix}",
|
||||
"deleteMessageMultiple": "Opravdu chcete odstranit soubor(y) {count}?",
|
||||
"deleteMessageShare": "Jste si jisti, že chcete tuto sdílenou složku odstranit: {path} ?",
|
||||
"lastModified": "Poslední úprava{suffix}",
|
||||
"numberDirs": "Počet adresářů{suffix}",
|
||||
"numberFiles": "Počet souborů{suffix}",
|
||||
"renameMessageConflict": "Položka s názvem \"{filename}\" již existuje!",
|
||||
"uploadSettingsChunked": "Maximální souběžné nahrávání: {maxConcurrentUpload}, velikost chunků: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Maximální souběžné nahrávání: {maxConcurrentUpload}, vypnuto hromadné nahrávání"
|
||||
},
|
||||
"search": {
|
||||
"images": "Obrázky",
|
||||
"music": "Hudba",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Ve vyhledávání s indexem nebyly nalezeny žádné výsledky.",
|
||||
"search": "Hledat...",
|
||||
"typeToSearch": "Začněte psát pro hledání... (minimálně 3 znaky)",
|
||||
"path": "Cesta:",
|
||||
"types": "Typy",
|
||||
"video": "Video",
|
||||
"smallerThan": "Menší než:",
|
||||
"largerThan": "Větší než:",
|
||||
"helpText1": "Vyhledávání probíhá po každém napsaném znaku (minimálně 3 znaky pro hledaný výraz).",
|
||||
"helpText2": "Index: Vyhledávání využívá index, který se automaticky aktualizuje v nastaveném intervalu (výchozí: 5 minut). Hledání ihned po spuštění programu může vracet neúplné výsledky."
|
||||
"helpText2": "Index: Vyhledávání využívá index, který se automaticky aktualizuje v nastaveném intervalu (výchozí: 5 minut). Hledání ihned po spuštění programu může vracet neúplné výsledky.",
|
||||
"noResults": "V indexovaném vyhledávání nebyly nalezeny žádné výsledky.",
|
||||
"onlyFolders": "Pouze složky",
|
||||
"onlyFiles": "Pouze soubory",
|
||||
"showOptions": "Zobrazit možnosti",
|
||||
"photos": "Fotografie",
|
||||
"audio": "Audio",
|
||||
"videos": "Videa",
|
||||
"documents": "Dokumenty",
|
||||
"archives": "Archivy",
|
||||
"number": "Číslo",
|
||||
"searchContext": "Kontext vyhledávání: {context}",
|
||||
"notEnoughCharacters": "Nedostatek znaků pro vyhledávání (min. {minSearchLength})",
|
||||
"typeToSearch": "Pro zahájení vyhledávání začněte psát {minSearchLength} nebo více znaků."
|
||||
},
|
||||
"profileSettings": {
|
||||
"setDateFormat": "Nastavit formát data",
|
||||
@@ -289,7 +311,13 @@
|
||||
"defaultThemeDescription": "default - Výchozí téma",
|
||||
"customTheme": "Vyberte si téma",
|
||||
"showSelectMultiple": "Zobrazení výběru více položek v kontextové nabídce na ploše",
|
||||
"showSelectMultipleDescription": "Ve výchozím nastavení nemá kontextová nabídka na ploše možnost \"vybrat více\" jako v mobilním telefonu. Tato možnost se v kontextové nabídce zobrazuje vždy."
|
||||
"showSelectMultipleDescription": "Ve výchozím nastavení nemá kontextová nabídka na ploše možnost \"vybrat více\" jako v mobilním telefonu. Tato možnost se v kontextové nabídce zobrazuje vždy.",
|
||||
"defaultMediaPlayer": "Použití nativního přehrávače médií v prohlížeči",
|
||||
"defaultMediaPlayerDescription": "Pro video a zvukové soubory používejte nativní přehrávač médií prohlížeče, nikoli přehrávač médií vue-plyr, který je součástí aplikace FileBrowser.",
|
||||
"debugOfficeEditor": "Povolení režimu ladění OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Zobrazení vyskakovacího okna pro ladění s dalšími informacemi v editoru OnlyOffice.",
|
||||
"fileViewerOptions": "Možnosti prohlížeče souborů",
|
||||
"themeAndLanguage": "Téma a jazyk"
|
||||
},
|
||||
"settings": {
|
||||
"enterPassword": "Zadejte heslo",
|
||||
@@ -316,10 +344,8 @@
|
||||
"avoidChanges": "Zaškrtněte také 'změnit heslo' pro použití tohoto hesla",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Cesta k adresáři s brandingem",
|
||||
"brandingHelp": "Můžete si upravit vzhled File Browseru změnou názvu, loga, přidáním vlastních stylů nebo zakázáním externích odkazů na GitHub.\nPro více informací o úpravách vzhledu navštivte {0}.",
|
||||
"changePassword": "Změnit heslo",
|
||||
"commandRunner": "Spouštěč příkazů",
|
||||
"commandRunnerHelp": "Zde můžete nastavit příkazy, které se budou spouštět při vybraných událostech. Každý příkaz pište na nový řádek. Proměnné prostředí {0} a {1} budou dostupné; {0} je relativní k {1}. Pro více informací o této funkci a dostupných proměnných si přečtěte {2}.",
|
||||
"commandsUpdated": "Příkazy aktualizovány!",
|
||||
"createUserDir": "Automaticky vytvářet domovský adresář uživatele při přidání nového uživatele",
|
||||
"tusUploads": "Dávkové nahrávání",
|
||||
@@ -388,7 +414,14 @@
|
||||
"username": "Uživatelské jméno",
|
||||
"users": "Uživatelé",
|
||||
"adminOptions": "Možnosti administrátora",
|
||||
"downloads": "Ke stažení na"
|
||||
"downloads": "Ke stažení na",
|
||||
"systemAdmin": "Systém a správce",
|
||||
"configViewer": "Prohlížeč konfigurace",
|
||||
"configViewerShowFull": "Zobrazit úplnou konfiguraci",
|
||||
"configViewerShowComments": "Zobrazit komentáře",
|
||||
"configViewerLoadConfig": "Konfigurace načítání",
|
||||
"brandingHelp": "Vzhled instance Průzkumníka souborů si můžete přizpůsobit změnou názvu, nahrazením loga, přidáním vlastních stylů a dokonce i zakázáním externích odkazů na GitHub.\nDalší informace o vlastním brandingu naleznete na stránce {0}.",
|
||||
"commandRunnerHelp": "Zde můžete nastavit příkazy, které se provedou v pojmenovaných událostech. Na každý řádek musíte napsat jeden. K dispozici budou proměnné prostředí {0} a {1}, přičemž {0} bude relativní vůči {1}. Další informace o této funkci a dostupných proměnných prostředí naleznete na {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Nápověda",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "Název",
|
||||
"accessManagement": "Správa přístupu",
|
||||
"all": "Odmítnout vše",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "To ukazuje, co se stane, když se uživatel pokusí získat přístup k cestě, kde neexistují žádná specifická pravidla přístupu. \"Povolit\" znamená, že uživatelé mají ve výchozím nastavení přístup, \"Odmítnout\" znamená, že uživatelé jsou ve výchozím nastavení blokováni, pokud není výslovně povoleno."
|
||||
"defaultBehaviorDescription": "To ukazuje, co se stane, když se uživatel pokusí získat přístup k cestě, kde neexistují žádná specifická pravidla přístupu. \"Povolit\" znamená, že uživatelé mají ve výchozím nastavení přístup, \"Odmítnout\" znamená, že uživatelé jsou ve výchozím nastavení blokováni, pokud není výslovně povoleno.",
|
||||
"defaultBehavior": "Výchozí akce{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Nepodařilo se inicializovat editor. Prosím, načtěte jej znovu.",
|
||||
"saveFailed": "Soubor se nepodařilo uložit. Zkuste to prosím znovu.",
|
||||
"saveAborted": "Uložení přerušeno. Název souboru neodpovídá aktivnímu souboru.",
|
||||
"saveDisabled": "Ukládání sdílených souborů není podporováno.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operace ukládání byla přerušena. Aktivní soubor aplikace ({activeFile}) neodpovídá souboru, který se pokoušíte uložit ({tryingToSave}).",
|
||||
"syncFailed": "po 5 pokusech se nepodařilo synchronizovat stav s trasou pro \"{filename}\". Přerušení nastavení editoru, aby se zabránilo poškození dat."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Povolená smyčka",
|
||||
"LoopDisabled": "Vypnutá smyčka"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Abgeschlossene löschen",
|
||||
"showMore": "Mehr anzeigen",
|
||||
"showLess": "Weniger anzeigen",
|
||||
"openParentFolder": "Übergeordneten Ordner öffnen"
|
||||
"openParentFolder": "Übergeordneten Ordner öffnen",
|
||||
"selectedCount": "Anzahl der zur Durchführung einer Aktion ausgewählten Elemente"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Datei herunterladen",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "Wähle Datei oder Ordner",
|
||||
"ctrl": {
|
||||
"click": "Markiere mehrere Dateien oder Ordner",
|
||||
"f": "Öffnet eine neue Suche",
|
||||
"s": "Speichert eine Datei oder einen Ordner am akutellen Ort"
|
||||
"d": "ausgewählte Artikel herunterladen"
|
||||
},
|
||||
"del": "Löscht die ausgewählten Elemente",
|
||||
"doubleClick": "Öffnet eine Datei oder einen Ordner",
|
||||
"esc": "Auswahl zurücksetzen und/oder Dialog schließen",
|
||||
"f1": "Diese Informationsseite",
|
||||
"f2": "Datei umbenennen",
|
||||
"help": "Hilfe"
|
||||
"help": "Hilfe",
|
||||
"f1": "Hilfeprompt anzeigen",
|
||||
"description": "Nachstehend finden Sie die grundlegenden Navigationsoptionen. Für zusätzliche Informationen besuchen Sie bitte",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Kopieren",
|
||||
"copyMessage": "Wählen Sie einen Zielort zum Kopieren:",
|
||||
"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?",
|
||||
"deleteMessageShare": "Sind Sie sicher, dass Sie diese Freigabe löschen möchten ({path})?",
|
||||
"deleteTitle": "Dateien löschen",
|
||||
"displayName": "Anzeigename:",
|
||||
"download": "Dateien herunterladen",
|
||||
"downloadMessage": "Wählen Sie ein Format zum Herunterladen aus.",
|
||||
"error": "Etwas ist schief gelaufen",
|
||||
"fileInfo": "Dateiinformation",
|
||||
"lastModified": "Zuletzt geändert{suffix}",
|
||||
"move": "Verschieben",
|
||||
"moveMessage": "Wählen Sie einen neuen Ort für ihre Datei(en)/Ordner:",
|
||||
"newArchetype": "Erstelle neuen Beitrag auf Basis eines Archetyps. Ihre Datei wird im Inhalteordner erstellt.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Geben Sie den Namen des neuen Ordners an.",
|
||||
"newFile": "Neue Datei",
|
||||
"newFileMessage": "Geben Sie den Namen der neuen Datei an.",
|
||||
"numberDirs": "Anzahl der Ordner{suffix}",
|
||||
"numberFiles": "Anzahl der Dateien{suffix}",
|
||||
"rename": "Umbenennen",
|
||||
"renameMessage": "Geben Sie einen Namen ein für",
|
||||
"replace": "Ersetzen",
|
||||
"replaceMessage": "Eine der Datei mit dem gleichen Namen, wie die Sie hochladen wollen, existiert bereits. Soll die vorhandene Datei übersprungen oder ersetzt werden?\n",
|
||||
"schedule": "Zeitplan",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Dateien hierher ziehen und ablegen",
|
||||
"completed": "Abgeschlossen",
|
||||
"conflictsDetected": "Erkannte Widersprüche",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Ungültiger Name angegeben: kann nicht Container \"/\" oder \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limit für die Anzahl der Downloads",
|
||||
"maxBandwidth": "Maximale Download-Bandbreite (kbps)",
|
||||
"shareTheme": "Thema teilen",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Banner-URL freigeben",
|
||||
"shareFavicon": "Favicon-URL freigeben",
|
||||
"optionalPasswordHelp": "Falls festgelegt, müssen die Benutzer dieses Kennwort eingeben, um die freigegebenen Inhalte anzuzeigen.",
|
||||
"viewMode": "Ansichtsmodus"
|
||||
"viewMode": "Ansichtsmodus",
|
||||
"renameMessage": "Geben Sie einen neuen Namen ein:",
|
||||
"renameMessageInvalid": "Eine Datei kann nicht den Container \"/\" oder \"\\\" enthalten.",
|
||||
"source": "Quelle{suffix}",
|
||||
"deleteMessageMultiple": "Sind Sie sicher, dass Sie die Datei(en) {count} löschen möchten?",
|
||||
"deleteMessageShare": "Sind Sie sicher, dass Sie diese Freigabe löschen möchten: {path} ?",
|
||||
"lastModified": "Zuletzt modifiziert{suffix}",
|
||||
"numberDirs": "Anzahl der Verzeichnisse{suffix}",
|
||||
"numberFiles": "Anzahl der Dateien{suffix}",
|
||||
"renameMessageConflict": "Ein Artikel mit dem Namen \"{filename}\" existiert bereits!",
|
||||
"uploadSettingsChunked": "Max. gleichzeitige Uploads: {maxConcurrentUpload}, Chunk-Größe: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max. gleichzeitige Uploads: {maxConcurrentUpload}, Chunked Upload deaktiviert"
|
||||
},
|
||||
"search": {
|
||||
"images": "Bilder",
|
||||
"music": "Musik",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Drücken Sie Enter, um zu suchen...",
|
||||
"search": "Suche...",
|
||||
"typeToSearch": "Tippen, um zu suchen...",
|
||||
"types": "Typen",
|
||||
"video": "Video",
|
||||
"path": "Pfad:",
|
||||
"smallerThan": "Kleiner als:",
|
||||
"largerThan": "Größer als:",
|
||||
"helpText1": "Die Suche erfolgt bei jedem Zeichen, das Sie eingeben (mindestens 3 Zeichen für Suchbegriffe).",
|
||||
"helpText2": "Der Index: Die Suche verwendet den Index, der automatisch in dem konfigurierten Intervall aktualisiert wird (Standard: 5 Minuten). Eine Suche direkt nach dem Start des Programms kann zu unvollständigen Ergebnissen führen."
|
||||
"helpText2": "Der Index: Die Suche verwendet den Index, der automatisch in dem konfigurierten Intervall aktualisiert wird (Standard: 5 Minuten). Eine Suche direkt nach dem Start des Programms kann zu unvollständigen Ergebnissen führen.",
|
||||
"noResults": "Keine Ergebnisse in der indizierten Suche gefunden.",
|
||||
"onlyFolders": "Nur Ordner",
|
||||
"onlyFiles": "Nur Dateien",
|
||||
"showOptions": "Optionen anzeigen",
|
||||
"photos": "Fotos",
|
||||
"audio": "Audio",
|
||||
"videos": "Videos",
|
||||
"documents": "Dokumente",
|
||||
"archives": "Archiv",
|
||||
"number": "Nummer",
|
||||
"searchContext": "Suche im Kontext: {context}",
|
||||
"notEnoughCharacters": "Nicht genügend Zeichen für die Suche (min {minSearchLength})",
|
||||
"typeToSearch": "Beginnen Sie mit der Eingabe von {minSearchLength} oder mehr Zeichen, um die Suche zu starten."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(leer lassen, um Änderungen zu vermeiden)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Branding-Verzeichnispfad",
|
||||
"brandingHelp": "Sie können das Erscheinungsbild Ihres FileBrowser anpassen, indem Sie den Namen ändern, das Logo austauschen oder eigene Branding definieren bzw. sogar externe Links zu GitHub deaktivieren.\nUm mehr Informationen zum Anpassen des Designs zu bekommen, gehen Sie bitte zu {0}.",
|
||||
"changePassword": "Kennwort ändern",
|
||||
"commandRunner": "Befehlseingabe",
|
||||
"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 Umgebungsvariablen {0} und {1} sind verfügbar, wobei {0} relativ zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen lesen Sie bitte die {2}.",
|
||||
"commandsUpdated": "Befehle aktualisiert!",
|
||||
"createUserDir": "Automatisches Erstellen des Home-Verzeichnisses beim Anlegen neuer Benutzer",
|
||||
"tusUploads": "Gestückeltes Hochladen",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Optionen für Verwaltungsbenutzer",
|
||||
"shareDuration": "Abgelaufen",
|
||||
"searchOptions": "Suchoptionen",
|
||||
"downloads": "Herunterladen"
|
||||
"downloads": "Herunterladen",
|
||||
"systemAdmin": "System & Verwaltung",
|
||||
"configViewer": "Config Viewer",
|
||||
"configViewerShowFull": "Vollständige Konfiguration anzeigen",
|
||||
"configViewerShowComments": "Kommentare anzeigen",
|
||||
"configViewerLoadConfig": "Lastkonfiguration",
|
||||
"brandingHelp": "Sie können das Aussehen Ihrer Dateibrowser-Instanz anpassen, indem Sie den Namen ändern, das Logo ersetzen, eigene Stile hinzufügen und sogar externe Links zu GitHub deaktivieren.\nWeitere Informationen zum benutzerdefinierten Branding finden Sie unter {0}.",
|
||||
"commandRunnerHelp": "Hier können Sie Befehle eingeben, die bei den genannten Ereignissen ausgeführt werden. Sie müssen einen pro Zeile schreiben. Die Umgebungsvariablen {0} und {1} werden verfügbar sein, wobei {0} relativ zu {1} ist. Weitere Informationen über diese Funktion und die verfügbaren Umgebungsvariablen finden Sie unter {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Hilfe",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Navigationsschaltflächen ausblenden",
|
||||
"hideNavButtonsDescription": "Blenden Sie die Navigationsschaltflächen auf der Navigationsleiste in der Freigabe aus, um ein minimalistisches Aussehen zu erzielen.",
|
||||
"viewModeDescription": "Standardlayout für die gemeinsame Seite.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Sie haben eine Aktie zur Ansicht oder zum Download erhalten.",
|
||||
"disableShareCard": "Freigabekarte deaktivieren",
|
||||
"disableShareCardDescription": "Deaktivieren Sie die Freigabekarte auf der freigegebenen Seite in der Seitenleiste oder oben auf der Seite auf dem Handy.",
|
||||
"disableSidebar": "Seitenleiste deaktivieren",
|
||||
"disableSidebarDescription": "Deaktivieren Sie die Seitenleiste auf der gemeinsamen Seite."
|
||||
"disableSidebarDescription": "Deaktivieren Sie die Seitenleiste auf der gemeinsamen Seite.",
|
||||
"enforceDarkLightMode": "Themenmodus erzwingen",
|
||||
"enforceDarkLightModeDescription": "Erzwingt einen bestimmten Themenmodus (dunkel oder hell) für diese Freigabe und setzt die Benutzereinstellungen außer Kraft.",
|
||||
"default": "Keinen Modus erzwingen",
|
||||
"dark": "Dunkelheit",
|
||||
"light": "Licht",
|
||||
"enableOnlyOffice": "OnlyOffice viewer aktivieren",
|
||||
"enableOnlyOfficeDescription": "Erlauben Sie die Anzeige von Office-Dateien mit OnlyOffice in dieser Freigabe.",
|
||||
"enableOnlyOfficeEditing": "OnlyOffice-Bearbeitung aktivieren",
|
||||
"enableOnlyOfficeEditingDescription": "Erlauben Sie die Bearbeitung von Office-Dateien mit OnlyOffice in dieser Freigabe.",
|
||||
"titleDefault": "Gemeinsame Dateien - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API-Schlüssel:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Verweigert",
|
||||
"totalAllowed": "Erlaubt",
|
||||
"all": "Alle ablehnen",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Dies zeigt, was passiert, wenn ein Benutzer versucht, auf einen Pfad zuzugreifen, für den keine spezifischen Zugriffsregeln bestehen. Zulassen\" bedeutet, dass Benutzer standardmäßig Zugang haben, \"Verweigern\" bedeutet, dass Benutzer standardmäßig blockiert werden, wenn sie nicht ausdrücklich zugelassen sind."
|
||||
"defaultBehaviorDescription": "Dies zeigt, was passiert, wenn ein Benutzer versucht, auf einen Pfad zuzugreifen, für den keine spezifischen Zugriffsregeln bestehen. Zulassen\" bedeutet, dass Benutzer standardmäßig Zugang haben, \"Verweigern\" bedeutet, dass Benutzer standardmäßig blockiert werden, wenn sie nicht ausdrücklich zugelassen sind.",
|
||||
"defaultBehavior": "Standard-Aktion{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Uploads & Downloads",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Das Standardthema",
|
||||
"customTheme": "Wählen Sie Ihr Thema",
|
||||
"showSelectMultiple": "Mehrfachauswahl im Kontextmenü auf dem Desktop anzeigen",
|
||||
"showSelectMultipleDescription": "Standardmäßig verfügt das Desktop-Kontextmenü nicht über die Option \"Mehrere auswählen\" wie bei mobilen Geräten. Diese Option wird immer im Kontextmenü angezeigt."
|
||||
"showSelectMultipleDescription": "Standardmäßig verfügt das Desktop-Kontextmenü nicht über die Option \"Mehrere auswählen\" wie bei mobilen Geräten. Diese Option wird immer im Kontextmenü angezeigt.",
|
||||
"defaultMediaPlayer": "Verwenden Sie den nativen Media Player Ihres Browsers",
|
||||
"defaultMediaPlayerDescription": "Verwenden Sie für Video- und Audiodateien den nativen Mediaplayer Ihres Browsers und nicht den in FileBrowser enthaltenen 'vue-plyr' Mediaplayer.",
|
||||
"debugOfficeEditor": "OnlyOffice-Debug-Modus einschalten",
|
||||
"debugOfficeEditorDescription": "Ein Debug-Popup mit zusätzlichen Informationen im OnlyOffice-Editor anzeigen.",
|
||||
"fileViewerOptions": "Optionen des Dateibetrachters",
|
||||
"themeAndLanguage": "Thema und Sprache"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Der Editor konnte nicht initialisiert werden. Bitte neu laden.",
|
||||
"saveFailed": "Datei konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.",
|
||||
"saveAborted": "Speichern abgebrochen. Der Dateiname stimmt nicht mit der aktiven Datei überein.",
|
||||
"saveDisabled": "Das Speichern von gemeinsam genutzten Dateien wird nicht unterstützt.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Der Speichervorgang wurde abgebrochen. Die aktive Datei der Anwendung ({activeFile}) stimmt nicht mit der Datei überein, die Sie zu speichern versuchen ({tryingToSave}).",
|
||||
"syncFailed": "Nach 5 Versuchen ist es nicht gelungen, den Status mit der Route für \"{filename}\" zu synchronisieren. Abbruch der Editor-Einrichtung, um Datenbeschädigung zu vermeiden."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Schleife Aktiviert",
|
||||
"LoopDisabled": "Schleife Deaktiviert"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Εκκαθάριση ολοκληρώθηκε",
|
||||
"showMore": "Εμφάνιση περισσότερων",
|
||||
"showLess": "Εμφάνιση λιγότερων",
|
||||
"openParentFolder": "Άνοιγμα γονικού φακέλου"
|
||||
"openParentFolder": "Άνοιγμα γονικού φακέλου",
|
||||
"selectedCount": "Αριθμός επιλεγμένων στοιχείων για την εκτέλεση μιας ενέργειας"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Λήψη αρχείου",
|
||||
@@ -87,15 +88,15 @@
|
||||
"click": "επιλέξτε αρχείο ή φάκελο",
|
||||
"ctrl": {
|
||||
"click": "επιλογή πολλαπλών αρχείων ή φακέλων",
|
||||
"f": "ανοίγει την αναζήτηση",
|
||||
"s": "αποθηκεύει ένα αρχείο ή εκκινεί λήψη του φακέλου στον οποίο βρίσκεστε"
|
||||
"d": "λήψη επιλεγμένων στοιχείων"
|
||||
},
|
||||
"del": "διαγραφή επιλεγμένων στοιχείων",
|
||||
"doubleClick": "ανοίγει ένα αρχείο ή φάκελο",
|
||||
"esc": "καθαρίζει την επιλογή ή/και κλείνει το παράθυρο",
|
||||
"f1": "αυτή η πληροφορία",
|
||||
"f2": "μετονομασία αρχείου",
|
||||
"help": "Βοήθεια"
|
||||
"help": "Βοήθεια",
|
||||
"f1": "Εμφάνιση προτροπής βοήθειας",
|
||||
"description": "Μπορείτε να δείτε τις βασικές επιλογές πλοήγησης παρακάτω. Για πρόσθετες πληροφορίες, επισκεφθείτε τη διεύθυνση",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -137,16 +138,13 @@
|
||||
"prompts": {
|
||||
"copy": "Αντιγραφή",
|
||||
"copyMessage": "Επιλέξτε τοποθεσία για αντιγραφή των αρχείων σας:",
|
||||
"deleteMessageMultiple": "Είστε σίγουροι ότι θέλετε να διαγράψετε {count} αρχεία;",
|
||||
"deleteMessageSingle": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο/φάκελο;",
|
||||
"deleteMessageShare": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτή την κοινοποίηση ({path});",
|
||||
"deleteTitle": "Διαγραφή αρχείων",
|
||||
"displayName": "Εμφάνιση ονόματος:",
|
||||
"download": "Λήψη αρχείων",
|
||||
"downloadMessage": "Επιλέξτε τη μορφή που θέλετε να λάβετε.",
|
||||
"error": "Προέκυψε κάποιο σφάλμα",
|
||||
"fileInfo": "Πληροφορίες αρχείου",
|
||||
"lastModified": "Τελευταία τροποποίηση{suffix}",
|
||||
"move": "Μετακίνηση",
|
||||
"moveMessage": "Επιλέξτε νέα τοποθεσία για τα αρχεία / τους φακέλους σας:",
|
||||
"newArchetype": "Δημιουργία νέας ανάρτησης με βάση έναν αρχέτυπο. Το αρχείο σας θα δημιουργηθεί στο φάκελο περιεχομένου.",
|
||||
@@ -154,10 +152,7 @@
|
||||
"newDirMessage": "Γράψτε το όνομα του νέου φακέλου.",
|
||||
"newFile": "Νέο αρχείο",
|
||||
"newFileMessage": "Γράψτε το όνομα του νέου αρχείου.",
|
||||
"numberDirs": "Αριθμός φακέλων{suffix}",
|
||||
"numberFiles": "Αριθμός αρχείων{suffix}",
|
||||
"rename": "Μετονομασία",
|
||||
"renameMessage": "Εισαγάγετε ένα νέο όνομα για το",
|
||||
"replace": "Αντικατάσταση",
|
||||
"replaceMessage": "Ένα από τα αρχεία που προσπαθείτε να μεταφορτώσετε δημιουργεί σύγκρουση με υπάρχον αρχείο λόγω του ονόματός του. Θέλετε να συνεχίσετε τη μεταφόρτωση ή να αντικαταστήσετε το υπάρχον;\n",
|
||||
"schedule": "Προγραμματισμός",
|
||||
@@ -175,10 +170,6 @@
|
||||
"dragAndDrop": "Σύρετε και αφήστε αρχεία εδώ",
|
||||
"completed": "Ολοκληρωμένο",
|
||||
"conflictsDetected": "Ανιχνεύονται συγκρούσεις",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Μη έγκυρο όνομα: δεν μπορεί να γίνει container \"/\" ή \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Όριο αριθμού λήψεων",
|
||||
"maxBandwidth": "Μέγιστο εύρος ζώνης λήψης (kbps)",
|
||||
"shareTheme": "Μοιραστείτε το θέμα",
|
||||
@@ -189,22 +180,44 @@
|
||||
"shareBanner": "Μοιραστείτε το URL του banner",
|
||||
"shareFavicon": "Μοιραστείτε το URL favicon",
|
||||
"optionalPasswordHelp": "Εάν οριστεί, οι χρήστες πρέπει να εισάγουν αυτόν τον κωδικό πρόσβασης για να προβάλουν το κοινόχρηστο περιεχόμενο.",
|
||||
"viewMode": "Λειτουργία προβολής"
|
||||
"viewMode": "Λειτουργία προβολής",
|
||||
"renameMessage": "Εισάγετε ένα νέο όνομα:",
|
||||
"renameMessageInvalid": "Ένα αρχείο δεν μπορεί να περιέχει \"/\" ή \"\\\"",
|
||||
"source": "Πηγή{suffix}",
|
||||
"deleteMessageMultiple": "Είστε σίγουροι ότι θέλετε να διαγράψετε το/τα αρχείο/α {count};",
|
||||
"deleteMessageShare": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το μερίδιο: {path} ?",
|
||||
"lastModified": "Τελευταία τροποποίηση{suffix}",
|
||||
"numberDirs": "Αριθμός καταλόγων{suffix}",
|
||||
"numberFiles": "Αριθμός αρχείων{suffix}",
|
||||
"renameMessageConflict": "Ένα στοιχείο με όνομα \"{filename}\" υπάρχει ήδη!",
|
||||
"uploadSettingsChunked": "Μέγιστες ταυτόχρονες μεταφορτώσεις: {maxConcurrentUpload}, μέγεθος τεμαχίου: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Μέγιστες ταυτόχρονες μεταφορτώσεις: {maxConcurrentUpload}, chunked upload disabled"
|
||||
},
|
||||
"search": {
|
||||
"images": "Εικόνες",
|
||||
"music": "Μουσική",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Πατήστε Enter για αναζήτηση…",
|
||||
"search": "Αναζήτηση…",
|
||||
"typeToSearch": "Πληκτρολογήστε για αναζήτηση…",
|
||||
"types": "Τύποι",
|
||||
"video": "Βίντεο",
|
||||
"path": "Μονοπάτι:",
|
||||
"smallerThan": "Μικρότερο από:",
|
||||
"largerThan": "Μεγαλύτερο από:",
|
||||
"helpText1": "Η αναζήτηση πραγματοποιείται σε κάθε χαρακτήρα που πληκτρολογείτε (ελάχιστο όριο 3 χαρακτήρων για τους όρους αναζήτησης).",
|
||||
"helpText2": "Ο δείκτης: Η αναζήτηση χρησιμοποιεί το ευρετήριο το οποίο ενημερώνεται αυτόματα στο ρυθμισμένο διάστημα (προεπιλογή: 5 λεπτά). Η αναζήτηση όταν το πρόγραμμα έχει μόλις ξεκινήσει μπορεί να οδηγήσει σε ελλιπή αποτελέσματα."
|
||||
"helpText2": "Ο δείκτης: Η αναζήτηση χρησιμοποιεί το ευρετήριο το οποίο ενημερώνεται αυτόματα στο ρυθμισμένο διάστημα (προεπιλογή: 5 λεπτά). Η αναζήτηση όταν το πρόγραμμα έχει μόλις ξεκινήσει μπορεί να οδηγήσει σε ελλιπή αποτελέσματα.",
|
||||
"noResults": "Δεν βρέθηκαν αποτελέσματα στην ευρετηριασμένη αναζήτηση.",
|
||||
"onlyFolders": "Μόνο φάκελοι",
|
||||
"onlyFiles": "Μόνο αρχεία",
|
||||
"showOptions": "Εμφάνιση επιλογών",
|
||||
"photos": "Φωτογραφίες",
|
||||
"audio": "Ήχος",
|
||||
"videos": "Βίντεο",
|
||||
"documents": "Έγγραφα",
|
||||
"archives": "Αρχεία",
|
||||
"number": "Αριθμός",
|
||||
"searchContext": "Πλαίσιο αναζήτησης: {context}",
|
||||
"notEnoughCharacters": "Δεν υπάρχουν αρκετοί χαρακτήρες για αναζήτηση (min {minSearchLength})",
|
||||
"typeToSearch": "Αρχίστε να πληκτρολογείτε {minSearchLength} ή περισσότερους χαρακτήρες για να ξεκινήσετε την αναζήτηση."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Διαχειριστής",
|
||||
@@ -217,10 +230,8 @@
|
||||
"avoidChanges": "(αφήστε το κενό για αποφυγή αλλαγών)",
|
||||
"branding": "Εξατομίκευση",
|
||||
"brandingDirectoryPath": "Διαδρομή φακέλου εξατομίκευσης",
|
||||
"brandingHelp": "Μπορείτε να προσαρμόσετε την εμφάνισης της εφαρμογής File Browser αλλάζοντας το όνομά της, αντικαθιστώντας το λογότυπό της, προσθέτοντας προσαρμοσμένα στυλ και ακόμα και απενεργοποιώντας εξωτερικούς συνδέσμους προς το GitHub.\nΓια περισσότερες πληροφορίες σχετικά με αυτές τις προσαρμογές, ελέγξτε το {0}.",
|
||||
"changePassword": "Αλλαγή κωδικού πρόσβασης",
|
||||
"commandRunner": "Εκτέλεση εντολών",
|
||||
"commandRunnerHelp": "Εδώ μπορείτε να ορίσετε εντολές που εκτελούνται στα ονομασμένα γεγονότα και δραστηριότητες. Πρέπει να γράψετε μία εντολή ανά γραμμή. Οι μεταβλητές περιβάλλοντος {0} και {1} θα είναι διαθέσιμες, και θα είναι {0} σχετικές με το {1}. Για περισσότερες πληροφορίες σχετικά με αυτή τη λειτουργία και τις διαθέσιμες μεταβλητές περιβάλλοντος, παρακαλώ διαβάστε το {2}.",
|
||||
"commandsUpdated": "Οι εντολές ενημερώθηκαν!",
|
||||
"createUserDir": "Αυτόματη δημιουργία φακέλου χρήστη κατά την προσθήκη νέου χρήστη",
|
||||
"tusUploads": "Τμηματικές μεταφορές αρχείων",
|
||||
@@ -303,7 +314,14 @@
|
||||
"adminOptions": "Επιλογές χρήστη διαχειριστή",
|
||||
"shareDuration": "Λήγει",
|
||||
"searchOptions": "Επιλογές αναζήτησης",
|
||||
"downloads": "Λήψεις"
|
||||
"downloads": "Λήψεις",
|
||||
"systemAdmin": "Σύστημα & Διαχείριση",
|
||||
"configViewer": "Config Viewer",
|
||||
"configViewerShowFull": "Εμφάνιση πλήρων ρυθμίσεων",
|
||||
"configViewerShowComments": "Εμφάνιση σχολίων",
|
||||
"configViewerLoadConfig": "Φόρτωση Config",
|
||||
"brandingHelp": "Μπορείτε να προσαρμόσετε την εμφάνιση και την αίσθηση του File Browser αλλάζοντας το όνομά του, αντικαθιστώντας το λογότυπο, προσθέτοντας προσαρμοσμένα στυλ και ακόμη και απενεργοποιώντας τους εξωτερικούς συνδέσμους προς το GitHub.\nΓια περισσότερες πληροφορίες σχετικά με την προσαρμοσμένη επωνυμία, ανατρέξτε στη διεύθυνση {0}.",
|
||||
"commandRunnerHelp": "Εδώ μπορείτε να ορίσετε τις εντολές που εκτελούνται στα συμβάντα που ονομάζονται. Πρέπει να γράψετε μία ανά γραμμή. Οι περιβαλλοντικές μεταβλητές {0} και {1} θα είναι διαθέσιμες, ενώ η {0} θα είναι σχετική με την {1}. Για περισσότερες πληροφορίες σχετικά με αυτή τη λειτουργία και τις διαθέσιμες μεταβλητές περιβάλλοντος, διαβάστε το {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Βοήθεια",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Απόκρυψη κουμπιών πλοήγησης",
|
||||
"hideNavButtonsDescription": "Κρύψτε τα κουμπιά πλοήγησης στη γραμμή πλοήγησης στο μερίδιο για να δημιουργήσετε μια μινιμαλιστική εμφάνιση.",
|
||||
"viewModeDescription": "Προεπιλεγμένη διάταξη για την κοινή σελίδα.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Σας έχει αποσταλεί ένα share για να το δείτε ή να το κατεβάσετε.",
|
||||
"disableShareCard": "Απενεργοποίηση κάρτας κοινής χρήσης",
|
||||
"disableShareCardDescription": "Απενεργοποιήστε την κάρτα κοινής χρήσης στην κοινόχρηστη σελίδα στην πλαϊνή μπάρα ή στην κορυφή της σελίδας στο κινητό.",
|
||||
"disableSidebar": "Απενεργοποίηση πλευρικής μπάρας",
|
||||
"disableSidebarDescription": "Απενεργοποιήστε την πλαϊνή μπάρα στην κοινή σελίδα."
|
||||
"disableSidebarDescription": "Απενεργοποιήστε την πλαϊνή μπάρα στην κοινή σελίδα.",
|
||||
"enforceDarkLightMode": "Επιβολή λειτουργίας θέματος",
|
||||
"enforceDarkLightModeDescription": "Επιβάλλει μια συγκεκριμένη λειτουργία θέματος (σκούρο ή ανοιχτό) για αυτό το κοινόχρηστο, παρακάμπτοντας τις προτιμήσεις του χρήστη.",
|
||||
"default": "Μην επιβάλλετε μια λειτουργία",
|
||||
"dark": "Σκούρο",
|
||||
"light": "Φως",
|
||||
"enableOnlyOffice": "Ενεργοποίηση της προβολής OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Επιτρέψτε την προβολή αρχείων γραφείου χρησιμοποιώντας το OnlyOffice σε αυτό το κοινόχρηστο χώρο.",
|
||||
"enableOnlyOfficeEditing": "Ενεργοποίηση της επεξεργασίας OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Επιτρέψτε την επεξεργασία αρχείων γραφείου χρησιμοποιώντας το OnlyOffice σε αυτό το κοινόχρηστο χώρο.",
|
||||
"titleDefault": "Κοινόχρηστα αρχεία - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Κλειδιά API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Απορρίπτεται",
|
||||
"totalAllowed": "Επιτρεπόμενο",
|
||||
"all": "Άρνηση όλων",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Αυτό δείχνει τι συμβαίνει όταν ένας χρήστης προσπαθεί να αποκτήσει πρόσβαση σε μια διαδρομή όπου δεν υπάρχουν συγκεκριμένοι κανόνες πρόσβασης. 'Allow' σημαίνει ότι οι χρήστες μπορούν να έχουν πρόσβαση από προεπιλογή, 'Deny' σημαίνει ότι οι χρήστες αποκλείονται από προεπιλογή, εκτός αν επιτρέπεται ρητά."
|
||||
"defaultBehaviorDescription": "Αυτό δείχνει τι συμβαίνει όταν ένας χρήστης προσπαθεί να αποκτήσει πρόσβαση σε μια διαδρομή όπου δεν υπάρχουν συγκεκριμένοι κανόνες πρόσβασης. 'Allow' σημαίνει ότι οι χρήστες μπορούν να έχουν πρόσβαση από προεπιλογή, 'Deny' σημαίνει ότι οι χρήστες αποκλείονται από προεπιλογή, εκτός αν επιτρέπεται ρητά.",
|
||||
"defaultBehavior": "Προεπιλεγμένη ενέργεια{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Ανεβάσματα & λήψεις",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Το προεπιλεγμένο θέμα",
|
||||
"customTheme": "Επιλέξτε το θέμα σας",
|
||||
"showSelectMultiple": "Εμφάνιση πολλαπλών επιλογών στο μενού περιβάλλοντος στην επιφάνεια εργασίας",
|
||||
"showSelectMultipleDescription": "Από προεπιλογή, το μενού περιβάλλοντος της επιφάνειας εργασίας δεν διαθέτει την επιλογή \"επιλογή πολλαπλών\" όπως το κινητό. Αυτή η επιλογή εμφανίζεται πάντα στο μενού περιβάλλοντος."
|
||||
"showSelectMultipleDescription": "Από προεπιλογή, το μενού περιβάλλοντος της επιφάνειας εργασίας δεν διαθέτει την επιλογή \"επιλογή πολλαπλών\" όπως το κινητό. Αυτή η επιλογή εμφανίζεται πάντα στο μενού περιβάλλοντος.",
|
||||
"defaultMediaPlayer": "Χρησιμοποιήστε το εγγενές πρόγραμμα αναπαραγωγής πολυμέσων του προγράμματος περιήγησής σας",
|
||||
"defaultMediaPlayerDescription": "Χρησιμοποιήστε το εγγενές πρόγραμμα αναπαραγωγής πολυμέσων του προγράμματος περιήγησης για αρχεία βίντεο και ήχου αντί για το πρόγραμμα αναπαραγωγής πολυμέσων \"vue-plyr\" που περιλαμβάνεται στο FileBrowser.",
|
||||
"debugOfficeEditor": "Ενεργοποίηση λειτουργίας εντοπισμού σφαλμάτων OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Εμφάνιση ενός αναδυόμενου παραθύρου εντοπισμού σφαλμάτων με πρόσθετες πληροφορίες στον επεξεργαστή OnlyOffice.",
|
||||
"fileViewerOptions": "Επιλογές προβολής αρχείων",
|
||||
"themeAndLanguage": "Θέμα & Γλώσσα"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Απέτυχε η αρχικοποίηση του συντάκτη. Παρακαλούμε επαναφορτώστε.",
|
||||
"saveFailed": "Απέτυχε η αποθήκευση του αρχείου. Προσπαθήστε ξανά.",
|
||||
"saveAborted": "Η αποθήκευση διακόπηκε. Το όνομα του αρχείου δεν ταιριάζει με το ενεργό αρχείο.",
|
||||
"saveDisabled": "Δεν υποστηρίζεται η αποθήκευση κοινόχρηστων αρχείων.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Διακοπή της λειτουργίας αποθήκευσης. Το ενεργό αρχείο της εφαρμογής ({activeFile}) δεν ταιριάζει με το αρχείο που προσπαθείτε να αποθηκεύσετε ({tryingToSave}).",
|
||||
"syncFailed": "απέτυχε να συγχρονίσει την κατάσταση με τη διαδρομή για το \"{filename}\" μετά από 5 προσπάθειες. Διακοπή της ρύθμισης του συντάκτη για να αποφευχθεί η αλλοίωση των δεδομένων."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Ενεργοποίηση βρόχου",
|
||||
"LoopDisabled": "Βρόχος απενεργοποιημένος"
|
||||
}
|
||||
}
|
||||
+50
-13
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Clear completed",
|
||||
"showMore": "Show more",
|
||||
"showLess": "Show less",
|
||||
"openParentFolder": "Open parent folder"
|
||||
"openParentFolder": "Open parent folder",
|
||||
"selectedCount": "Number of items selected to perform an action"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
@@ -87,15 +88,15 @@
|
||||
"click": "select file or directory",
|
||||
"ctrl": {
|
||||
"click": "select multiple files or directories",
|
||||
"f": "opens search",
|
||||
"s": "save a file or download the directory where you are"
|
||||
"d": "download selected items"
|
||||
},
|
||||
"del": "delete selected items",
|
||||
"doubleClick": "open a file or directory",
|
||||
"esc": "clear selection and/or close the prompt",
|
||||
"f1": "this information",
|
||||
"f2": "rename file",
|
||||
"help": "Help"
|
||||
"f1": "show help prompt",
|
||||
"help": "Help",
|
||||
"description": "You can view the basic naviagation options below. For additional information, please visit",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -166,7 +167,16 @@
|
||||
"disableShareCard": "Disable share card",
|
||||
"disableShareCardDescription": "Disable the share card on the shared page in the sidebar or at the top of the page on mobile.",
|
||||
"disableSidebar": "Disable sidebar",
|
||||
"disableSidebarDescription": "Disable the sidebar on the shared page."
|
||||
"disableSidebarDescription": "Disable the sidebar on the shared page.",
|
||||
"enforceDarkLightMode": "Enforce theme mode",
|
||||
"enforceDarkLightModeDescription": "Force a specific theme mode (dark or light) for this share, overriding user preferences.",
|
||||
"default": "Do not enforce a mode",
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
"enableOnlyOffice": "Enable OnlyOffice viewer",
|
||||
"enableOnlyOfficeDescription": "Allow viewing office files using OnlyOffice in this share.",
|
||||
"enableOnlyOfficeEditing": "Enable OnlyOffice editing",
|
||||
"enableOnlyOfficeEditingDescription": "Allow editing of office files using OnlyOffice in this share."
|
||||
},
|
||||
"prompts": {
|
||||
"downloadsLimit": "Number of downloads limit",
|
||||
@@ -180,7 +190,6 @@
|
||||
"shareBanner": "Share banner URL",
|
||||
"shareFavicon": "Share favicon URL",
|
||||
"source": "Source{suffix}",
|
||||
"invalidName": "Invalid name provided: cannot container \"/\" or \"\\\"",
|
||||
"destinationSource": "Destination Source:",
|
||||
"copy": "Copy",
|
||||
"copyMessage": "Choose the place to copy your files:",
|
||||
@@ -209,7 +218,9 @@
|
||||
"numberDirs": "Number of directories{suffix}",
|
||||
"numberFiles": "Number of files{suffix}",
|
||||
"rename": "Rename",
|
||||
"renameMessage": "An item named \"{filename}\" already exists! Enter a new name:",
|
||||
"renameMessageConflict": "An item named \"{filename}\" already exists!",
|
||||
"renameMessage": "Enter a new name:",
|
||||
"renameMessageInvalid": "A file can't container \"/\" or \"\\\"",
|
||||
"replace": "Replace",
|
||||
"replaceMessage": "One of the files you're trying to upload is conflicting because of its name. Do you wish to continue to upload or replace the existing one?\n",
|
||||
"schedule": "Schedule",
|
||||
@@ -226,12 +237,23 @@
|
||||
"conflictsDetected": "Conflicts detected"
|
||||
},
|
||||
"search": {
|
||||
"searchContext": "Search Context: {context}",
|
||||
"onlyFolders": "Only Folders",
|
||||
"onlyFiles": "Only Files",
|
||||
"showOptions": "Show Options",
|
||||
"photos": "Photos",
|
||||
"audio": "Audio",
|
||||
"videos": "Videos",
|
||||
"documents": "Documents",
|
||||
"archives": "Archives",
|
||||
"notEnoughCharacters": "Not enough characters to search (min {minSearchLength})",
|
||||
"typeToSearch": "Start typing {minSearchLength} or more characters to begin searching.",
|
||||
"images": "Images",
|
||||
"music": "Music",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "No results found in indexed search.",
|
||||
"search": "Search...",
|
||||
"typeToSearch": "Type to search... (3 character minimum)",
|
||||
"number": "Number",
|
||||
"noResults": "No results found in indexed search.",
|
||||
"path": "Path:",
|
||||
"types": "Types",
|
||||
"video": "Video",
|
||||
@@ -241,6 +263,10 @@
|
||||
"helpText2": "The index: Search utilizes the index which automatically gets updated on the configured interval (default: 5 minutes). Searching when the program has just started may result in incomplete results."
|
||||
},
|
||||
"profileSettings": {
|
||||
"debugOfficeEditor": "Enable OnlyOffice Debug Mode",
|
||||
"debugOfficeEditorDescription": "Show a debug popup with additional information in the OnlyOffice editor.",
|
||||
"defaultMediaPlayer": "Use your browser's native media player",
|
||||
"defaultMediaPlayerDescription": "Use your browser's native media player for video and audio files rather than FileBrowser's included 'vue-plyr' media player.",
|
||||
"showSelectMultiple": "Show select multiple in context menu on desktop",
|
||||
"showSelectMultipleDescription": "By default, desktop context menu does not have \"select multiple\" option like mobile. This option always shows it in the context menu.",
|
||||
"customTheme": "Choose your theme",
|
||||
@@ -289,9 +315,16 @@
|
||||
"disablePreviewExt": "Disable office file previews for certain file extensions",
|
||||
"disablePreviewExtDescription": "A space-separated list of file extensions to disable office file previews for (e.g., '.docx .pptx').",
|
||||
"disableOfficeEditorDescription": "A space-separated list of file extensions to disable the OnlyOffice editor and viewer for. (e.g., '.txt .html .pdf').",
|
||||
"disableOfficeEditor": "Disable office viewer for certain file extensions"
|
||||
"disableOfficeEditor": "Disable office viewer for certain file extensions",
|
||||
"fileViewerOptions": "File Viewer Options",
|
||||
"themeAndLanguage": "Theme & Language"
|
||||
},
|
||||
"settings": {
|
||||
"systemAdmin": "System & Admin",
|
||||
"configViewer": "Config Viewer",
|
||||
"configViewerShowFull": "Show full config",
|
||||
"configViewerShowComments": "Show comments",
|
||||
"configViewerLoadConfig": "Load Config",
|
||||
"enterPassword": "Enter password",
|
||||
"enterPasswordAgain": "Enter the password again",
|
||||
"passwordsDoNotMatch": "Passwords do not match",
|
||||
@@ -523,5 +556,9 @@
|
||||
"saveDisabled":"Saving shared files is not supported.",
|
||||
"saveAbortedMessage":"Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed":"failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Loop Enabled",
|
||||
"LoopDisabled": "Loop Disabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
+133
-96
@@ -31,25 +31,26 @@
|
||||
"share": "Compartir",
|
||||
"shell": "Presiona Enter para buscar...",
|
||||
"submit": "Enviar",
|
||||
"switchView": "Cambiar vista",
|
||||
"toggleSidebar": "Alternar menú",
|
||||
"switchView": "Cambiar modo de visualización",
|
||||
"toggleSidebar": "Cambiar visibilidad del panel lateral",
|
||||
"update": "Actualizar",
|
||||
"upload": "Subir",
|
||||
"openFile": "Abrir archivo",
|
||||
"copyDownloadLinkToClipboard": "Copiar enlace de descarga",
|
||||
"continue": "Continuar",
|
||||
"edit": "Editar",
|
||||
"generateNewOtp": "Generar nuevo código 2FA",
|
||||
"generateNewOtp": "Generar nuevo código de dos factores (2FA)",
|
||||
"verify": "Verificar",
|
||||
"pause": "Pausar",
|
||||
"resume": "Reanudar",
|
||||
"retry": "Reintentar",
|
||||
"pauseAll": "Pausar todo",
|
||||
"resumeAll": "Reanudar todo",
|
||||
"clearCompleted": "Limpiar lista",
|
||||
"clearCompleted": "Limpiar completados",
|
||||
"showMore": "Ver más",
|
||||
"showLess": "Mostrar menos",
|
||||
"openParentFolder": "Abrir carpeta principal"
|
||||
"openParentFolder": "Abrir carpeta principal",
|
||||
"selectedCount": "Número de elementos seleccionados para realizar una acción"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descargar archivo",
|
||||
@@ -61,7 +62,7 @@
|
||||
"internal": "Error interno :(",
|
||||
"notFound": "No se encontró el directorio",
|
||||
"connection": "No se puede conectar al servidor.",
|
||||
"shareNotFound": "Acción no válida o caducada."
|
||||
"shareNotFound": "No se encuentra lo que buscas, tal vez el enlace expiró."
|
||||
},
|
||||
"files": {
|
||||
"body": "Contenido",
|
||||
@@ -75,7 +76,7 @@
|
||||
"multipleSelectionEnabled": "Selección múltiple activada",
|
||||
"name": "Nombre",
|
||||
"size": "Tamaño",
|
||||
"sortByLastModified": "Ordenar por modificación",
|
||||
"sortByLastModified": "Ordenar por fecha de modificación",
|
||||
"sortByName": "Ordenar por nombre",
|
||||
"sortBySize": "Ordenar por tamaño",
|
||||
"noPreview": "Vista previa no disponible"
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "Selecciona un archivo o carpeta",
|
||||
"ctrl": {
|
||||
"click": "Selecciona varios archivos o carpetas (Selección multiple)",
|
||||
"f": "Abrir panel de búsqueda",
|
||||
"s": "Guardar archivo (solo funciona si estás en el editor)"
|
||||
"d": "descargar artículos seleccionados"
|
||||
},
|
||||
"del": "Eliminar archivos/carpetas seleccionados",
|
||||
"doubleClick": "Abre un archivo o carpeta",
|
||||
"esc": "Cancela la selección o cierra la ventana actual (ej: Para salir rápido cuando estas viendo una imagen o video)",
|
||||
"f1": "Esta ventana",
|
||||
"f2": "Cambiar nombre del archivo",
|
||||
"help": "Ayuda"
|
||||
"f2": "Cambiar nombre del archivo o carpeta",
|
||||
"help": "Ayuda",
|
||||
"f1": "mostrar mensaje de ayuda",
|
||||
"description": "A continuación puede ver las opciones básicas de navegación. Para más información, visite",
|
||||
"wiki": "Wiki de FileBrowser Quantum"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Copiar",
|
||||
"copyMessage": "Selecciona una ubicación para copiar",
|
||||
"deleteMessageMultiple": "¿Estás seguro que quieres eliminar {count} archivo(s)?",
|
||||
"deleteMessageSingle": "¿Estás seguro que quieres eliminar este archivo/carpeta?",
|
||||
"deleteMessageShare": "¿Estás seguro que quieres eliminar este recurso compartido ({path})?",
|
||||
"deleteTitle": "Borrar archivos",
|
||||
"displayName": "Nombre:",
|
||||
"download": "Descargar archivos",
|
||||
"downloadMessage": "Elige el formato de descarga",
|
||||
"error": "Ha ocurrido un error",
|
||||
"fileInfo": "Información del archivo",
|
||||
"lastModified": "Última modificación{suffix}",
|
||||
"move": "Mover",
|
||||
"moveMessage": "Selecciona un nuevo destino:",
|
||||
"newArchetype": "Crear una nueva publicación en base a una plantilla, tu archivo se creara en la misma carpeta",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Nombre de la carpeta",
|
||||
"newFile": "Nuevo archivo",
|
||||
"newFileMessage": "Nombre del archivo",
|
||||
"numberDirs": "Carpetas",
|
||||
"numberFiles": "Archivos",
|
||||
"rename": "Cambiar nombre",
|
||||
"renameMessage": "Nuevo nombre para:",
|
||||
"replace": "Reemplazar",
|
||||
"replaceMessage": "Un archivo con el mismo nombre ya existe. ¿Quieres cambiar el nombre?\n",
|
||||
"schedule": "Programar",
|
||||
@@ -169,39 +164,57 @@
|
||||
"selected": "seleccionado",
|
||||
"deleted": "¡Borrado con éxito!",
|
||||
"destinationSource": "Destino:",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Límite de descargas",
|
||||
"maxBandwidth": "Ancho de banda máximo de descarga (kbps)",
|
||||
"shareTheme": "Compartir tema",
|
||||
"shareThemeColor": "Compartir color del tema (valor CSS)",
|
||||
"shareTitle": "Compartir título",
|
||||
"shareDescription": "Compartir descripción",
|
||||
"shareLogo": "Compartir URL del logotipo",
|
||||
"shareBanner": "Compartir URL del banner",
|
||||
"shareFavicon": "Compartir URL de favicon",
|
||||
"optionalPasswordHelp": "Si se establece, los usuarios deben introducir esta contraseña para ver el contenido compartido.",
|
||||
"maxBandwidth": "Velocidad máxima de descarga (kbps)",
|
||||
"shareTheme": "Tema",
|
||||
"shareThemeColor": "Color del tema (valor CSS)",
|
||||
"shareTitle": "Título la página",
|
||||
"shareDescription": "Descripción de la página",
|
||||
"shareLogo": "Imagen/Logo de la página",
|
||||
"shareBanner": "Banner de la página",
|
||||
"shareFavicon": "Mini-icono de la página (favicon)",
|
||||
"optionalPasswordHelp": "Si eliges una contraseña, los usuarios que accedan deben introducir esa misma contraseña para ver el contenido.",
|
||||
"dragAndDrop": "Arrastra y suelta los archivos aquí",
|
||||
"completed": "Completado",
|
||||
"conflictsDetected": "Conflictos detectados",
|
||||
"uploadSettingsChunked": "Subidas simultáneas: {maxConcurrentUpload}, velocidad de subida máxima: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Subidas simultáneas: {maxConcurrentUpload}, subida de archivos desactivada",
|
||||
"invalidName": "Nombre inválido: no puede contener \"/\" o \"\\\".",
|
||||
"viewMode": "Ver modo"
|
||||
"viewMode": "Cambiar modo de visualización",
|
||||
"renameMessage": "Introduce un nuevo nombre:",
|
||||
"renameMessageInvalid": "Un archivo no puede contener \"/\" o \"\\\"",
|
||||
"source": "Fuente{suffix}",
|
||||
"deleteMessageMultiple": "¿Está seguro de que desea eliminar los archivos de {count}?",
|
||||
"deleteMessageShare": "¿Estás seguro de que quieres borrar esta acción? {path} ?",
|
||||
"lastModified": "Última modificación{suffix}",
|
||||
"numberDirs": "Número de directorios{suffix}",
|
||||
"numberFiles": "Número de ficheros{suffix}",
|
||||
"renameMessageConflict": "Ya existe un elemento llamado \"{filename}\".",
|
||||
"uploadSettingsChunked": "Carga máxima simultánea: {maxConcurrentUpload} Tamaño del chunk {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Carga máxima simultánea: {maxConcurrentUpload} Carga por trozos desactivada"
|
||||
},
|
||||
"search": {
|
||||
"images": "Imágenes",
|
||||
"music": "Música",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Enter para buscar...",
|
||||
"search": "Buscar...",
|
||||
"typeToSearch": "Escribe para buscar...",
|
||||
"types": "Tipos",
|
||||
"video": "Vídeo",
|
||||
"path": "Ruta:",
|
||||
"smallerThan": "Menor que:",
|
||||
"largerThan": "Mayor que:",
|
||||
"helpText1": "La búsqueda se realiza por cada carácter (mínimo de 3 caracteres)",
|
||||
"helpText2": "El índice se actualiza automáticamente (cada 5 min por defecto). Si es interrumpido pueden ocurrir errores"
|
||||
"helpText2": "El índice se actualiza automáticamente (cada 5 min por defecto). Si es interrumpido pueden ocurrir errores",
|
||||
"onlyFolders": "Sólo carpetas",
|
||||
"onlyFiles": "Sólo archivos",
|
||||
"showOptions": "Mostrar opciones",
|
||||
"photos": "Fotos",
|
||||
"audio": "Audio",
|
||||
"videos": "Vídeos",
|
||||
"documents": "Documentos",
|
||||
"archives": "Archivos",
|
||||
"number": "Número",
|
||||
"noResults": "No se han encontrado resultados en la búsqueda indexada.",
|
||||
"searchContext": "Contexto de búsqueda: {context}",
|
||||
"notEnoughCharacters": "No hay suficientes caracteres para buscar (min {minSearchLength})",
|
||||
"typeToSearch": "Escriba {minSearchLength} o más caracteres para iniciar la búsqueda."
|
||||
},
|
||||
"settings": {
|
||||
"scopes": "Establece los directorios y los permisos que el usuario puede acceder",
|
||||
@@ -215,10 +228,8 @@
|
||||
"avoidChanges": "(dejar en blanco para evitar cambios)",
|
||||
"branding": "Personalización",
|
||||
"brandingDirectoryPath": "Directorio de tus archivos que usarás para personalizar FileBrowser",
|
||||
"brandingHelp": "Personaliza FileBrowser cambiando el nombre, logo, estilos. Más información en {0}",
|
||||
"changePassword": "Cambiar contraseña",
|
||||
"commandRunner": "Ejecutor de comandos",
|
||||
"commandRunnerHelp": "Comandos para eventos. Variables disponibles: {0} (ruta relativa), {1} (ruta base). Más info en {2}",
|
||||
"commandsUpdated": "¡Comandos actualizados!",
|
||||
"createUserDir": "Crea automaticamente una carpeta cuando se agrega un nuevo usuario",
|
||||
"userHomeBasePath": "Ruta base para los directorios personales de los usuarios",
|
||||
@@ -249,7 +260,7 @@
|
||||
"modify": "Editar archivos",
|
||||
"share": "Compartir archivos",
|
||||
"admin": "Administrador",
|
||||
"api": "Gestionar claves API",
|
||||
"api": "Gestión de APIs",
|
||||
"realtime": "Actualizaciones en tiempo real"
|
||||
},
|
||||
"permissions-name": "Permisos",
|
||||
@@ -261,7 +272,7 @@
|
||||
"rulesHelp": "Puedes definir un conjunto de reglas de permisos para este usuario específico. Los archivos bloqueados no se mostrarán en las listas y no serán accesibles por el usuario. Puedes utilizar regex y rutas relativas a la raíz del usuario.\n",
|
||||
"scope": "Raíz",
|
||||
"settingsUpdated": "¡Ajustes actualizados!",
|
||||
"shareManagement": "Gestión Compartida",
|
||||
"shareManagement": "Gestión de recursos compartidos",
|
||||
"shareDeleted": "¡Compartido eliminado!",
|
||||
"singleClick": "Abrir con un solo clic",
|
||||
"themes": {
|
||||
@@ -275,7 +286,7 @@
|
||||
"userCreated": "¡Usuario creado!",
|
||||
"userDefaults": "Configuración de usuario por defecto",
|
||||
"userDeleted": "¡Usuario eliminado!",
|
||||
"userManagement": "Gestionar usuarios",
|
||||
"userManagement": "Gestión de usuarios",
|
||||
"userUpdated": "¡Usuario actualizado!",
|
||||
"username": "Nombre de Usuario",
|
||||
"users": "Usuarios",
|
||||
@@ -287,20 +298,27 @@
|
||||
"UserManagement": "Gestión de usuarios",
|
||||
"tusUploads": "Subir en partes",
|
||||
"tusUploadsHelp": "File Browser Quantum permite subir archivos en partes, lo que permite que sean subidos de manera eficiente, fiable, que sea reanudable, incluso si tu internet es inestable",
|
||||
"tusUploadsChunkSize": "Tamaño máximo de subida de un archivo. Puede introducir un número entero que indique una entrada en bytes o una cadena como 10 MB, 1 GB, etc",
|
||||
"tusUploadsChunkSize": "Tamaño máximo de subida de un archivo. Puede escribir un número entero que indique una entrada en bytes o una cadena como 10 MB, 1 GB, etc",
|
||||
"tusUploadsRetryCount": "Número de reintentos, si falla la subida de un archivo",
|
||||
"api": "Claves API",
|
||||
"loginMethodDescription": "Método de inicio de sesión permitido",
|
||||
"userNotAdmin": "El usuario no es administrador",
|
||||
"enterPassword": "Indusca la contraseña",
|
||||
"enterPasswordAgain": "Vuelva a introducir la contraseña",
|
||||
"enterPassword": "Escriba su contraseña",
|
||||
"enterPasswordAgain": "Vuelva a escribir su contraseña",
|
||||
"passwordsDoNotMatch": "Las contraseñas no coinciden",
|
||||
"loginMethod": "Método de inicio de sesión",
|
||||
"shareSettings": "Compartidos",
|
||||
"shareSettings": "Recursos compartidos",
|
||||
"adminOptions": "Opciones de administrador",
|
||||
"shareDuration": "Expira en",
|
||||
"searchOptions": "Opciones de búsqueda",
|
||||
"downloads": "Descargas"
|
||||
"downloads": "Descargas",
|
||||
"systemAdmin": "Sistema y administración",
|
||||
"configViewer": "Visor de configuración",
|
||||
"configViewerShowFull": "Mostrar configuración completa",
|
||||
"configViewerShowComments": "Mostrar comentarios",
|
||||
"configViewerLoadConfig": "Configuración de carga",
|
||||
"brandingHelp": "Puede personalizar el aspecto de su instancia del Navegador de archivos cambiando su nombre, sustituyendo el logotipo, añadiendo estilos personalizados e incluso desactivando los enlaces externos a GitHub.\nPara obtener más información sobre la personalización de la marca, consulta la página {0}.",
|
||||
"commandRunnerHelp": "Aquí puede establecer los comandos que se ejecutan en los eventos nombrados. Debe escribir uno por línea. Las variables de entorno {0} y {1} estarán disponibles, siendo {0} relativa a {1}. Para más información sobre esta característica y las variables de entorno disponibles, por favor lea el {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Ayuda",
|
||||
@@ -333,61 +351,70 @@
|
||||
},
|
||||
"share": {
|
||||
"notice": "Nota: cuando se crea un recurso compartido, queda más expuesto al público y tiene diferentes normas de acceso. Si su recurso compartido no está configurado de forma segura, cualquiera puede acceder a él.",
|
||||
"disableAnonymousDescription": "Cuando está activada, sólo los usuarios autenticados pueden acceder al recurso compartido. El usuario debe tener acceso al origen del recurso compartido.",
|
||||
"disableAnonymousDescription": "Cuando está activado, sólo los usuarios con su permiso pueden acceder al recurso compartido. El usuario debe tener acceso al origen recurso compartido.",
|
||||
"disableThumbnailsDescription": "Si está activada, las miniaturas de previsualización no se mostrarán en la acción.",
|
||||
"enableAllowedUsernamesDescription": "Cuando se activa, sólo los usuarios especificados pueden acceder al recurso compartido. El usuario debe tener acceso al origen del recurso compartido.",
|
||||
"enableAllowedUsernamesDescription": "Cuando se activa, sólo los usuarios que especifique pueden acceder al recurso compartido. El usuario debe tener acceso al origen del recurso compartido.",
|
||||
"downloadsLimitDescription": "Número máximo de veces que se puede descargar un archivo/carpeta del recurso compartido. Dejar vacío para ilimitado.",
|
||||
"maxBandwidthDescription": "El ancho de banda máximo de descarga en kilobytes por segundo. Dejar vacío para ilimitado.",
|
||||
"shareThemeDescription": "El tema que se utilizará para el enlace compartido.",
|
||||
"disableAnonymous": "Desactivar el acceso anónimo",
|
||||
"disableThumbnails": "Desactivar miniaturas",
|
||||
"enableAllowedUsernames": "Compartir sólo con determinados usuarios",
|
||||
"allowedUsernamesPlaceholder": "Introduzca los nombres de usuario, separados por comas",
|
||||
"keepAfterExpiration": "No eliminar la acción una vez caducada",
|
||||
"keepAfterExpirationDescription": "La acción no se borrará después de que caduque. Esto resulta útil si desea ampliar el plazo de expiración de una acción o editarla después de que expire.",
|
||||
"allowedUsernamesPlaceholder": "Escriba los nombres de usuario (separado por comas)",
|
||||
"keepAfterExpiration": "No eliminar el recurso compartido una vez expirado",
|
||||
"keepAfterExpirationDescription": "Los recursos compartidos no se borraran cuando expiren. Es útil si después quieres ampliar la duración del recurso, o editarlo.",
|
||||
"saveDisabled": "No se pueden guardar los cambios en los archivos compartidos.",
|
||||
"shareDurationDescription": "Duración antes de que caduque la acción. Dejar en blanco para una acción permanente.",
|
||||
"passwordDescription": "Contraseña opcional necesaria para acceder a este recurso compartido, incluidos los usuarios autenticados.",
|
||||
"shareDurationDescription": "Duración del recurso compartido. Dejar en blanco para permanente.",
|
||||
"passwordDescription": "Contraseña opcional necesaria para acceder al recurso compartido, incluidos los usuarios autenticados.",
|
||||
"allowedUsernamesListDescription": "Nombres de usuario separados por comas que pueden acceder al recurso compartido (si está activado).",
|
||||
"shareThemeColorDescription": "Valor de color CSS aplicado al tema de la acción (por ejemplo, rojo, #0ea5e9, rgb(14,165,233)).",
|
||||
"shareTitleDescription": "Título de página personalizado que se muestra en la página de compartición.",
|
||||
"shareDescriptionHelp": "Breve descripción mostrada en la página de compartición (puede utilizarse en las metaetiquetas).",
|
||||
"shareLogoDescription": "URL o ruta de la imagen del logotipo accesible por el cliente -- se aceptan rutas abosolutas o de índice: por ejemplo, 'https://domain.com/logo.png' o '/ruta/a/logo.png'.",
|
||||
"shareBannerDescription": "URL de la imagen del banner o ruta accesible por el cliente -- se aceptan rutas abosolutas o de índice: por ejemplo, 'https://domain.com/banner.png' o '/ruta/a/banner.png'.",
|
||||
"shareFaviconDescription": "URL del favicon o ruta accesible por el cliente -- se aceptan rutas abosolutas o de índice: por ejemplo 'https://domain.com/favicon.png' o '/ruta/a/favicon.png'.",
|
||||
"shareTitleDescription": "Título de página personalizado de página",
|
||||
"shareDescriptionHelp": "Breve descripción mostrada en la página (que suele aparecer cuando compartes el enlace)",
|
||||
"shareLogoDescription": "Enlace (URL), o ruta de la imagen del logo que se mostrará en el enlace -- por ejemplo, 'https://ejemplo.com/logo.png' o '/ruta/a/logo.png'.",
|
||||
"shareBannerDescription": "Enlace (URL), o ruta de la imagen del banner que se mostrará en el enlace -- por ejemplo, 'https://ejemplo.com/banner.png' o '/ruta/a/banner.png'.",
|
||||
"shareFaviconDescription": "Enlace (URL) del icono (favicon) que se mostrará en el enlace -- por ejemplo 'https://domain.com/favicon.png' o '/ruta/a/favicon.png'",
|
||||
"hideNavButtons": "Ocultar botones de navegación",
|
||||
"hideNavButtonsDescription": "Oculte los botones de navegación de la barra de navegación en la acción para crear un aspecto minimalista.",
|
||||
"viewModeDescription": "Diseño por defecto de la página compartida.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Se le ha enviado una acción para que la vea o descargue.",
|
||||
"disableShareCard": "Desactivar tarjeta compartida",
|
||||
"disableShareCardDescription": "Desactivar la tarjeta de compartir en la página compartida en la barra lateral o en la parte superior de la página en móvil.",
|
||||
"disableSidebar": "Desactivar la barra lateral",
|
||||
"disableSidebarDescription": "Desactivar la barra lateral en la página compartida."
|
||||
"disableShareCardDescription": "Desactivar la tarjeta de compartir en la página compartida en el panel lateral o en la parte superior de la página en móvil.",
|
||||
"disableSidebar": "Desactivar el panel lateral",
|
||||
"disableSidebarDescription": "Desactivar el panel lateral en la página compartida.",
|
||||
"enforceDarkLightMode": "Aplicar el modo temático",
|
||||
"enforceDarkLightModeDescription": "Forzar un modo de tema específico (oscuro o claro) para esta acción, anulando las preferencias del usuario.",
|
||||
"default": "No imponer un modo",
|
||||
"dark": "Oscuro",
|
||||
"light": "Luz",
|
||||
"enableOnlyOffice": "Activar el visor de OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Permitir la visualización de archivos de oficina utilizando OnlyOffice en este recurso compartido.",
|
||||
"enableOnlyOfficeEditing": "Habilitar la edición de OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Permitir la edición de archivos de oficina utilizando OnlyOffice en este recurso compartido.",
|
||||
"titleDefault": "Archivos compartidos - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Crear API:",
|
||||
"keyName": "Nombre de la clave:",
|
||||
"title": "Claves API",
|
||||
"keyName": "Nombre para la API",
|
||||
"createdAt": "Creado en:",
|
||||
"expiresAt": "Expira en:",
|
||||
"permissions": "Permisos:",
|
||||
"createTitle": "Crear clave API",
|
||||
"keyNamePlaceholder": "Nombre único para clave API:",
|
||||
"keyNamePlaceholder": "Escriba un nombre único",
|
||||
"tokenDuration": "Duración:",
|
||||
"durationNumberPlaceholder": "Número:",
|
||||
"days": "Días",
|
||||
"months": "Meses",
|
||||
"swaggerLinkText": "Documentación de Swagger",
|
||||
"swaggerLinkText": "Documentación de Swagger (API)",
|
||||
"name": "Nombre",
|
||||
"created": "Creado",
|
||||
"expires": "Expira en",
|
||||
"expires": "Expira en:",
|
||||
"actions": "Acciones",
|
||||
"enabled": "Habilitado",
|
||||
"disabled": "Deshabilitado",
|
||||
"permissionNote": "Elija al menos un permiso para la clave. Su usuario también debe tener el permiso.",
|
||||
"permissionNote": "Elija al menos un permiso para la API. Tu usuario también debe tener ese permiso.",
|
||||
"createKeyFailed": "Error al crear la clave API.",
|
||||
"createKeySuccess": "Clave API creada correctamente.",
|
||||
"description": "Las claves API se basan en el usuario que las crea. Véase"
|
||||
"createKeySuccess": "¡Clave API creada con exito!",
|
||||
"description": "Las claves API solo podrán tener los permisos que el usuario tenga. Para más info:"
|
||||
},
|
||||
"index": {
|
||||
"status": "Estado",
|
||||
@@ -399,7 +426,7 @@
|
||||
"logout": "Cerrar sesión",
|
||||
"toggleClick": "Abrir con un solo clic",
|
||||
"toggleDark": "Activar modo oscuro",
|
||||
"toggleSticky": "Barra lateral visible",
|
||||
"toggleSticky": "Cambiar visibilidad del panel lateral",
|
||||
"used": "usado",
|
||||
"noSources": "No hay fuentes disponibles. Añada una o active defaultEnabled: true."
|
||||
},
|
||||
@@ -439,7 +466,7 @@
|
||||
"allow": "Permitir",
|
||||
"allowDeny": "Permitir/Denegar",
|
||||
"userGroup": "Usuario/Grupo",
|
||||
"enterName": "Introduzca el nombre del usuario o del grupo",
|
||||
"enterName": "Escriba el nombre del usuario o del grupo",
|
||||
"deleted": "Regla de acceso eliminada",
|
||||
"added": "Regla de acceso añadida",
|
||||
"name": "Nombre",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Denegado",
|
||||
"totalAllowed": "Permitido",
|
||||
"all": "Denegar todo",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Muestra lo que ocurre cuando un usuario intenta acceder a una ruta en la que no existen reglas de acceso específicas. Permitir\" significa que los usuarios pueden acceder por defecto, \"Denegar\" significa que los usuarios están bloqueados por defecto a menos que se les permita explícitamente."
|
||||
"defaultBehaviorDescription": "Muestra lo que ocurre cuando un usuario intenta acceder a una ruta en la que no existen reglas de acceso específicas. Permitir\" significa que los usuarios pueden acceder por defecto, \"Denegar\" significa que los usuarios están bloqueados por defecto a menos que se les permita explícitamente.",
|
||||
"defaultBehavior": "Acción por defecto{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Carga de archivos",
|
||||
@@ -458,17 +485,17 @@
|
||||
"uploadChunkSizeMbHelp": "Típicamente 5 a 50 MB, más pequeño es mejor para conexiones inestables. Si se pone a 0, se desactiva la subida de archivo en partes"
|
||||
},
|
||||
"colors": {
|
||||
"red": "rojo",
|
||||
"green": "verde",
|
||||
"blue": "azul",
|
||||
"yellow": "amarillo",
|
||||
"violet": "violeta",
|
||||
"orange": "naranja"
|
||||
"red": "Rojo",
|
||||
"green": "Verde",
|
||||
"blue": "Azul",
|
||||
"yellow": "Amarillo",
|
||||
"violet": "Morado",
|
||||
"orange": "Naranja"
|
||||
},
|
||||
"profileSettings": {
|
||||
"autoplayMedia": "Reproducción automática de archivos multimedia",
|
||||
"autoplayMediaDescription": "Reproducción automática de archivos multimedia al visualizarlos. Esto sólo funcionará si el archivo es un archivo multimedia",
|
||||
"sidebarOptions": "Opciones de la barra lateral",
|
||||
"sidebarOptions": "Opciones del panel lateral",
|
||||
"editorViewerOptions": "Edición y visualización",
|
||||
"editorQuickSave": "Botón de guardado rápido en el editor",
|
||||
"editorQuickSaveDescription": "El editor de texto mostrará siempre el icono de guardar, ocultando cualquier otra acción",
|
||||
@@ -484,9 +511,9 @@
|
||||
"disableOfficeEditor": "Desactivar el visor y editor de office para las siguientes extensiones:",
|
||||
"defaultThemeDescription": "Predeterminado",
|
||||
"customTheme": "Cambiar tema",
|
||||
"showSelectMultiple": "Mostrar selección múltiple en el menú contextual del escritorio",
|
||||
"showSelectMultiple": "Mostrar selección múltiple en el menú contextuaL (en PC)",
|
||||
"showSelectMultipleDescription": "Por defecto, el menú contextual del escritorio no tiene la opción \"seleccionar múltiples\" como el móvil. Esta opción siempre se muestra en el menú contextual.",
|
||||
"setDateFormat": "Establecer el formato exacto de la fecha",
|
||||
"setDateFormat": "Usar fecha exacta (DD/MM/AA)",
|
||||
"showHiddenFiles": "Mostrar archivos ocultos",
|
||||
"showHiddenFilesDescription": "Muestra los archivos ocultos. (por ejemplo, .thumbnails, ProgramData), pero siguen siendo accesibles manualmente si están configurados para ser indexados.",
|
||||
"showQuickDownload": "Icono de descarga rápida",
|
||||
@@ -504,24 +531,34 @@
|
||||
"popupPreview": "Vista previa emergente",
|
||||
"popupPreviewDescription": "Se mostrará una ventana emergente superpuesta sobre la interfaz de usuario al pasar el ratón por encima de los archivos que tengan una miniatura",
|
||||
"filePreviewOptions": "Opciones de previsualización de archivos",
|
||||
"disableUpdateNotifications": "Desactivar la visualización de notificaciones de actualización",
|
||||
"disableUpdateNotificationsDescription": "Desactivar el banner de notificación de actualización que aparece para los usuarios administradores en la barra lateral.",
|
||||
"disableHideSidebar": "Mantener la barra lateral abierta al previsualizar o editar archivos",
|
||||
"disableHideSidebarDescription": "Si está activada, la barra lateral no se ocultará automáticamente al previsualizar o editar archivos.",
|
||||
"disableQuickToggles": "Desactivar alternancias rápidas",
|
||||
"disableQuickTogglesDescription": "Desactiva los toggles rápidos que aparecen en la barra lateral.",
|
||||
"disableUpdateNotifications": "Desactivar las notificaciones de actualización",
|
||||
"disableUpdateNotificationsDescription": "Desactivar el banner de notificación de actualización que aparece para los usuarios administradores en el panel lateral.",
|
||||
"disableHideSidebar": "Mantener el panel lateral abierto al previsualizar o editar archivos",
|
||||
"disableHideSidebarDescription": "Si está activado, el panel lateral no se ocultará automáticamente al previsualizar o editar archivos.",
|
||||
"disableQuickToggles": "Desactivar ajustes rápidos",
|
||||
"disableQuickTogglesDescription": "Desactiva los ajustes rápidos que aparecen en el panel lateral. (cambiar tema, visibilidad del panel y modo de un solo click)",
|
||||
"disableSearchOptions": "Desactivar las opciones de búsqueda",
|
||||
"disableSearchOptionsDescription": "Desactivar las opciones de búsqueda que aparecen en la barra lateral.",
|
||||
"hideSidebarFileActions": "Ocultar el botón de acciones de archivo en la barra lateral",
|
||||
"disableSearchOptionsDescription": "Desactivar las opciones de búsqueda que aparecen en el panel.",
|
||||
"hideSidebarFileActions": "Ocultar el botón de acciones de archivo en el panel",
|
||||
"deleteWithoutConfirming": "Eliminar archivos sin mensaje de confirmación",
|
||||
"deleteWithoutConfirmingDescription": "Eliminar archivos sin mensaje de confirmación. Esto sólo funcionará si el archivo es un archivo multimedia."
|
||||
"deleteWithoutConfirmingDescription": "Eliminar archivos sin mensaje de confirmación. Esto sólo funcionará si el archivo es un archivo multimedia.",
|
||||
"defaultMediaPlayer": "Usar el reproductor multimedia nativo del navegador",
|
||||
"defaultMediaPlayerDescription": "Usar el reproductor multimedia nativo de tu navegador para los archivos de vídeo y audio en lugar del reproductor multimedia incluido en FileBrowser.",
|
||||
"debugOfficeEditor": "Activar el modo de depuración de OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Mostrar una ventana emergente de depuración con información adicional en el editor OnlyOffice.",
|
||||
"fileViewerOptions": "Opciones del visor de archivos",
|
||||
"themeAndLanguage": "Tema y lengua"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Error al inicializar el editor. Por favor, vuelva a cargar.",
|
||||
"uninitialized": "Error al iniciar el editor, inténtelo de nuevo",
|
||||
"saveFailed": "No se ha podido guardar el archivo. Por favor, inténtelo de nuevo.",
|
||||
"saveAborted": "Guardado abortado. El nombre del fichero no coincide con el fichero activo.",
|
||||
"saveAborted": "No se ha podido guardar el archivo. El nombre del archivo no coincide con el que estás editando.",
|
||||
"saveDisabled": "No es posible guardar archivos compartidos.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operación de guardado abortada. El archivo activo de la aplicación ({activeFile}) no coincide con el archivo que está intentando guardar ({tryingToSave}).",
|
||||
"syncFailed": "No se ha podido sincronizar el estado con la ruta para \"{filename}\" después de 5 intentos. Abortar la configuración del editor para evitar la corrupción de datos."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Bucle activado",
|
||||
"LoopDisabled": "Bucle desactivado"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Effacement terminé",
|
||||
"showMore": "Afficher plus",
|
||||
"showLess": "Montrer moins",
|
||||
"openParentFolder": "Ouvrir le dossier parent"
|
||||
"openParentFolder": "Ouvrir le dossier parent",
|
||||
"selectedCount": "Nombre d'éléments sélectionnés pour effectuer une action"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Télécharger le fichier",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "Sélectionner un élément",
|
||||
"ctrl": {
|
||||
"click": "Sélectionner plusieurs éléments",
|
||||
"f": "Ouvrir l'invite de recherche",
|
||||
"s": "Télécharger l'élément actuel"
|
||||
"d": "télécharger les éléments sélectionnés"
|
||||
},
|
||||
"del": "Supprimer les éléments sélectionnés",
|
||||
"doubleClick": "Ouvrir un élément",
|
||||
"esc": "Désélectionner et/ou fermer la boîte de dialogue",
|
||||
"f1": "Ouvrir l'aide",
|
||||
"f2": "Renommer le fichier",
|
||||
"help": "Aide"
|
||||
"help": "Aide",
|
||||
"f1": "show help prompt",
|
||||
"description": "Vous pouvez consulter les options de navigation de base ci-dessous. Pour plus d'informations, veuillez consulter le site",
|
||||
"wiki": "Wiki FileBrowser Quantum"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Copier",
|
||||
"copyMessage": "Choisissez l'emplacement où copier la sélection :",
|
||||
"deleteMessageMultiple": "Êtes-vous sûr de vouloir supprimer ces {count} élément(s) ?",
|
||||
"deleteMessageSingle": "Êtes-vous sûr de vouloir supprimer cet élément ?",
|
||||
"deleteMessageShare": "Êtes-vous sûr de vouloir supprimer ce partage ({path}) ?",
|
||||
"deleteTitle": "Supprimer",
|
||||
"displayName": "Nom :",
|
||||
"download": "Télécharger",
|
||||
"downloadMessage": "Choisissez le format de téléchargement :",
|
||||
"error": "Quelque chose s'est mal passé",
|
||||
"fileInfo": "Informations",
|
||||
"lastModified": "Dernière modification{suffix}",
|
||||
"move": "Déplacer",
|
||||
"moveMessage": "Choisissez l'emplacement où déplacer la sélection :",
|
||||
"newArchetype": "Créer un nouveau post basé sur un archétype. Votre fichier sera créé dans le dossier de contenu.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Nom du nouveau dossier :",
|
||||
"newFile": "Nouveau fichier",
|
||||
"newFileMessage": "Nom du nouveau fichier :",
|
||||
"numberDirs": "Nombre de dossiers{suffix}",
|
||||
"numberFiles": "Nombre de fichiers{suffix}",
|
||||
"rename": "Renommer",
|
||||
"renameMessage": "Nouveau nom pour",
|
||||
"replace": "Remplacer",
|
||||
"replaceMessage": "Un des fichiers que vous êtes en train d'importer a le même nom qu'un autre déjà présent. Voulez-vous remplacer le fichier actuel par le nouveau ?\n",
|
||||
"schedule": "Fixer la date",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Glisser-déposer des fichiers ici",
|
||||
"completed": "Terminé",
|
||||
"conflictsDetected": "Conflits détectés",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nom invalide fourni : ne peut contenir \"/\" ou \"\\\".",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limite du nombre de téléchargements",
|
||||
"maxBandwidth": "Largeur de bande maximale de téléchargement (kbps)",
|
||||
"shareTheme": "Thème de partage",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Partager l'URL de la bannière",
|
||||
"shareFavicon": "Partager l'URL de la favicon",
|
||||
"optionalPasswordHelp": "S'il est défini, les utilisateurs doivent saisir ce mot de passe pour consulter le contenu partagé.",
|
||||
"viewMode": "Mode d'affichage"
|
||||
"viewMode": "Mode d'affichage",
|
||||
"renameMessage": "Saisissez un nouveau nom :",
|
||||
"renameMessageInvalid": "Un fichier ne peut pas contenir \"/\" ou \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"deleteMessageMultiple": "Êtes-vous sûr de vouloir supprimer le(s) fichier(s) {count}?",
|
||||
"deleteMessageShare": "Êtes-vous sûr de vouloir supprimer ce partage : {path} ?",
|
||||
"lastModified": "Dernière modification{suffix}",
|
||||
"numberDirs": "Nombre de répertoires{suffix}",
|
||||
"numberFiles": "Nombre de fichiers{suffix}",
|
||||
"renameMessageConflict": "Un article nommé \"{filename}\" existe déjà !",
|
||||
"uploadSettingsChunked": "Nombre maximum de téléchargements simultanés : {maxConcurrentUpload}, taille de chunk : {uploadChunkSizeMb} MO",
|
||||
"uploadSettingsNoChunk": "Nombre maximum de téléchargements simultanés : {maxConcurrentUpload}, chunked upload désactivé"
|
||||
},
|
||||
"search": {
|
||||
"images": "Images",
|
||||
"music": "Musique",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Appuyez sur entrée pour chercher...",
|
||||
"search": "Recherche en cours...",
|
||||
"typeToSearch": "Écrivez pour chercher...",
|
||||
"types": "Types",
|
||||
"video": "Vidéo",
|
||||
"path": "Chemin :",
|
||||
"smallerThan": "Plus petit que :",
|
||||
"largerThan": "Plus grand que :",
|
||||
"helpText1": "La recherche s'effectue sur chaque caractère saisi (3 caractères minimum pour les termes de recherche).",
|
||||
"helpText2": "Index : La recherche utilise l'index qui est automatiquement mis à jour à l'intervalle configuré (par défaut : 5 minutes). Une recherche effectuée alors que le programme vient de démarrer peut donner des résultats incomplets."
|
||||
"helpText2": "Index : La recherche utilise l'index qui est automatiquement mis à jour à l'intervalle configuré (par défaut : 5 minutes). Une recherche effectuée alors que le programme vient de démarrer peut donner des résultats incomplets.",
|
||||
"noResults": "Aucun résultat n'a été trouvé dans la recherche indexée.",
|
||||
"onlyFolders": "Dossiers uniquement",
|
||||
"onlyFiles": "Uniquement les fichiers",
|
||||
"showOptions": "Afficher les options",
|
||||
"photos": "Photos",
|
||||
"audio": "Audio",
|
||||
"videos": "Vidéos",
|
||||
"documents": "Documents",
|
||||
"archives": "Archives",
|
||||
"number": "Nombre",
|
||||
"searchContext": "Contexte de la recherche : {context}",
|
||||
"notEnoughCharacters": "Pas assez de caractères pour effectuer une recherche (min {minSearchLength})",
|
||||
"typeToSearch": "Commencez à taper {minSearchLength} ou plus de caractères pour lancer la recherche."
|
||||
},
|
||||
"settings": {
|
||||
"scopes": "Définir les sources autorisées et la portée de l'utilisateur au sein de celles-ci",
|
||||
@@ -215,10 +228,8 @@
|
||||
"avoidChanges": "(Laisser vide pour conserver l'actuel)",
|
||||
"branding": "Image de marque",
|
||||
"brandingDirectoryPath": "Chemin du dossier d'image de marque",
|
||||
"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",
|
||||
"commandRunner": "Fichier de commandes",
|
||||
"commandRunnerHelp": "Ici, vous pouvez définir les commandes qui sont exécutées pour les événements nommés précédemment. Vous devez en écrire une par ligne. Les variables d'environnement {0} et {1} seront disponibles, {0} étant relatif à {1}. Pour plus d'informations sur cette fonctionnalité et les variables d'environnement disponibles, veuillez lire la {2}.",
|
||||
"commandsUpdated": "Commandes mises à jour !",
|
||||
"createUserDir": "Créer automatiquement un dossier pour l'utilisateur",
|
||||
"customStylesheet": "Feuille de style personnalisée",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Options de l'utilisateur administrateur",
|
||||
"shareDuration": "Expiration",
|
||||
"searchOptions": "Options de recherche",
|
||||
"downloads": "Téléchargements"
|
||||
"downloads": "Téléchargements",
|
||||
"systemAdmin": "Système et administration",
|
||||
"configViewer": "Config Viewer",
|
||||
"configViewerShowFull": "Afficher la configuration complète",
|
||||
"configViewerShowComments": "Afficher les commentaires",
|
||||
"configViewerLoadConfig": "Configuration de la charge",
|
||||
"brandingHelp": "Vous pouvez personnaliser l'apparence de votre instance du navigateur de fichiers 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, consultez le site {0}.",
|
||||
"commandRunnerHelp": "Vous pouvez définir ici les commandes qui seront exécutées dans les événements nommés. 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 consulter le site {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Aide",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Masquer les boutons de navigation",
|
||||
"hideNavButtonsDescription": "Masquez les boutons de navigation de la barre de navigation dans le partage pour créer une apparence minimaliste.",
|
||||
"viewModeDescription": "Mise en page par défaut de la page partagée.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Une action vous a été envoyée pour que vous la consultiez ou la téléchargiez.",
|
||||
"disableShareCard": "Désactiver la carte de partage",
|
||||
"disableShareCardDescription": "Désactiver la carte de partage sur la page partagée dans la barre latérale ou en haut de la page sur mobile.",
|
||||
"disableSidebar": "Désactiver la barre latérale",
|
||||
"disableSidebarDescription": "Désactiver la barre latérale sur la page partagée."
|
||||
"disableSidebarDescription": "Désactiver la barre latérale sur la page partagée.",
|
||||
"enforceDarkLightMode": "Renforcer le mode thématique",
|
||||
"enforceDarkLightModeDescription": "Forcer un mode de thème spécifique (sombre ou clair) pour ce partage, sans tenir compte des préférences de l'utilisateur.",
|
||||
"default": "Ne pas imposer de mode",
|
||||
"dark": "Sombre",
|
||||
"light": "Lumière",
|
||||
"enableOnlyOffice": "Activer la visionneuse OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Autoriser la visualisation de fichiers bureautiques en utilisant OnlyOffice dans ce partage.",
|
||||
"enableOnlyOfficeEditing": "Activer l'édition OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Autoriser l'édition de fichiers bureautiques en utilisant OnlyOffice dans ce partage.",
|
||||
"titleDefault": "Fichiers partagés - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Clés API :",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Refusé",
|
||||
"totalAllowed": "Autorisé",
|
||||
"all": "Refuser tout",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Ceci montre ce qui se passe lorsqu'un utilisateur tente d'accéder à un chemin pour lequel il n'existe pas de règles d'accès spécifiques. La mention \"Autoriser\" signifie que les utilisateurs peuvent accéder par défaut, la mention \"Refuser\" signifie que les utilisateurs sont bloqués par défaut, à moins qu'ils n'y soient explicitement autorisés."
|
||||
"defaultBehaviorDescription": "Ceci montre ce qui se passe lorsqu'un utilisateur tente d'accéder à un chemin pour lequel il n'existe pas de règles d'accès spécifiques. La mention \"Autoriser\" signifie que les utilisateurs peuvent accéder par défaut, la mention \"Refuser\" signifie que les utilisateurs sont bloqués par défaut, à moins qu'ils n'y soient explicitement autorisés.",
|
||||
"defaultBehavior": "Action par défaut{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Chargements et téléchargements",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Le thème par défaut",
|
||||
"customTheme": "Choisissez votre thème",
|
||||
"showSelectMultiple": "Afficher la sélection multiple dans le menu contextuel sur le bureau",
|
||||
"showSelectMultipleDescription": "Par défaut, le menu contextuel de l'ordinateur de bureau n'a pas l'option \"sélectionner plusieurs\" comme celui de l'ordinateur mobile. Cette option apparaît toujours dans le menu contextuel."
|
||||
"showSelectMultipleDescription": "Par défaut, le menu contextuel de l'ordinateur de bureau n'a pas l'option \"sélectionner plusieurs\" comme celui de l'ordinateur mobile. Cette option apparaît toujours dans le menu contextuel.",
|
||||
"defaultMediaPlayer": "Utilisez le lecteur multimédia natif de votre navigateur",
|
||||
"defaultMediaPlayerDescription": "Utilisez le lecteur multimédia natif de votre navigateur pour les fichiers vidéo et audio plutôt que le lecteur multimédia \"vue-plyr\" inclus dans FileBrowser.",
|
||||
"debugOfficeEditor": "Activer le mode de débogage d'OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Afficher une fenêtre de débogage avec des informations supplémentaires dans l'éditeur OnlyOffice.",
|
||||
"fileViewerOptions": "Options de visualisation des fichiers",
|
||||
"themeAndLanguage": "Thème et langue"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Échec de l'initialisation de l'éditeur. Veuillez recharger.",
|
||||
"saveFailed": "Échec de l'enregistrement du fichier. Veuillez réessayer.",
|
||||
"saveAborted": "Sauvegarde interrompue. Le nom du fichier ne correspond pas au fichier actif.",
|
||||
"saveDisabled": "L'enregistrement de fichiers partagés n'est pas possible.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "L'opération d'enregistrement a été interrompue. Le fichier actif de l'application ({activeFile}) ne correspond pas au fichier que vous essayez d'enregistrer ({tryingToSave}).",
|
||||
"syncFailed": "n'a pas réussi à synchroniser l'état avec l'itinéraire pour \"{filename}\" après 5 tentatives. Abandon de la configuration de l'éditeur pour éviter la corruption des données."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Boucle activée",
|
||||
"LoopDisabled": "Boucle désactivée"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "השלם",
|
||||
"showMore": "הצג עוד",
|
||||
"showLess": "הצג פחות",
|
||||
"openParentFolder": "פתח את התיקייה הראשית"
|
||||
"openParentFolder": "פתח את התיקייה הראשית",
|
||||
"selectedCount": "מספר הפריטים שנבחרו לביצוע פעולה"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "הורד קובץ",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "בחר קובץ או תקייה",
|
||||
"ctrl": {
|
||||
"click": "בחר מספר קבצים או תקיות",
|
||||
"f": "פותח את החיפוש",
|
||||
"s": "לשמור קובץ או להוריד את התקייה שבה אתה נמצא"
|
||||
"d": "הורדת פריטים נבחרים"
|
||||
},
|
||||
"del": "מחק את מה שנבחר",
|
||||
"doubleClick": "פתח קובץ או תקייה",
|
||||
"esc": "נקה את הבחירה ו/או סגור את השדה",
|
||||
"f1": "המידע הזה",
|
||||
"f2": "שנה שם קובץ",
|
||||
"help": "עזרה"
|
||||
"help": "עזרה",
|
||||
"f1": "הצג עזרה",
|
||||
"description": "ניתן לצפות באפשרויות הניווט הבסיסיות להלן. למידע נוסף, בקרו בכתובת",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "העתק",
|
||||
"copyMessage": "בחר לאן להעתיק את הקבצים:",
|
||||
"deleteMessageMultiple": "האם אתה בטוח שברצונך למחוק {count} קבצים?",
|
||||
"deleteMessageSingle": "האם אתה בטוח שברצונך למחוק את הקובץ או התקייה?",
|
||||
"deleteMessageShare": "האם אתה בטוח שברצונך למחוק את השיתוף הזה?({path})?",
|
||||
"deleteTitle": "מחק קבצים",
|
||||
"displayName": "שם:",
|
||||
"download": "הורד קבצים",
|
||||
"downloadMessage": "בחר את הפורמט שברצונך להוריד",
|
||||
"error": "משהו השתבש",
|
||||
"fileInfo": "מידע על הקובץ",
|
||||
"lastModified": "שונה לאחרונה{suffix}",
|
||||
"move": "העבר",
|
||||
"moveMessage": "בחר מיקום חדש לקובץ / תקייה:",
|
||||
"newArchetype": "צור פוסט חדש. הקובץ יווצר בתקיית התוכן",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "כתוב את שם התקייה החדשה",
|
||||
"newFile": "קובץ חדש",
|
||||
"newFileMessage": "כתוב את שם הקובץ החדש",
|
||||
"numberDirs": "מספר התקיות{suffix}",
|
||||
"numberFiles": "מספר הקבצים{suffix}",
|
||||
"rename": "שנה שם",
|
||||
"renameMessage": "הכנס שם חדש עבור",
|
||||
"replace": "החלף",
|
||||
"replaceMessage": "אחד הקבצים בעל שם זהה לקובץ קיים, האם ברצונך להחליף את הקובץ הקיים בחדש? זהירות - הקובץ הישן ימחק\n",
|
||||
"schedule": "תזמון",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "גרור ושחרר קבצים לכאן",
|
||||
"completed": "הושלם",
|
||||
"conflictsDetected": "סכסוכים שזוהו",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "שם לא חוקי: לא ניתן להכיל \"/\" או \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "הגבלת מספר ההורדות",
|
||||
"maxBandwidth": "רוחב פס הורדה מרבי (kbps)",
|
||||
"shareTheme": "שתף נושא",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "שתף כתובת URL של באנר",
|
||||
"shareFavicon": "שתף כתובת URL של סמל המועדפים",
|
||||
"optionalPasswordHelp": "אם אפשרות זו מוגדרת, על המשתמשים להזין סיסמה זו כדי להציג את התוכן המשותף.",
|
||||
"viewMode": "מצב תצוגה"
|
||||
"viewMode": "מצב תצוגה",
|
||||
"renameMessage": "הזן שם חדש:",
|
||||
"renameMessageInvalid": "קובץ לא יכול להכיל את התווים \"/\" או \"\\\"",
|
||||
"source": "מקור{suffix}",
|
||||
"deleteMessageMultiple": "האם אתה בטוח שברצונך למחוק את הקובץ/ים {count}?",
|
||||
"deleteMessageShare": "האם אתה בטוח שברצונך למחוק שיתוף זה: {path}?",
|
||||
"lastModified": "שינוי אחרון{suffix}",
|
||||
"numberDirs": "מספר הספריות{suffix}",
|
||||
"numberFiles": "מספר הקבצים{suffix}",
|
||||
"renameMessageConflict": "פריט בשם \"{filename}\" כבר קיים!",
|
||||
"uploadSettingsChunked": "מספר העלאות מקסימלי בו-זמנית: {maxConcurrentUpload}, גודל קובץ: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "מספר העלאות מקסימלי בו-זמנית: {maxConcurrentUpload}, העלאה מקוטעת מושבתת"
|
||||
},
|
||||
"search": {
|
||||
"images": "תמונות",
|
||||
"music": "מוזיקה",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "הקש אנטר לחיפוש...",
|
||||
"search": "חפש...",
|
||||
"typeToSearch": "הקלד לחיפוש...",
|
||||
"types": "סוג",
|
||||
"video": "וידאו",
|
||||
"path": "נתיב:",
|
||||
"smallerThan": "קטן מ:",
|
||||
"largerThan": "גדול מ:",
|
||||
"helpText1": "החיפוש מתבצע על כל תו שאתה מקליד (מינימום 3 תווים עבור מונחי חיפוש).",
|
||||
"helpText2": "האינדקס: החיפוש משתמש באינדקס שמתעדכן אוטומטית במרווח הזמן שהוגדר (ברירת מחדל: 5 דקות). חיפוש בעת הפעלת התוכנית עלול להניב תוצאות חלקיות."
|
||||
"helpText2": "האינדקס: החיפוש משתמש באינדקס שמתעדכן אוטומטית במרווח הזמן שהוגדר (ברירת מחדל: 5 דקות). חיפוש בעת הפעלת התוכנית עלול להניב תוצאות חלקיות.",
|
||||
"noResults": "לא נמצאו תוצאות בחיפוש באינדקס.",
|
||||
"onlyFolders": "רק תיקיות",
|
||||
"onlyFiles": "רק קבצים",
|
||||
"showOptions": "הצג אפשרויות",
|
||||
"photos": "תמונות",
|
||||
"audio": "אודיו",
|
||||
"videos": "סרטונים",
|
||||
"documents": "מסמכים",
|
||||
"archives": "ארכיונים",
|
||||
"number": "מספר",
|
||||
"searchContext": "הקשר החיפוש: {context}",
|
||||
"notEnoughCharacters": "אין מספיק תווים לחיפוש (מינימום {minSearchLength})",
|
||||
"typeToSearch": "התחל להקליד {minSearchLength} או יותר תווים כדי להתחיל בחיפוש."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "מנהל",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(השאר ריק כדי למנוע שינויים)",
|
||||
"branding": "מיתוג",
|
||||
"brandingDirectoryPath": "נתיב תקיית מיתוג",
|
||||
"brandingHelp": "אתה יכול להגדיר את האופן שבו האפליקציה תראה על ידי שינוי שם האפליקציה, החלפת הלוגו, הוספת עיצוב מותאם אישית ואפילו השבתת קישורים חיצוניים לGithub.\nלמידע נוסף עיין ב-{0}.",
|
||||
"changePassword": "שנה סיסמא",
|
||||
"commandRunner": "הרצת פקודות",
|
||||
"commandRunnerHelp": "אתה יכול להגדיר פקודות שיבוצעו באירועים שונים. עליך לכתוב אחד בכל שורה. משתני הסביבה {0} ו-{1} יהיו זמינים, בהיותם {0} ביחס ל-{1}. למידע נוסף על תכונה זו ועל משתני הסביבה הזמינים, עיין ב {2}.",
|
||||
"commandsUpdated": "הפקודות עודכנו.",
|
||||
"createUserDir": "צור תקיית בית במהלך הוספת משתמש חדש",
|
||||
"userHomeBasePath": "נתיב ראשי לתקיות הבית של משתמשים",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "אפשרויות משתמש מנהל",
|
||||
"shareDuration": "תוקף",
|
||||
"searchOptions": "אפשרויות חיפוש",
|
||||
"downloads": "הורדות"
|
||||
"downloads": "הורדות",
|
||||
"systemAdmin": "מערכת וניהול",
|
||||
"configViewer": "מציג תצורה",
|
||||
"configViewerShowFull": "הצג את התצורה המלאה",
|
||||
"configViewerShowComments": "הצג תגובות",
|
||||
"configViewerLoadConfig": "טען תצורה",
|
||||
"brandingHelp": "ניתן להתאים אישית את המראה והתחושה של דפדפן הקבצים על ידי שינוי שמו, החלפת הלוגו, הוספת סגנונות מותאמים אישית ואפילו השבתת קישורים חיצוניים ל-GitHub.\nלמידע נוסף על מיתוג מותאם אישית, בקרו ב- {0}.",
|
||||
"commandRunnerHelp": "כאן תוכל להגדיר פקודות שיבוצעו באירועים המוזכרים. עליך לכתוב פקודה אחת בכל שורה. המשתנים הסביבתיים {0} ו- {1} יהיו זמינים, והם יהיו {0} ביחס ל- {1}. למידע נוסף על תכונה זו ועל המשתנים הסביבתיים הזמינים, אנא קרא את {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "עזרה",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "הסתר כפתורי ניווט",
|
||||
"hideNavButtonsDescription": "הסתר את כפתורי הניווט בסרגל הניווט בשיתוף כדי ליצור מראה מינימליסטי.",
|
||||
"viewModeDescription": "פריסת ברירת המחדל עבור הדף המשותף.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "נשלחה אליך קובץ לשיתוף או להורדה.",
|
||||
"disableShareCard": "השבת כרטיס שיתוף",
|
||||
"disableShareCardDescription": "השבת את כרטיס השיתוף בדף המשותף בסרגל הצד או בחלק העליון של הדף במכשירים ניידים.",
|
||||
"disableSidebar": "השבת סרגל צד",
|
||||
"disableSidebarDescription": "השבת את סרגל הצד בדף המשותף."
|
||||
"disableSidebarDescription": "השבת את סרגל הצד בדף המשותף.",
|
||||
"enforceDarkLightMode": "אכוף מצב ערכת נושא",
|
||||
"enforceDarkLightModeDescription": "כפה מצב ערכת נושא ספציפי (כהה או בהיר) עבור שיתוף זה, תוך עקיפת העדפות המשתמש.",
|
||||
"default": "אל תכפה מצב",
|
||||
"dark": "אפל",
|
||||
"light": "אור",
|
||||
"enableOnlyOffice": "הפעל את תוכנת הצפייה OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "אפשר צפייה בקבצי Office באמצעות OnlyOffice בשיתוף זה.",
|
||||
"enableOnlyOfficeEditing": "אפשר עריכה ב-OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "אפשר עריכת קבצי Office באמצעות OnlyOffice בשיתוף זה.",
|
||||
"titleDefault": "קבצים משותפים - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "מפתחות API",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "נדחה",
|
||||
"totalAllowed": "מותר",
|
||||
"all": "דחה הכל",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "זה מראה מה קורה כאשר משתמש מנסה לגשת לנתיב שבו אין כללי גישה ספציפיים. 'אפשר' פירושו שמשתמשים יכולים לגשת כברירת מחדל, 'דחה' פירושו שמשתמשים נחסמים כברירת מחדל, אלא אם כן הותר להם במפורש."
|
||||
"defaultBehaviorDescription": "זה מראה מה קורה כאשר משתמש מנסה לגשת לנתיב שבו אין כללי גישה ספציפיים. 'אפשר' פירושו שמשתמשים יכולים לגשת כברירת מחדל, 'דחה' פירושו שמשתמשים נחסמים כברירת מחדל, אלא אם כן הותר להם במפורש.",
|
||||
"defaultBehavior": "פעולה ברירת מחדל{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "העלאות והורדות",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "ברירת מחדל - ערכת הנושא המוגדרת כברירת מחדל",
|
||||
"customTheme": "בחר את הנושא שלך",
|
||||
"showSelectMultiple": "הצג בחירה מרובה בתפריט ההקשר בשולחן העבודה",
|
||||
"showSelectMultipleDescription": "כברירת מחדל, בתפריט ההקשר של שולחן העבודה אין אפשרות \"בחר מספר פריטים\" כמו במכשירים ניידים. אפשרות זו תמיד מוצגת בתפריט ההקשר."
|
||||
"showSelectMultipleDescription": "כברירת מחדל, בתפריט ההקשר של שולחן העבודה אין אפשרות \"בחר מספר פריטים\" כמו במכשירים ניידים. אפשרות זו תמיד מוצגת בתפריט ההקשר.",
|
||||
"defaultMediaPlayer": "השתמש בנגן המדיה המובנה בדפדפן שלך",
|
||||
"defaultMediaPlayerDescription": "השתמש בנגן המדיה המובנה בדפדפן שלך עבור קבצי וידאו ואודיו, במקום בנגן המדיה 'vue-plyr' הכלול ב-FileBrowser.",
|
||||
"debugOfficeEditor": "הפעל מצב ניפוי באגים של OnlyOffice",
|
||||
"debugOfficeEditorDescription": "הצג חלון קופץ לאיתור באגים עם מידע נוסף בעורך OnlyOffice.",
|
||||
"fileViewerOptions": "אפשרויות תצוגת קבצים",
|
||||
"themeAndLanguage": "נושא ושפה"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "לא ניתן לאתחל את העורך. אנא טען מחדש.",
|
||||
"saveFailed": "לא ניתן לשמור את הקובץ. אנא נסה שוב.",
|
||||
"saveAborted": "שמירה בוטלה. שם הקובץ אינו תואם לקובץ הפעיל.",
|
||||
"saveDisabled": "שמירת קבצים משותפים אינה נתמכת.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "פעולת השמירה בוטלה. הקובץ הפעיל של היישום ({activeFile}) אינו תואם לקובץ שאתה מנסה לשמור ({tryingToSave}).",
|
||||
"syncFailed": "לא הצליח לסנכרן את המצב עם המסלול עבור \"{filename}\" לאחר 5 ניסיונות. מבטל את הגדרת העורך כדי למנוע פגיעה בנתונים."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "לולאה מופעלת",
|
||||
"LoopDisabled": "לולאה מושבתת"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Tiszta befejezve",
|
||||
"showMore": "Többet mutasson",
|
||||
"showLess": "Kevesebb mutatása",
|
||||
"openParentFolder": "Szülői mappa megnyitása"
|
||||
"openParentFolder": "Szülői mappa megnyitása",
|
||||
"selectedCount": "A művelet végrehajtásához kiválasztott elemek száma"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Fájl letöltése",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "mappa vagy fájl kijelölése",
|
||||
"ctrl": {
|
||||
"click": "több mappa vagy fájl kijelölése",
|
||||
"f": "keresés megnyitása",
|
||||
"s": "az aktuális fájl vagy mappa letöltése"
|
||||
"d": "kiválasztott elemek letöltése"
|
||||
},
|
||||
"del": "kijelölt elemek törlése",
|
||||
"doubleClick": "fájl vagy mappa megnyitása",
|
||||
"esc": "kijelölés törlése és/vagy parancssor bezárása",
|
||||
"f1": "ezen információ megjelenítése",
|
||||
"f2": "fájl átnevezése",
|
||||
"help": "Súgó"
|
||||
"help": "Súgó",
|
||||
"f1": "show help prompt",
|
||||
"description": "Az alábbiakban megtekintheti az alapvető navigációs lehetőségeket. További információkért kérjük, látogasson el a következő weboldalra",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Másolása",
|
||||
"copyMessage": "Válassza ki a másolás célját:",
|
||||
"deleteMessageMultiple": "Biztosan törölni szeretne {count} fájlt?",
|
||||
"deleteMessageSingle": "Biztosan törölni szeretné ezt a fájl vagy mappát?",
|
||||
"deleteMessageShare": "Biztosan törölni szeretné ezt a megosztást ({path})?",
|
||||
"deleteTitle": "Fájlok törlése",
|
||||
"displayName": "Megjelenített név:",
|
||||
"download": "Fájlok letöltése",
|
||||
"downloadMessage": "Válassza ki a letöltés formátumát.",
|
||||
"error": "Valami rosszul sült el",
|
||||
"fileInfo": "Fájlinformáció",
|
||||
"lastModified": "Utolsó módosítás{suffix}",
|
||||
"move": "Mozgatás",
|
||||
"moveMessage": "Válasszon új helyet a fájl(ok)nak/mappá(k)nak:",
|
||||
"newArchetype": "Új bejegyzést hoz létre egy archetípus alapján. A fájl a tartalom mappában jön létre.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Adja meg az új mappa nevét.",
|
||||
"newFile": "Új fájl",
|
||||
"newFileMessage": "Adja meg az új fájl nevét.",
|
||||
"numberDirs": "Mappák száma{suffix}",
|
||||
"numberFiles": "Fájlok száma{suffix}",
|
||||
"rename": "Átnevezés",
|
||||
"renameMessage": "Adja meg az új nevét:",
|
||||
"replace": "Csere",
|
||||
"replaceMessage": "Az egyik feltölteni kívánt fájl a neve miatt ütközik. Szeretné lecserélni a meglévő fájlt?\n",
|
||||
"schedule": "Ütemezés",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Húzza ide a fájlokat",
|
||||
"completed": "Befejezett",
|
||||
"conflictsDetected": "Feltárt konfliktusok",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Érvénytelen név: nem lehet \"/\" vagy \"\\\" konténer",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Letöltések száma limit",
|
||||
"maxBandwidth": "Maximális letöltési sávszélesség (kbps)",
|
||||
"shareTheme": "Megosztás témája",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Banner URL megosztása",
|
||||
"shareFavicon": "Favicon URL megosztása",
|
||||
"optionalPasswordHelp": "Ha be van állítva, a felhasználóknak ezt a jelszót kell megadniuk a megosztott tartalom megtekintéséhez.",
|
||||
"viewMode": "Nézet mód"
|
||||
"viewMode": "Nézet mód",
|
||||
"renameMessage": "Adjon meg egy új nevet:",
|
||||
"renameMessageInvalid": "Egy fájl nem tartalmazhat \"/\" vagy \"\\\" konténert.",
|
||||
"source": "Forrás{suffix}",
|
||||
"deleteMessageMultiple": "Biztos, hogy törölni szeretné a {count} fájl(ok)at?",
|
||||
"deleteMessageShare": "Biztos, hogy törölni szeretné ezt a megosztást: {path} ?",
|
||||
"lastModified": "Utoljára módosítva{suffix}",
|
||||
"numberDirs": "A könyvtárak száma{suffix}",
|
||||
"numberFiles": "Fájlok száma{suffix}",
|
||||
"renameMessageConflict": "A \"{filename}\" nevű elem már létezik!",
|
||||
"uploadSettingsChunked": "Maximális egyidejű feltöltés: {maxConcurrentUpload}, chunk méret: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Maximális egyidejű feltöltés: {maxConcurrentUpload}, chunked upload disabled"
|
||||
},
|
||||
"search": {
|
||||
"images": "Képek",
|
||||
"music": "Zene",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Keresés indítása Enterrel…",
|
||||
"search": "Keresés…",
|
||||
"typeToSearch": "Keresés indítása beírással…",
|
||||
"types": "Típusok",
|
||||
"video": "Videó",
|
||||
"path": "Útvonal:",
|
||||
"smallerThan": "Kisebb, mint:",
|
||||
"largerThan": "Nagyobb, mint:",
|
||||
"helpText1": "A keresés minden egyes beírt karakterre történik (minimum 3 karakteres keresőkifejezés).",
|
||||
"helpText2": "Az index: A keresés az indexet használja, amely automatikusan frissül a beállított időközönként (alapértelmezett: 5 perc). A program indulásakor végzett keresés hiányos eredményeket eredményezhet."
|
||||
"helpText2": "Az index: A keresés az indexet használja, amely automatikusan frissül a beállított időközönként (alapértelmezett: 5 perc). A program indulásakor végzett keresés hiányos eredményeket eredményezhet.",
|
||||
"noResults": "Az indexelt keresésben nem találtak találatot.",
|
||||
"onlyFolders": "Csak mappák",
|
||||
"onlyFiles": "Csak fájlok",
|
||||
"showOptions": "Beállítások megjelenítése",
|
||||
"photos": "Fotók",
|
||||
"audio": "Audio",
|
||||
"videos": "Videók",
|
||||
"documents": "Dokumentumok",
|
||||
"archives": "Archívum",
|
||||
"number": "Szám",
|
||||
"searchContext": "Keresési kontextus: {context}",
|
||||
"notEnoughCharacters": "Nincs elég karakter a kereséshez (min {minSearchLength})",
|
||||
"typeToSearch": "A keresés megkezdéséhez írja be a {minSearchLength} vagy több karaktert."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(üresen hagyva nincs változás)",
|
||||
"branding": "Márkázás",
|
||||
"brandingDirectoryPath": "Márkázás mappaútvonala",
|
||||
"brandingHelp": "Testre szabhatja a File Browser példányának megjelenését a név megváltoztatásával, a logó cseréjével, egyéni stílusok hozzáadásával és még a GitHubra mutató külső hivatkozások letiltásával is.\nAz egyéni márkázással kapcsolatos további információkért tekintse meg: {0}.",
|
||||
"changePassword": "Jelszó módosítása",
|
||||
"commandRunner": "Parancsfuttató",
|
||||
"commandRunnerHelp": "Beállíthatja azokat a parancsokat, amelyek a megnevezett események során végrehajtásra kerülnek. Soronként egyet kell megadni. A {0} és a {1} környezeti változók lesznek elérhetőek, ahol a {0} relatív a {1}-hez. A funkcióról és a rendelkezésre álló környezeti változókról további információ: {2}.",
|
||||
"commandsUpdated": "Parancsok frissítve!",
|
||||
"createUserDir": "Felhasználók saját mappáinak automatikus létrehozása új felhasználók hozzáadásakor",
|
||||
"userHomeBasePath": "Alap elérési útvonal a felhasználók saját mappáihoz",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Admin felhasználói beállítások",
|
||||
"shareDuration": "Lejárat:",
|
||||
"searchOptions": "Keresési lehetőségek",
|
||||
"downloads": "Letöltések"
|
||||
"downloads": "Letöltések",
|
||||
"systemAdmin": "Rendszer és adminisztráció",
|
||||
"configViewer": "Konfiguráció megtekintő",
|
||||
"configViewerShowFull": "Teljes konfiguráció megjelenítése",
|
||||
"configViewerShowComments": "Hozzászólások megjelenítése",
|
||||
"configViewerLoadConfig": "Konfiguráció betöltése",
|
||||
"brandingHelp": "Testreszabhatja a Fájlböngésző példányának megjelenését és érzetét a név megváltoztatásával, a logó cseréjével, egyéni stílusok hozzáadásával és akár a GitHubra mutató külső hivatkozások letiltásával.\nAz egyéni márkaépítéssel kapcsolatos további információkat a {0} oldalon talál.",
|
||||
"commandRunnerHelp": "Itt állíthatja be azokat a parancsokat, amelyek a megnevezett események során végrehajtásra kerülnek. Soronként egyet kell írni. A {0} és a {1} környezeti változók lesznek elérhetőek, a {0} a {1}-hoz képest . A funkcióról és a rendelkezésre álló környezeti változókról bővebb információt a {2} oldalon talál."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Súgó",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Navigációs gombok elrejtése",
|
||||
"hideNavButtonsDescription": "Rejtse el a navigációs gombokat a megosztás navigációs sávján a minimalista megjelenés érdekében.",
|
||||
"viewModeDescription": "A megosztott oldal alapértelmezett elrendezése.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Megosztást küldtünk Önnek megtekintésre vagy letöltésre.",
|
||||
"disableShareCard": "Megosztási kártya letiltása",
|
||||
"disableShareCardDescription": "Kapcsolja ki a megosztási kártyát a megosztott oldalon az oldalsávban vagy mobilon az oldal tetején.",
|
||||
"disableSidebar": "Oldalsáv kikapcsolása",
|
||||
"disableSidebarDescription": "Kapcsolja ki az oldalsávot a megosztott oldalon."
|
||||
"disableSidebarDescription": "Kapcsolja ki az oldalsávot a megosztott oldalon.",
|
||||
"enforceDarkLightMode": "Témamód érvényesítése",
|
||||
"enforceDarkLightModeDescription": "Egy adott témamód (sötét vagy világos) kikényszerítése a megosztás számára, felülírva a felhasználói beállításokat.",
|
||||
"default": "Ne kényszerítsen ki üzemmódot",
|
||||
"dark": "Sötét",
|
||||
"light": "Fény",
|
||||
"enableOnlyOffice": "OnlyOffice megjelenítő engedélyezése",
|
||||
"enableOnlyOfficeDescription": "Engedélyezze az irodai fájlok megtekintését az OnlyOffice használatával ezen a megosztáson.",
|
||||
"enableOnlyOfficeEditing": "Engedélyezze az OnlyOffice szerkesztést",
|
||||
"enableOnlyOfficeEditingDescription": "Engedélyezze az irodai fájlok szerkesztését az OnlyOffice használatával ezen a megosztáson.",
|
||||
"titleDefault": "Megosztott fájlok - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API-kulcsok:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Elutasítva",
|
||||
"totalAllowed": "Engedélyezett",
|
||||
"all": "Mindent megtagadni",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Ez azt mutatja, hogy mi történik, ha egy felhasználó megpróbál hozzáférni egy olyan elérési útvonalhoz, ahol nincsenek speciális hozzáférési szabályok. Az 'Allow' azt jelenti, hogy a felhasználók alapértelmezés szerint hozzáférhetnek, a 'Deny' azt jelenti, hogy a felhasználók alapértelmezés szerint blokkolva vannak, kivéve, ha kifejezetten engedélyezik."
|
||||
"defaultBehaviorDescription": "Ez azt mutatja, hogy mi történik, ha egy felhasználó megpróbál hozzáférni egy olyan elérési útvonalhoz, ahol nincsenek speciális hozzáférési szabályok. Az 'Allow' azt jelenti, hogy a felhasználók alapértelmezés szerint hozzáférhetnek, a 'Deny' azt jelenti, hogy a felhasználók alapértelmezés szerint blokkolva vannak, kivéve, ha kifejezetten engedélyezik.",
|
||||
"defaultBehavior": "Alapértelmezett cselekvés{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Feltöltések és letöltések",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Az alapértelmezett téma",
|
||||
"customTheme": "Válasszon témát",
|
||||
"showSelectMultiple": "Többszörös kijelölés megjelenítése az asztali kontextusmenüben",
|
||||
"showSelectMultipleDescription": "Alapértelmezés szerint az asztali kontextusmenü nem rendelkezik a \"többszörös kiválasztás\" opcióval, mint a mobilon. Ez az opció mindig megjelenik a kontextusmenüben."
|
||||
"showSelectMultipleDescription": "Alapértelmezés szerint az asztali kontextusmenü nem rendelkezik a \"többszörös kiválasztás\" opcióval, mint a mobilon. Ez az opció mindig megjelenik a kontextusmenüben.",
|
||||
"defaultMediaPlayer": "Használja a böngésző natív médialejátszóját",
|
||||
"defaultMediaPlayerDescription": "Használja a böngésző natív médialejátszóját a videó- és hangfájlokhoz, nem pedig a FileBrowser mellékelt \"vue-plyr\" médialejátszóját.",
|
||||
"debugOfficeEditor": "OnlyOffice hibakeresési mód engedélyezése",
|
||||
"debugOfficeEditorDescription": "További információkat tartalmazó hibakeresési felugró ablak megjelenítése az OnlyOffice szerkesztőben.",
|
||||
"fileViewerOptions": "Fájlnéző beállításai",
|
||||
"themeAndLanguage": "Téma és nyelv"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Nem sikerült inicializálni a szerkesztőt. Kérjük, töltse újra.",
|
||||
"saveFailed": "A fájl mentése sikertelen. Kérjük, próbálja újra.",
|
||||
"saveAborted": "A mentés megszakadt. A fájl neve nem egyezik az aktív fájléval.",
|
||||
"saveDisabled": "A megosztott fájlok mentése nem támogatott.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "A mentési művelet megszakadt. Az alkalmazás aktív fájlja ({activeFile}) nem egyezik meg a menteni kívánt fájllal ({tryingToSave}).",
|
||||
"syncFailed": "5 próbálkozás után nem sikerült szinkronizálni az állapotot a \"{filename}\" útvonallal. A szerkesztő beállításának megszakítása az adatok sérülésének elkerülése érdekében."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Hurok engedélyezve",
|
||||
"LoopDisabled": "Hurok letiltva"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Completato il lavoro di pulizia",
|
||||
"showMore": "Mostra di più",
|
||||
"showLess": "Mostra meno",
|
||||
"openParentFolder": "Aprire la cartella madre"
|
||||
"openParentFolder": "Aprire la cartella madre",
|
||||
"selectedCount": "Numero di elementi selezionati per eseguire un'azione"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Scarica file",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "seleziona un file o una cartella",
|
||||
"ctrl": {
|
||||
"click": "seleziona più file o cartelle",
|
||||
"f": "apre la barra di ricerca",
|
||||
"s": "salva un file o scarica la cartella in cui ci si trova"
|
||||
"d": "scaricare gli articoli selezionati"
|
||||
},
|
||||
"del": "elimina gli elementi selezionati",
|
||||
"doubleClick": "apre un file o una cartella",
|
||||
"esc": "annulla la selezione e/o chiude la finestra aperta",
|
||||
"f1": "questo pannello",
|
||||
"f2": "rinomina un file",
|
||||
"help": "Aiuto"
|
||||
"help": "Aiuto",
|
||||
"f1": "mostra la richiesta di aiuto",
|
||||
"description": "Le opzioni di navigazione di base sono riportate di seguito. Per ulteriori informazioni, visitate il sito",
|
||||
"wiki": "Wiki di FileBrowser Quantum"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "Copia",
|
||||
"copyMessage": "Seleziona la cartella in cui copiare i file:",
|
||||
"deleteMessageMultiple": "Sei sicuro di voler eliminare {count} file?",
|
||||
"deleteMessageSingle": "Sei sicuro di voler eliminare questo file/cartella?",
|
||||
"deleteTitle": "Elimina",
|
||||
"displayName": "Nome visualizzato:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "Seleziona il formato che vuoi scaricare.",
|
||||
"error": "Qualcosa è andato per il verso storto",
|
||||
"fileInfo": "Informazioni sul file",
|
||||
"lastModified": "Ultima modifica{suffix}",
|
||||
"move": "Sposta",
|
||||
"moveMessage": "Seleziona la nuova posizione per i tuoi file e/o cartella/e:",
|
||||
"newArchetype": "Crea un nuovo post basato su un modello. Il tuo file verrà creato nella cartella.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "Scrivi il nome della nuova cartella.",
|
||||
"newFile": "Nuovo file",
|
||||
"newFileMessage": "Scrivi il nome del nuovo file.",
|
||||
"numberDirs": "Numero di cartelle{suffix}",
|
||||
"numberFiles": "Numero di file{suffix}",
|
||||
"rename": "Rinomina",
|
||||
"renameMessage": "Inserisci un nuovo nome per",
|
||||
"replace": "Sostituisci",
|
||||
"replaceMessage": "Uno dei file che stai cercando di caricare sta generando un conflitto per via del suo nome. Desideri sostituire il file già esistente?\n",
|
||||
"schedule": "Pianifica",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "Mostra",
|
||||
"size": "Dimensione",
|
||||
"upload": "Carica",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "Siete sicuri di voler eliminare questo utente?",
|
||||
"filesSelected": "file selezionati",
|
||||
"optionalPassword": "Password opzionale",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Trascinare e rilasciare i file qui",
|
||||
"completed": "Completato",
|
||||
"conflictsDetected": "Conflitti rilevati",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nome non valido: non può contenere \"/\" o \"\\\".",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limite del numero di download",
|
||||
"maxBandwidth": "Larghezza di banda massima per il download (kbps)",
|
||||
"shareTheme": "Tema della condivisione",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Condividi l'URL del banner",
|
||||
"shareFavicon": "Condividere l'URL della favicon",
|
||||
"optionalPasswordHelp": "Se impostata, gli utenti devono inserire questa password per visualizzare il contenuto condiviso.",
|
||||
"viewMode": "Modalità di visualizzazione"
|
||||
"viewMode": "Modalità di visualizzazione",
|
||||
"renameMessage": "Inserire un nuovo nome:",
|
||||
"renameMessageInvalid": "Un file non può contenere \"/\" o \"\\\".",
|
||||
"source": "Fonte{suffix}",
|
||||
"deleteMessageMultiple": "Siete sicuri di voler eliminare i file {count}?",
|
||||
"deleteMessageShare": "Sei sicuro di voler eliminare questa condivisione: {path} ?",
|
||||
"lastModified": "Ultima modifica{suffix}",
|
||||
"numberDirs": "Numero di directory{suffix}",
|
||||
"numberFiles": "Numero di file{suffix}",
|
||||
"renameMessageConflict": "Un elemento chiamato \"{filename}\" esiste già!",
|
||||
"uploadSettingsChunked": "Carichi massimi contemporanei: {maxConcurrentUpload}, dimensione dei pezzi: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Carichi massimi contemporanei: {maxConcurrentUpload}, caricamento chunked disabilitato"
|
||||
},
|
||||
"search": {
|
||||
"images": "Immagini",
|
||||
"music": "Musica",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Premi Invio per cercare...",
|
||||
"search": "Cerca...",
|
||||
"typeToSearch": "Scrivi per cercare...",
|
||||
"types": "Tipi",
|
||||
"video": "Video",
|
||||
"path": "Percorso:",
|
||||
"smallerThan": "Più piccolo di:",
|
||||
"largerThan": "Più grande di:",
|
||||
"helpText1": "La ricerca avviene per ogni carattere digitato (minimo 3 caratteri per i termini di ricerca).",
|
||||
"helpText2": "L'indice: La ricerca utilizza l'indice che viene aggiornato automaticamente all'intervallo configurato (default: 5 minuti). La ricerca quando il programma è appena stato avviato può dare risultati incompleti."
|
||||
"helpText2": "L'indice: La ricerca utilizza l'indice che viene aggiornato automaticamente all'intervallo configurato (default: 5 minuti). La ricerca quando il programma è appena stato avviato può dare risultati incompleti.",
|
||||
"noResults": "Nessun risultato trovato nella ricerca indicizzata.",
|
||||
"onlyFolders": "Solo cartelle",
|
||||
"onlyFiles": "Solo file",
|
||||
"showOptions": "Mostra opzioni",
|
||||
"photos": "Foto",
|
||||
"audio": "Audio",
|
||||
"videos": "Video",
|
||||
"documents": "Documenti",
|
||||
"archives": "Archivio",
|
||||
"number": "Numero",
|
||||
"searchContext": "Contesto di ricerca: {context}",
|
||||
"notEnoughCharacters": "Non ci sono abbastanza caratteri per la ricerca (min {minSearchLength})",
|
||||
"typeToSearch": "Iniziare a digitare {minSearchLength} o più caratteri per avviare la ricerca."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(lascia vuoto per evitare cambiamenti)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Directory del branding",
|
||||
"brandingHelp": "Puoi personalizzare l'aspetto e il comportamento di File Browser cambiando il suo nome, logo, aggiungendo stili personalizzati e anche disabilitando link esterni verso GitHub.\nPer altre informazioni sul branding personalizzato, vai alla {0}.",
|
||||
"changePassword": "Modifica password",
|
||||
"commandRunner": "Esecutore di comandi",
|
||||
"commandRunnerHelp": "Qui puoi impostare i comandi da eseguire negli eventi nominati. Ne devi scrivere uno per riga. Le variabili d'ambiente {0} e {1} sono disponibili, essendo {0} relativo a {1}. Per altre informazioni su questa funzionalità e sulle variabili d'ambiente utilizzabili, leggi la {2}.",
|
||||
"commandsUpdated": "Comandi aggiornati!",
|
||||
"createUserDir": "Crea automaticamente la home directory dell'utente quando lo aggiungi",
|
||||
"customStylesheet": "Foglio di stile personalizzato",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Opzioni dell'utente amministratore",
|
||||
"shareDuration": "Scadenza",
|
||||
"searchOptions": "Opzioni di ricerca",
|
||||
"downloads": "Scaricamento"
|
||||
"downloads": "Scaricamento",
|
||||
"systemAdmin": "Sistema e amministrazione",
|
||||
"configViewer": "Visualizzatore di configurazione",
|
||||
"configViewerShowFull": "Mostra la configurazione completa",
|
||||
"configViewerShowComments": "Mostra i commenti",
|
||||
"configViewerLoadConfig": "Configurazione del carico",
|
||||
"brandingHelp": "È possibile personalizzare l'aspetto dell'istanza del File Browser cambiandone il nome, sostituendo il logo, aggiungendo stili personalizzati e persino disabilitando i collegamenti esterni a GitHub.\nPer ulteriori informazioni sul branding personalizzato, consultare il sito {0}.",
|
||||
"commandRunnerHelp": "Qui è possibile impostare i comandi che vengono eseguiti negli eventi denominati. È necessario scriverne uno per riga. Le variabili d'ambiente {0} e {1} saranno disponibili, essendo {0} relativa a {1}. Per ulteriori informazioni su questa funzione e sulle variabili d'ambiente disponibili, leggere {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Aiuto",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Nascondere i pulsanti di navigazione",
|
||||
"hideNavButtonsDescription": "Nascondere i pulsanti di navigazione sulla navbar nella condivisione per creare un look minimalista.",
|
||||
"viewModeDescription": "Layout predefinito per la pagina condivisa.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "È stata inviata una condivisione da visualizzare o scaricare.",
|
||||
"disableShareCard": "Disattivare la scheda di condivisione",
|
||||
"disableShareCardDescription": "Disattivare la scheda di condivisione sulla pagina condivisa nella barra laterale o nella parte superiore della pagina su mobile.",
|
||||
"disableSidebar": "Disattivare la barra laterale",
|
||||
"disableSidebarDescription": "Disattivare la barra laterale della pagina condivisa."
|
||||
"disableSidebarDescription": "Disattivare la barra laterale della pagina condivisa.",
|
||||
"enforceDarkLightMode": "Applicare la modalità a tema",
|
||||
"enforceDarkLightModeDescription": "Forza una modalità tema specifica (scuro o chiaro) per questa condivisione, ignorando le preferenze dell'utente.",
|
||||
"default": "Non applicare una modalità",
|
||||
"dark": "Scuro",
|
||||
"light": "Luce",
|
||||
"enableOnlyOffice": "Abilitate il visualizzatore OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Consente di visualizzare i file di Office utilizzando OnlyOffice in questa condivisione.",
|
||||
"enableOnlyOfficeEditing": "Abilita la modifica di OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Consentire la modifica dei file di Office utilizzando OnlyOffice in questa condivisione.",
|
||||
"titleDefault": "File condivisi - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Chiavi API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Negato",
|
||||
"totalAllowed": "Consentito",
|
||||
"all": "Rifiuta tutto",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Questo mostra cosa succede quando un utente cerca di accedere a un percorso per il quale non esistono regole di accesso specifiche. 'Consenti' significa che gli utenti possono accedere per impostazione predefinita, 'Rifiuta' significa che gli utenti sono bloccati per impostazione predefinita, a meno che non siano esplicitamente autorizzati."
|
||||
"defaultBehaviorDescription": "Questo mostra cosa succede quando un utente cerca di accedere a un percorso per il quale non esistono regole di accesso specifiche. 'Consenti' significa che gli utenti possono accedere per impostazione predefinita, 'Rifiuta' significa che gli utenti sono bloccati per impostazione predefinita, a meno che non siano esplicitamente autorizzati.",
|
||||
"defaultBehavior": "Azione predefinita{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Caricamenti e download",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Il tema predefinito",
|
||||
"customTheme": "Scegliete il vostro tema",
|
||||
"showSelectMultiple": "Mostra selezione multipla nel menu contestuale sul desktop",
|
||||
"showSelectMultipleDescription": "Per impostazione predefinita, il menu contestuale del desktop non dispone dell'opzione \"seleziona più\" come quello del cellulare. Questa opzione viene sempre visualizzata nel menu contestuale."
|
||||
"showSelectMultipleDescription": "Per impostazione predefinita, il menu contestuale del desktop non dispone dell'opzione \"seleziona più\" come quello del cellulare. Questa opzione viene sempre visualizzata nel menu contestuale.",
|
||||
"defaultMediaPlayer": "Utilizzare il lettore multimediale nativo del browser",
|
||||
"defaultMediaPlayerDescription": "Utilizzate il lettore multimediale nativo del vostro browser per i file video e audio piuttosto che il lettore multimediale \"vue-plyr\" incluso in FileBrowser.",
|
||||
"debugOfficeEditor": "Abilita la modalità di debug di OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Mostra un popup di debug con informazioni aggiuntive nell'editor di OnlyOffice.",
|
||||
"fileViewerOptions": "Opzioni del visualizzatore di file",
|
||||
"themeAndLanguage": "Tema e linguaggio"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Impossibile inizializzare l'editor. Ricaricare.",
|
||||
"saveFailed": "Impossibile salvare il file. Riprovare.",
|
||||
"saveAborted": "Salvataggio interrotto. Il nome del file non corrisponde al file attivo.",
|
||||
"saveDisabled": "Il salvataggio di file condivisi non è supportato.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operazione di salvataggio interrotta. Il file attivo dell'applicazione ({activeFile}) non corrisponde al file che si sta cercando di salvare ({tryingToSave}).",
|
||||
"syncFailed": "non è riuscito a sincronizzare lo stato con il percorso per \"{filename}\" dopo 5 tentativi. Interruzione dell'impostazione dell'editor per evitare la corruzione dei dati."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Loop abilitato",
|
||||
"LoopDisabled": "Loop disabilitato"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "クリア完了",
|
||||
"showMore": "もっと見る",
|
||||
"showLess": "もっと見る",
|
||||
"openParentFolder": "親フォルダを開く"
|
||||
"openParentFolder": "親フォルダを開く",
|
||||
"selectedCount": "アクションを実行するために選択された項目の数"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "ファイルやディレクトリを選択",
|
||||
"ctrl": {
|
||||
"click": "複数のファイルやディレクトリを選択",
|
||||
"f": "検索を有効にする",
|
||||
"s": "ファイルを保存またはカレントディレクトリをダウンロード"
|
||||
"d": "ダウンロード"
|
||||
},
|
||||
"del": "選択した項目を削除",
|
||||
"doubleClick": "ファイルやディレクトリをオープン",
|
||||
"esc": "選択をクリアーまたはプロンプトを閉じる",
|
||||
"f1": "このヘルプを表示",
|
||||
"f2": "ファイルの名前を変更",
|
||||
"help": "ヘルプ"
|
||||
"help": "ヘルプ",
|
||||
"f1": "ヘルププロンプトを表示する",
|
||||
"description": "基本的なナビオプションは以下からご覧いただけます。その他の情報については",
|
||||
"wiki": "ファイルブラウザ クォンタム ウィキ"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "コピー",
|
||||
"copyMessage": "コピーの目標ディレクトリを選択してください:",
|
||||
"deleteMessageMultiple": "{count} つのファイルを本当に削除してよろしいですか。",
|
||||
"deleteMessageSingle": "このファイル/フォルダを本当に削除してよろしいですか。",
|
||||
"deleteTitle": "ファイルを削除",
|
||||
"displayName": "名前:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "圧縮形式を選択してください。",
|
||||
"error": "あるエラーが発生しました。",
|
||||
"fileInfo": "ファイル情報",
|
||||
"lastModified": "最終変更{suffix}",
|
||||
"move": "移動",
|
||||
"moveMessage": "移動の目標ディレクトリを選択してください:",
|
||||
"newArchetype": "ある元型に基づいて新しいポストを作成します。ファイルは コンテンツフォルダに作成されます。",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "新しいディレクトリの名前を入力してください。",
|
||||
"newFile": "新しいファイルを作成",
|
||||
"newFileMessage": "新しいファイルの名前を入力してください。",
|
||||
"numberDirs": "ディレクトリ個数{suffix}",
|
||||
"numberFiles": "ファイル個数{suffix}",
|
||||
"rename": "名前を変更",
|
||||
"renameMessage": "名前を変更しようファイルは:",
|
||||
"replace": "置き換える",
|
||||
"replaceMessage": "アップロードするファイルの中でかち合う名前が一つあります。 既存のファイルを置き換えりませんか。\n",
|
||||
"schedule": "スケジュール",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "表示",
|
||||
"size": "サイズ",
|
||||
"upload": "アップロード",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "本当にこのユーザーを削除しますか?",
|
||||
"filesSelected": "選択されたファイル",
|
||||
"optionalPassword": "任意のパスワード",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "ここにファイルをドラッグ&ドロップする",
|
||||
"completed": "完成",
|
||||
"conflictsDetected": "コンフリクト検出",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "無効な名前が指定されました:\"/\"または\"˶\"をコンテナに入れることはできません。",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "ダウンロード数制限",
|
||||
"maxBandwidth": "最大ダウンロード帯域幅 (kbps)",
|
||||
"shareTheme": "シェアテーマ",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "共有バナーURL",
|
||||
"shareFavicon": "ファビコンのURLを共有する",
|
||||
"optionalPasswordHelp": "設定されている場合、ユーザーは共有コンテンツを見るためにこのパスワードを入力しなければならない。",
|
||||
"viewMode": "ビューモード"
|
||||
"viewMode": "ビューモード",
|
||||
"renameMessage": "新しい名前を入力する:",
|
||||
"renameMessageInvalid": "ファイルには\"/\"や\"˶\"を入れることはできません。",
|
||||
"source": "ソース{suffix}",
|
||||
"deleteMessageMultiple": "本当に{count} ファイルを削除しますか?",
|
||||
"deleteMessageShare": "この共有を削除してもよろしいですか? {path} ?",
|
||||
"lastModified": "最終更新日{suffix}",
|
||||
"numberDirs": "ディレクトリ数{suffix}",
|
||||
"numberFiles": "ファイル数{suffix}",
|
||||
"renameMessageConflict": "{filename}\" という名前のアイテムがすでに存在する!",
|
||||
"uploadSettingsChunked": "最大同時アップロード数{maxConcurrentUpload}チャンクサイズ {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "最大同時アップロード数{maxConcurrentUpload}チャンクアップロード無効"
|
||||
},
|
||||
"search": {
|
||||
"images": "画像",
|
||||
"music": "音楽",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Press enter to search...",
|
||||
"search": "検索...",
|
||||
"typeToSearch": "Type to search...",
|
||||
"types": "種類",
|
||||
"video": "ビデオ",
|
||||
"path": "パス",
|
||||
"smallerThan": "より小さい:",
|
||||
"largerThan": "より大きい:",
|
||||
"helpText1": "検索は入力した文字ごとに行われます(検索語は最低3文字)。",
|
||||
"helpText2": "インデックス:検索は、設定された間隔(デフォルト:5分)で自動的に更新されるインデックスを利用します。プログラムが開始したばかりの時に検索すると、不完全な結果になることがあります。"
|
||||
"helpText2": "インデックス:検索は、設定された間隔(デフォルト:5分)で自動的に更新されるインデックスを利用します。プログラムが開始したばかりの時に検索すると、不完全な結果になることがあります。",
|
||||
"noResults": "インデックス検索で結果が見つかりませんでした。",
|
||||
"onlyFolders": "フォルダのみ",
|
||||
"onlyFiles": "ファイルのみ",
|
||||
"showOptions": "オプションの表示",
|
||||
"photos": "写真",
|
||||
"audio": "オーディオ",
|
||||
"videos": "ビデオ",
|
||||
"documents": "資料",
|
||||
"archives": "アーカイブス",
|
||||
"number": "番号",
|
||||
"searchContext": "検索コンテキスト:{context}",
|
||||
"notEnoughCharacters": "検索する文字数が足りない (最小値{minSearchLength})",
|
||||
"typeToSearch": "検索を開始するには、{minSearchLength} 以上の文字を入力します。"
|
||||
},
|
||||
"settings": {
|
||||
"admin": "管理者",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(変更を避けるために空白にしてください)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Branding directory path",
|
||||
"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}.",
|
||||
"changePassword": "パスワードを変更",
|
||||
"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}.",
|
||||
"commandsUpdated": "コマンドは更新されました!",
|
||||
"createUserDir": "Auto create user home dir while adding new user",
|
||||
"customStylesheet": "カスタムスタイルシ ート",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "管理者ユーザーオプション",
|
||||
"shareDuration": "期限切れ",
|
||||
"searchOptions": "検索オプション",
|
||||
"downloads": "ダウンロード"
|
||||
"downloads": "ダウンロード",
|
||||
"systemAdmin": "システム&管理",
|
||||
"configViewer": "コンフィグビューア",
|
||||
"configViewerShowFull": "フルコンフィグを表示",
|
||||
"configViewerShowComments": "コメントを表示",
|
||||
"configViewerLoadConfig": "ロードコンフィグ",
|
||||
"brandingHelp": "ファイルブラウザのインスタンスの見た目や使い勝手は、名前の変更、ロゴの置き換え、カスタムスタイルの追加、GitHub への外部リンクの無効化などでカスタマイズできます。\nカスタムブランディングの詳細については、{0} をご覧ください。",
|
||||
"commandRunnerHelp": "ここでは、指定したイベントで実行されるコマンドを設定することができる。1行に1つ書く必要がある。環境変数{0} と{1} が使用可能になり、{0} は{1} の相対変数になります。この機能と使用可能な環境変数の詳細については、{2} をお読みください。"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "ヘルプ",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "ナビゲーションボタンを隠す",
|
||||
"hideNavButtonsDescription": "シェア内のナビバーにあるナビゲーションボタンを非表示にして、ミニマルな外観を作る。",
|
||||
"viewModeDescription": "共有ページのデフォルトレイアウト。",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "シェアが送信されましたので、閲覧またはダウンロードしてください。",
|
||||
"disableShareCard": "シェアカードを無効にする",
|
||||
"disableShareCardDescription": "サイドバーの共有ページ、またはモバイルのページ上部にある共有カードを無効にする。",
|
||||
"disableSidebar": "サイドバーを無効にする",
|
||||
"disableSidebarDescription": "共有ページのサイドバーを無効にする。"
|
||||
"disableSidebarDescription": "共有ページのサイドバーを無効にする。",
|
||||
"enforceDarkLightMode": "テーマモードの強制",
|
||||
"enforceDarkLightModeDescription": "この共有に特定のテーマモード(ダークまたはライト)を強制します。",
|
||||
"default": "モードを強制しない",
|
||||
"dark": "暗い",
|
||||
"light": "ライト",
|
||||
"enableOnlyOffice": "OnlyOfficeビューアを有効にする",
|
||||
"enableOnlyOfficeDescription": "この共有でOnlyOfficeを使用したオフィスファイルの閲覧を許可する。",
|
||||
"enableOnlyOfficeEditing": "OnlyOffice編集を有効にする",
|
||||
"enableOnlyOfficeEditingDescription": "この共有でOnlyOfficeを使ったオフィスファイルの編集を許可する。",
|
||||
"titleDefault": "共有ファイル{title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "APIキー:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "却下",
|
||||
"totalAllowed": "可",
|
||||
"all": "すべて拒否",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "これは、特定のアクセスルールが存在しないパスにユーザがアクセスしようとしたときに何が起こるかを示している。Allow」はユーザーがデフォルトでアクセスできることを意味し、「Deny」は明示的に許可されない限り、ユーザーがデフォルトでブロックされることを意味する。"
|
||||
"defaultBehaviorDescription": "これは、特定のアクセスルールが存在しないパスにユーザがアクセスしようとしたときに何が起こるかを示している。Allow」はユーザーがデフォルトでアクセスできることを意味し、「Deny」は明示的に許可されない限り、ユーザーがデフォルトでブロックされることを意味する。",
|
||||
"defaultBehavior": "デフォルト・アクション{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "アップロードとダウンロード",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - デフォルトのテーマ",
|
||||
"customTheme": "テーマを選ぶ",
|
||||
"showSelectMultiple": "デスクトップのコンテキストメニューに複数選択を表示する",
|
||||
"showSelectMultipleDescription": "デフォルトでは、デスクトップのコンテキストメニューには、モバイルのような「複数選択」オプションはありません。このオプションは常にコンテキストメニューに表示されます。"
|
||||
"showSelectMultipleDescription": "デフォルトでは、デスクトップのコンテキストメニューには、モバイルのような「複数選択」オプションはありません。このオプションは常にコンテキストメニューに表示されます。",
|
||||
"defaultMediaPlayer": "ブラウザのネイティブ・メディア・プレーヤーを使う",
|
||||
"defaultMediaPlayerDescription": "ビデオやオーディオファイルには、FileBrowserに含まれる「vue-plyr」メディアプレーヤーではなく、ブラウザのネイティブメディアプレーヤーを使用してください。",
|
||||
"debugOfficeEditor": "OnlyOfficeデバッグモードを有効にする",
|
||||
"debugOfficeEditorDescription": "OnlyOfficeエディタに追加情報のデバッグポップアップを表示します。",
|
||||
"fileViewerOptions": "ファイル・ビューアーのオプション",
|
||||
"themeAndLanguage": "テーマと言語"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "エディタの初期化に失敗しました。再読み込みしてください。",
|
||||
"saveFailed": "ファイルの保存に失敗しました。もう一度お試しください。",
|
||||
"saveAborted": "保存が中止された。ファイル名がアクティブなファイルと一致しません。",
|
||||
"saveDisabled": "共有ファイルの保存はサポートされていません。",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "保存操作が中断されました。アプリケーションのアクティブファイル({activeFile} )と保存しようとしているファイル({tryingToSave} )が一致しません。",
|
||||
"syncFailed": "{filename}\" のルートとステートの同期に失敗しました。データの破損を防ぐためにエディタのセットアップを中止しています。"
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "ループ有効",
|
||||
"LoopDisabled": "ループ無効"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "완료 지우기",
|
||||
"showMore": "자세히 보기",
|
||||
"showLess": "덜 보기",
|
||||
"openParentFolder": "상위 폴더 열기"
|
||||
"openParentFolder": "상위 폴더 열기",
|
||||
"selectedCount": "작업을 수행하기 위해 선택한 항목 수"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "파일 다운로드",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "파일이나 디렉토리를 선택해주세요.",
|
||||
"ctrl": {
|
||||
"click": "여러 개의 파일이나 디렉토리를 선택해주세요.",
|
||||
"f": "검색창 열기",
|
||||
"s": "파일 또는 디렉토리 다운로드"
|
||||
"d": "선택한 항목 다운로드"
|
||||
},
|
||||
"del": "선택된 파일 삭제",
|
||||
"doubleClick": "파일 또는 디렉토리 열기",
|
||||
"esc": "선택 취소/프롬프트 닫기",
|
||||
"f1": "정보",
|
||||
"f2": "파일 이름 변경",
|
||||
"help": "도움말"
|
||||
"help": "도움말",
|
||||
"f1": "도움말 프롬프트 표시",
|
||||
"description": "아래에서 기본 탐색 옵션을 확인할 수 있습니다. 자세한 내용은 다음을 참조하세요.",
|
||||
"wiki": "파일브라우저 퀀텀 위키"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "복사",
|
||||
"copyMessage": "복사할 디렉토리:",
|
||||
"deleteMessageMultiple": "{count} 개의 파일을 삭제하시겠습니까?",
|
||||
"deleteMessageSingle": "파일 혹은 디렉토리를 삭제하시겠습니까?",
|
||||
"deleteTitle": "파일 삭제",
|
||||
"displayName": "게시 이름:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "다운로드 포맷 설정.",
|
||||
"error": "에러 발생!",
|
||||
"fileInfo": "파일 정보",
|
||||
"lastModified": "최종 수정{suffix}",
|
||||
"move": "이동",
|
||||
"moveMessage": "이동할 화일 또는 디렉토리를 선택하세요:",
|
||||
"newArchetype": "원형을 유지하는 포스트를 생성합니다. 파일은 컨텐트 폴더에 생성됩니다.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "새 디렉토리 이름을 입력해주세요.",
|
||||
"newFile": "새 파일",
|
||||
"newFileMessage": "새 파일 이름을 입력해주세요.",
|
||||
"numberDirs": "디렉토리 수{suffix}",
|
||||
"numberFiles": "파일 수{suffix}",
|
||||
"rename": "이름 변경",
|
||||
"renameMessage": "새로운 이름을 입력하세요.",
|
||||
"replace": "대체하기",
|
||||
"replaceMessage": "동일한 파일 이름이 존재합니다. 현재 파일을 덮어쓸까요?\n",
|
||||
"schedule": "일정",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "보기",
|
||||
"size": "크기",
|
||||
"upload": "업로드",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "이 사용자를 삭제하시겠습니까?",
|
||||
"filesSelected": "선택한 파일",
|
||||
"optionalPassword": "선택적 비밀번호",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "파일을 여기로 끌어다 놓기",
|
||||
"completed": "완료",
|
||||
"conflictsDetected": "충돌 감지",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "제공된 이름이 잘못되었습니다: \"/\" 또는 \"\\\"를 포함할 수 없습니다.",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "다운로드 횟수 제한",
|
||||
"maxBandwidth": "최대 다운로드 대역폭(kbps)",
|
||||
"shareTheme": "테마 공유",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "배너 URL 공유",
|
||||
"shareFavicon": "파비콘 URL 공유",
|
||||
"optionalPasswordHelp": "이 비밀번호를 설정하면 사용자가 공유 콘텐츠를 보려면 이 비밀번호를 입력해야 합니다.",
|
||||
"viewMode": "보기 모드"
|
||||
"viewMode": "보기 모드",
|
||||
"renameMessage": "새 이름을 입력합니다:",
|
||||
"renameMessageInvalid": "파일은 \"/\" 또는 \"\\\"를 포함할 수 없습니다.",
|
||||
"source": "출처{suffix}",
|
||||
"deleteMessageMultiple": "{count} 파일을 정말 삭제하시겠습니까?",
|
||||
"deleteMessageShare": "이 공유를 삭제하시겠습니까? {path} ?",
|
||||
"lastModified": "마지막 수정{suffix}",
|
||||
"numberDirs": "디렉터리 수{suffix}",
|
||||
"numberFiles": "파일 수{suffix}",
|
||||
"renameMessageConflict": "\"{filename}\"라는 이름의 항목이 이미 존재합니다!",
|
||||
"uploadSettingsChunked": "최대 동시 업로드: {maxConcurrentUpload}, 청크 크기: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "최대 동시 업로드: {maxConcurrentUpload}, 청크 업로드 비활성화"
|
||||
},
|
||||
"search": {
|
||||
"images": "이미지",
|
||||
"music": "음악",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "검색하려면 엔터를 입력하세요",
|
||||
"search": "검색...",
|
||||
"typeToSearch": "검색어 입력...",
|
||||
"types": "Types",
|
||||
"video": "비디오",
|
||||
"path": "경로:",
|
||||
"smallerThan": "보다 작습니다:",
|
||||
"largerThan": "보다 큽니다:",
|
||||
"helpText1": "검색은 입력하는 각 문자(검색어의 경우 최소 3자)에 대해 이루어집니다.",
|
||||
"helpText2": "색인: 검색은 설정된 간격(기본값: 5분)에 따라 자동으로 업데이트되는 색인을 활용합니다. 프로그램이 막 시작되었을 때 검색하면 불완전한 결과가 나올 수 있습니다."
|
||||
"helpText2": "색인: 검색은 설정된 간격(기본값: 5분)에 따라 자동으로 업데이트되는 색인을 활용합니다. 프로그램이 막 시작되었을 때 검색하면 불완전한 결과가 나올 수 있습니다.",
|
||||
"noResults": "색인 검색에서 결과를 찾을 수 없습니다.",
|
||||
"onlyFolders": "폴더만",
|
||||
"onlyFiles": "파일만",
|
||||
"showOptions": "옵션 표시",
|
||||
"photos": "사진",
|
||||
"audio": "오디오",
|
||||
"videos": "동영상",
|
||||
"documents": "문서",
|
||||
"archives": "아카이브",
|
||||
"number": "번호",
|
||||
"searchContext": "컨텍스트 검색: {context}",
|
||||
"notEnoughCharacters": "검색할 문자가 부족합니다(최소 {minSearchLength})",
|
||||
"typeToSearch": "검색을 시작하려면 {minSearchLength} 을 입력하세요."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "관리자",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(수정하지 않으면 비워두세요)",
|
||||
"branding": "브랜딩",
|
||||
"brandingDirectoryPath": "브랜드 디렉토리 경로",
|
||||
"brandingHelp": "File Browser 인스턴스는 이름, 로고, 스타일 등을 변경할 수 있습니다. 자세한 사항은 여기{0}에서 확인하세요.",
|
||||
"changePassword": "비밀번호 변경",
|
||||
"commandRunner": "명령 실행기",
|
||||
"commandRunnerHelp": "이벤트에 해당하는 명령을 설정하세요. 줄당 1개의 명령을 적으세요. 환경 변수{0} 와 {1}이 사용가능하며, {0} 은 {1}에 상대 경로 입니다. 자세한 사항은 {2} 를 참조하세요.",
|
||||
"commandsUpdated": "명령 수정됨!",
|
||||
"createUserDir": "Auto create user home dir while adding new user",
|
||||
"customStylesheet": "커스텀 스타일시트",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "관리자 사용자 옵션",
|
||||
"shareDuration": "만료",
|
||||
"searchOptions": "검색 옵션",
|
||||
"downloads": "다운로드"
|
||||
"downloads": "다운로드",
|
||||
"systemAdmin": "시스템 및 관리자",
|
||||
"configViewer": "구성 뷰어",
|
||||
"configViewerShowFull": "전체 구성 표시",
|
||||
"configViewerShowComments": "댓글 표시",
|
||||
"configViewerLoadConfig": "로드 구성",
|
||||
"brandingHelp": "파일 브라우저 인스턴스의 이름을 변경하고, 로고를 바꾸고, 사용자 지정 스타일을 추가하고, GitHub에 대한 외부 링크를 비활성화하여 모양과 느낌을 사용자 지정할 수 있습니다.\n사용자 지정 브랜딩에 대한 자세한 내용은 {0} 을 참조하세요.",
|
||||
"commandRunnerHelp": "여기에서 명명된 이벤트에서 실행되는 명령을 설정할 수 있습니다. 한 줄에 하나씩 작성해야 합니다. 환경 변수 {0} 및 {1} 을 사용할 수 있으며, {0} 은 {1} 에 상대적입니다. 이 기능 및 사용 가능한 환경 변수에 대한 자세한 내용은 {2} 을 참조하세요."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "도움말",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "탐색 버튼 숨기기",
|
||||
"hideNavButtonsDescription": "공유의 탐색 모음에서 탐색 버튼을 숨겨 미니멀한 디자인을 만듭니다.",
|
||||
"viewModeDescription": "공유 페이지의 기본 레이아웃입니다.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "보거나 다운로드할 수 있는 공유물이 전송되었습니다.",
|
||||
"disableShareCard": "공유 카드 비활성화",
|
||||
"disableShareCardDescription": "사이드바의 공유 페이지 또는 모바일의 페이지 상단에서 공유 카드를 비활성화합니다.",
|
||||
"disableSidebar": "사이드바 비활성화",
|
||||
"disableSidebarDescription": "공유 페이지에서 사이드바를 비활성화합니다."
|
||||
"disableSidebarDescription": "공유 페이지에서 사이드바를 비활성화합니다.",
|
||||
"enforceDarkLightMode": "테마 모드 적용",
|
||||
"enforceDarkLightModeDescription": "이 공유에 특정 테마 모드(어둡거나 밝음)를 강제로 적용하여 사용자 기본 설정을 재정의합니다.",
|
||||
"default": "모드 강제 적용 안 함",
|
||||
"dark": "Dark",
|
||||
"light": "빛",
|
||||
"enableOnlyOffice": "Office 뷰어만 사용",
|
||||
"enableOnlyOfficeDescription": "이 공유에서 OnlyOffice를 사용하여 Office 파일을 볼 수 있도록 허용합니다.",
|
||||
"enableOnlyOfficeEditing": "Office 편집만 사용",
|
||||
"enableOnlyOfficeEditingDescription": "이 공유에서 OnlyOffice를 사용하여 Office 파일을 편집할 수 있도록 허용합니다.",
|
||||
"titleDefault": "공유 파일 - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API 키:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "거부됨",
|
||||
"totalAllowed": "허용됨",
|
||||
"all": "모두 거부",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "사용자가 특정 액세스 규칙이 없는 경로에 액세스하려고 할 때 어떤 일이 발생하는지 보여줍니다. '허용'은 사용자가 기본적으로 액세스할 수 있음을 의미하며, '거부'는 명시적으로 허용되지 않는 한 기본적으로 차단됨을 의미합니다."
|
||||
"defaultBehaviorDescription": "사용자가 특정 액세스 규칙이 없는 경로에 액세스하려고 할 때 어떤 일이 발생하는지 보여줍니다. '허용'은 사용자가 기본적으로 액세스할 수 있음을 의미하며, '거부'는 명시적으로 허용되지 않는 한 기본적으로 차단됨을 의미합니다.",
|
||||
"defaultBehavior": "기본 작업{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "업로드 및 다운로드",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - 기본 테마",
|
||||
"customTheme": "테마 선택",
|
||||
"showSelectMultiple": "데스크톱의 컨텍스트 메뉴에서 여러 개 선택 표시",
|
||||
"showSelectMultipleDescription": "기본적으로 데스크톱 컨텍스트 메뉴에는 모바일처럼 '여러 개 선택' 옵션이 없습니다. 이 옵션은 항상 컨텍스트 메뉴에 표시됩니다."
|
||||
"showSelectMultipleDescription": "기본적으로 데스크톱 컨텍스트 메뉴에는 모바일처럼 '여러 개 선택' 옵션이 없습니다. 이 옵션은 항상 컨텍스트 메뉴에 표시됩니다.",
|
||||
"defaultMediaPlayer": "브라우저의 기본 미디어 플레이어 사용",
|
||||
"defaultMediaPlayerDescription": "비디오 및 오디오 파일은 FileBrowser에 포함된 'vue-plyr' 미디어 플레이어가 아닌 브라우저의 기본 미디어 플레이어를 사용하세요.",
|
||||
"debugOfficeEditor": "Office 디버그 모드만 사용",
|
||||
"debugOfficeEditorDescription": "OnlyOffice 편집기에서 추가 정보가 포함된 디버그 팝업을 표시합니다.",
|
||||
"fileViewerOptions": "파일 뷰어 옵션",
|
||||
"themeAndLanguage": "테마 및 언어"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "편집기를 초기화하지 못했습니다. 다시 로드하세요.",
|
||||
"saveFailed": "파일을 저장하지 못했습니다. 다시 시도하세요.",
|
||||
"saveAborted": "저장이 중단되었습니다. 파일 이름이 활성 파일과 일치하지 않습니다.",
|
||||
"saveDisabled": "공유 파일 저장은 지원되지 않습니다.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "저장 작업이 중단되었습니다. 애플리케이션의 활성 파일({activeFile})이 저장하려는 파일({tryingToSave})과 일치하지 않습니다.",
|
||||
"syncFailed": "5번 시도 후 \"{filename}\"의 경로와 상태를 동기화하지 못했습니다. 데이터 손상을 방지하기 위해 편집기 설정을 중단합니다."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "루프 사용",
|
||||
"LoopDisabled": "루프 비활성화"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Gereed",
|
||||
"showMore": "Meer tonen",
|
||||
"showLess": "Minder tonen",
|
||||
"openParentFolder": "Bovenliggende map openen"
|
||||
"openParentFolder": "Bovenliggende map openen",
|
||||
"selectedCount": "Aantal items geselecteerd om een actie uit te voeren"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Bestand downloaden",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "selecteer bestand of map",
|
||||
"ctrl": {
|
||||
"click": "selecteer meerdere bestanden of mappen",
|
||||
"f": "opent zoeken",
|
||||
"s": "sla een bestand op of download de map waar u bent"
|
||||
"d": "geselecteerde items downloaden"
|
||||
},
|
||||
"del": "verwijder geselecteerde items",
|
||||
"doubleClick": "open een bestand of map",
|
||||
"esc": "Wis selectie en/of sluit de prompt",
|
||||
"f1": "deze informatie",
|
||||
"f2": "bestand herbenoemen",
|
||||
"help": "Help"
|
||||
"help": "Help",
|
||||
"f1": "help prompt tonen",
|
||||
"description": "Je kunt de basisnavigatieopties hieronder bekijken. Ga voor aanvullende informatie naar",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "Kopiëren",
|
||||
"copyMessage": "Kies een locatie om uw bestanden te kopiëren: ",
|
||||
"deleteMessageMultiple": "Weet u zeker dat u {count} bestand(en) wil verwijderen?",
|
||||
"deleteMessageSingle": "Weet u zeker dat u dit bestand/map wil verwijderen?",
|
||||
"deleteTitle": "Bestanden verwijderen",
|
||||
"displayName": "Weergavenaam: ",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "Kies het vermat dat u wilt downloaden. ",
|
||||
"error": "Er ging iets fout",
|
||||
"fileInfo": "Bestandinformatie",
|
||||
"lastModified": "Laatst Bewerkt{suffix}",
|
||||
"move": "Verplaatsen",
|
||||
"moveMessage": "Kies een nieuwe locatie voor je bestand(en)/map(pen)",
|
||||
"newArchetype": "Maak een nieuw bericht op basis van een archetype. Uw bestand wordt aangemaakt in de inhoudsmap.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "Schrijf de naam van de nieuwe map",
|
||||
"newFile": "Nieuw bestand",
|
||||
"newFileMessage": "Schrijf de naam van het nieuwe bestand",
|
||||
"numberDirs": "Aantal mappen{suffix}",
|
||||
"numberFiles": "Aantal bestanden{suffix}",
|
||||
"rename": "Hernoemen",
|
||||
"renameMessage": "Voer een nieuwe naam in voor",
|
||||
"replace": "Vervangen",
|
||||
"replaceMessage": "Een van de bestanden die u probeert te uploaden, geeft conflicten i.v.m. de naamgeving. Wilt u de bestaande bestanden vervangen?\n",
|
||||
"schedule": "Plannen",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "Tonen",
|
||||
"size": "Grootte",
|
||||
"upload": "Uploaden",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "Weet je zeker dat je deze gebruiker wilt verwijderen?",
|
||||
"selected": "geselecteerd",
|
||||
"filesSelected": "geselecteerde bestanden",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Sleep bestanden hier naartoe",
|
||||
"completed": "Voltooid",
|
||||
"conflictsDetected": "Gedetecteerde conflicten",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Ongeldige naam opgegeven: kan geen \"/\" of \"˜\" bevatten.",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limiet aantal downloads",
|
||||
"maxBandwidth": "Max. downloadbandbreedte (kbps)",
|
||||
"shareTheme": "Thema delen",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "URL banner delen",
|
||||
"shareFavicon": "favicon URL delen",
|
||||
"optionalPasswordHelp": "Indien ingesteld, moeten gebruikers dit wachtwoord invoeren om de gedeelde inhoud te bekijken.",
|
||||
"viewMode": "Weergavemodus"
|
||||
"viewMode": "Weergavemodus",
|
||||
"renameMessage": "Voer een nieuwe naam in:",
|
||||
"renameMessageInvalid": "Een bestand kan geen \"/\" of \"\\\" bevatten.",
|
||||
"source": "Bron{suffix}",
|
||||
"deleteMessageMultiple": "Weet je zeker dat je {count} bestand(en) wilt verwijderen?",
|
||||
"deleteMessageShare": "Weet je zeker dat je deze share wilt verwijderen: {path} ?",
|
||||
"lastModified": "Laatst gewijzigd{suffix}",
|
||||
"numberDirs": "Aantal mappen{suffix}",
|
||||
"numberFiles": "Aantal bestanden{suffix}",
|
||||
"renameMessageConflict": "Een item met de naam \"{filename}\" bestaat al!",
|
||||
"uploadSettingsChunked": "Max gelijktijdige uploads: {maxConcurrentUpload}, brokgrootte: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max gelijktijdige uploads: {maxConcurrentUpload}, chunked upload uitgeschakeld"
|
||||
},
|
||||
"search": {
|
||||
"images": "Afbeeldingen",
|
||||
"music": "Muziek",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Druk op enter om te zoeken...",
|
||||
"search": "Zoeken...",
|
||||
"typeToSearch": "Typ om te zoeken...",
|
||||
"types": "Types",
|
||||
"video": "Video",
|
||||
"path": "Pad:",
|
||||
"smallerThan": "Kleiner dan:",
|
||||
"largerThan": "Groter dan:",
|
||||
"helpText1": "Er wordt gezocht op elk teken dat je typt (minimaal 3 tekens voor zoektermen).",
|
||||
"helpText2": "De index: Zoeken maakt gebruik van de index die automatisch wordt bijgewerkt op het ingestelde interval (standaard: 5 minuten). Zoeken als het programma net is gestart, kan onvolledige resultaten opleveren."
|
||||
"helpText2": "De index: Zoeken maakt gebruik van de index die automatisch wordt bijgewerkt op het ingestelde interval (standaard: 5 minuten). Zoeken als het programma net is gestart, kan onvolledige resultaten opleveren.",
|
||||
"noResults": "Geen resultaten gevonden in geïndexeerde zoekopdrachten.",
|
||||
"onlyFolders": "Alleen mappen",
|
||||
"onlyFiles": "Alleen bestanden",
|
||||
"showOptions": "Opties weergeven",
|
||||
"photos": "Foto's",
|
||||
"audio": "Audio",
|
||||
"videos": "Video's",
|
||||
"documents": "Documenten",
|
||||
"archives": "Archief",
|
||||
"number": "Aantal",
|
||||
"searchContext": "Zoek context: {context}",
|
||||
"notEnoughCharacters": "Niet genoeg tekens om te zoeken (min {minSearchLength})",
|
||||
"typeToSearch": "Begin met het typen van {minSearchLength} of meer tekens om te beginnen met zoeken."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(laat leeg om wijzigingen te voorkomen)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Branding directory path",
|
||||
"brandingHelp": "U kunt de looks en feels hoe File Browser wordt getoond wijzigen door de naam aan te passen, het logo te vervangen, een aangepast stylesheet toe te voegen en/of de externe links naar GitHub uit te schakelen.\nVoor meer informatie over custom branding, bekijk {0}.",
|
||||
"changePassword": "Wijzig Wachtwoord",
|
||||
"commandRunner": "Command runner",
|
||||
"commandRunnerHelp": "Hier kunt u opdrachten instellen die worden uitgevoerd in de benoemde gebeurtenissen. U moet er één per regel schrijven. De omgevingsvariabelen {0} en {1} zijn beschikbaar, zijnde {0} relatief ten opzichte van {1}. Raadpleeg {2} voor meer informatie over deze functie en de beschikbare omgevingsvariabelen.",
|
||||
"commandsUpdated": "Commando's bijgewerkt!",
|
||||
"createUserDir": "Maak automatisch een thuismap aan wanneer een nieuwe gebruiker wordt aangemaakt",
|
||||
"customStylesheet": "Aangepast Stylesheet",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Opties voor beheerders",
|
||||
"shareDuration": "Verloopt op",
|
||||
"searchOptions": "Zoekopties",
|
||||
"downloads": "Downloads"
|
||||
"downloads": "Downloads",
|
||||
"systemAdmin": "Systeem & Beheer",
|
||||
"configViewer": "Configuratiescherm",
|
||||
"configViewerShowFull": "Volledige configuratie weergeven",
|
||||
"configViewerShowComments": "Opmerkingen tonen",
|
||||
"configViewerLoadConfig": "Configuratie laden",
|
||||
"brandingHelp": "Je kunt aanpassen hoe je File Browser instance eruit ziet en aanvoelt door de naam te veranderen, het logo te vervangen, aangepaste stijlen toe te voegen en zelfs externe links naar GitHub uit te schakelen.\nVoor meer informatie over het aanpassen van je branding, kun je kijken op {0}.",
|
||||
"commandRunnerHelp": "Hier kun je commando's instellen die worden uitgevoerd in de genoemde gebeurtenissen. Je moet er één per regel schrijven. De omgevingsvariabelen {0} en {1} zullen beschikbaar zijn, waarbij {0} relatief is ten opzichte van {1}. Lees voor meer informatie over deze functie en de beschikbare omgevingsvariabelen de {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Help",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Navigatieknoppen verbergen",
|
||||
"hideNavButtonsDescription": "Verberg de navigatieknoppen op de navigatiebalk in de share om een minimalistische look te creëren.",
|
||||
"viewModeDescription": "Standaard lay-out voor de gedeelde pagina.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Er is een share naar je verzonden om te bekijken of te downloaden.",
|
||||
"disableShareCard": "Aandelenkaart uitschakelen",
|
||||
"disableShareCardDescription": "Schakel de deelkaart uit op de gedeelde pagina in de zijbalk of bovenaan de pagina op mobiel.",
|
||||
"disableSidebar": "Zijbalk uitschakelen",
|
||||
"disableSidebarDescription": "Schakel de zijbalk op de gedeelde pagina uit."
|
||||
"disableSidebarDescription": "Schakel de zijbalk op de gedeelde pagina uit.",
|
||||
"enforceDarkLightMode": "Themamodus afdwingen",
|
||||
"enforceDarkLightModeDescription": "Forceer een specifieke themamodus (donker of licht) voor deze share, waarbij gebruikersvoorkeuren worden overschreven.",
|
||||
"default": "Een modus niet afdwingen",
|
||||
"dark": "Donker",
|
||||
"light": "Licht",
|
||||
"enableOnlyOffice": "OnlyOffice viewer inschakelen",
|
||||
"enableOnlyOfficeDescription": "Sta het bekijken van Office-bestanden toe met OnlyOffice in deze share.",
|
||||
"enableOnlyOfficeEditing": "Bewerken met OnlyOffice inschakelen",
|
||||
"enableOnlyOfficeEditingDescription": "Laat het bewerken van Office-bestanden met OnlyOffice toe in deze share.",
|
||||
"titleDefault": "Gedeelde bestanden - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API-sleutels",
|
||||
@@ -478,7 +505,13 @@
|
||||
"defaultThemeDescription": "standaard - Het standaardthema",
|
||||
"customTheme": "Kies je thema",
|
||||
"showSelectMultiple": "Meerdere selecteren tonen in contextmenu op bureaublad",
|
||||
"showSelectMultipleDescription": "Standaard heeft het contextmenu op de desktop geen optie \"meerdere selecteren\" zoals mobiel. Deze optie wordt altijd weergegeven in het contextmenu."
|
||||
"showSelectMultipleDescription": "Standaard heeft het contextmenu op de desktop geen optie \"meerdere selecteren\" zoals mobiel. Deze optie wordt altijd weergegeven in het contextmenu.",
|
||||
"defaultMediaPlayer": "Gebruik de ingebouwde mediaspeler van uw browser",
|
||||
"defaultMediaPlayerDescription": "Gebruik de eigen mediaspeler van uw browser voor video- en audiobestanden in plaats van de meegeleverde 'vue-plyr' mediaspeler van FileBrowser.",
|
||||
"debugOfficeEditor": "Debugmodus OnlyOffice inschakelen",
|
||||
"debugOfficeEditorDescription": "Een foutopsporingspop-up weergeven met aanvullende informatie in de OnlyOffice-editor.",
|
||||
"fileViewerOptions": "Opties voor bestandsviewer",
|
||||
"themeAndLanguage": "Thema & Taal"
|
||||
},
|
||||
"general": {
|
||||
"name": "Naam",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "Naam",
|
||||
"accessManagement": "Toegangsbeheer",
|
||||
"all": "Alles weigeren",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Dit laat zien wat er gebeurt als een gebruiker toegang probeert te krijgen tot een pad waarvoor geen specifieke toegangsregels bestaan. Toestaan' betekent dat gebruikers standaard toegang hebben, 'Weigeren' betekent dat gebruikers standaard worden geblokkeerd tenzij ze expliciet toestemming krijgen."
|
||||
"defaultBehaviorDescription": "Dit laat zien wat er gebeurt als een gebruiker toegang probeert te krijgen tot een pad waarvoor geen specifieke toegangsregels bestaan. Toestaan' betekent dat gebruikers standaard toegang hebben, 'Weigeren' betekent dat gebruikers standaard worden geblokkeerd tenzij ze expliciet toestemming krijgen.",
|
||||
"defaultBehavior": "Standaardactie{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Het is niet gelukt de editor te initialiseren. Gelieve opnieuw te laden.",
|
||||
"saveFailed": "Het is niet gelukt om het bestand op te slaan. Probeer het opnieuw.",
|
||||
"saveAborted": "Opslaan afgebroken. De bestandsnaam komt niet overeen met het actieve bestand.",
|
||||
"saveDisabled": "Het opslaan van gedeelde bestanden wordt niet ondersteund.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Opslaan afgebroken. Het actieve bestand van de toepassing ({activeFile}) komt niet overeen met het bestand dat u probeert op te slaan ({tryingToSave}).",
|
||||
"syncFailed": "Na 5 pogingen is het niet gelukt om de status te synchroniseren met de route voor \"{filename}\". Installatie van editor afgebroken om gegevensbeschadiging te voorkomen."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Lus ingeschakeld",
|
||||
"LoopDisabled": "Lus uitgeschakeld"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Czyszczenie zakończone",
|
||||
"showMore": "Pokaż więcej",
|
||||
"showLess": "Pokaż mniej",
|
||||
"openParentFolder": "Otwórz folder nadrzędny"
|
||||
"openParentFolder": "Otwórz folder nadrzędny",
|
||||
"selectedCount": "Liczba elementów wybranych do wykonania akcji"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Pobierz plik",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "wybierz plik lub foler",
|
||||
"ctrl": {
|
||||
"click": "wybierz wiele plików lub folderów",
|
||||
"f": "otwórz wyszukiwarkę",
|
||||
"s": "pobierz aktywny plik lub folder"
|
||||
"d": "Pobierz wybrane elementy"
|
||||
},
|
||||
"del": "usuń zaznaczone",
|
||||
"doubleClick": "otwórz plik lub folder",
|
||||
"esc": "wyczyść zaznaczenie i/lub zamknij okno z powiadomieniem",
|
||||
"f1": "ta informacja",
|
||||
"f2": "zmień nazwę pliku",
|
||||
"help": "Pomoc"
|
||||
"help": "Pomoc",
|
||||
"f1": "pokaż monit pomocy",
|
||||
"description": "Poniżej znajdują się podstawowe opcje nawigacji. Dodatkowe informacje można znaleźć na stronie",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "Kopiuj",
|
||||
"copyMessage": "Wybierz lokalizację do której mają być skopiowane wybrane pliki",
|
||||
"deleteMessageMultiple": "Czy jesteś pewien że chcesz usunąć {count} plik(ów)?",
|
||||
"deleteMessageSingle": "Czy jesteś pewien, że chcesz usunąć ten plik/folder?",
|
||||
"deleteTitle": "Usuń pliki",
|
||||
"displayName": "Wyświetlana Nazwa:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "Wybierz format, jaki chesz pobrać.",
|
||||
"error": "Pojawił się nieznany błąd",
|
||||
"fileInfo": "Informacje o pliku",
|
||||
"lastModified": "Ostatnio Zmodyfikowane{suffix}",
|
||||
"move": "Przenieś",
|
||||
"moveMessage": "Wybierz nową lokalizację dla swoich plik(ów)/folder(ów):",
|
||||
"newArchetype": "Utwórz nowy wpis na bazie wybranego wzorca. Twój plik będzie utworzony w wybranym folderze.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "Podaj nazwę tworzonego folderu.",
|
||||
"newFile": "Nowy plik",
|
||||
"newFileMessage": "Podaj nazwętworzonego pliku.",
|
||||
"numberDirs": "Ilość katalogów{suffix}",
|
||||
"numberFiles": "Ilość plików{suffix}",
|
||||
"rename": "Zmień nazwę",
|
||||
"renameMessage": "Podaj nową nazwę dla",
|
||||
"replace": "Zamień",
|
||||
"replaceMessage": "Jednen z plików który próbujesz wrzucić próbje nadpisać plik o tej samej nazwie. Czy chcesz nadpisać poprzedni plik?\n",
|
||||
"schedule": "Grafi",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "Pokaż",
|
||||
"size": "Rozmiar",
|
||||
"upload": "Prześlij",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "Czy na pewno chcesz usunąć tego użytkownika?",
|
||||
"filesSelected": "wybrane pliki",
|
||||
"optionalPassword": "Hasło opcjonalne",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Przeciągnij i upuść pliki tutaj",
|
||||
"completed": "Zakończono",
|
||||
"conflictsDetected": "Wykryte konflikty",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nieprawidłowa nazwa: nie można użyć kontenera \"/\" lub \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limit liczby pobrań",
|
||||
"maxBandwidth": "Maksymalna przepustowość pobierania (kbps)",
|
||||
"shareTheme": "Udostępnij temat",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Udostępnij adres URL banera",
|
||||
"shareFavicon": "Udostępnij adres URL favicon",
|
||||
"optionalPasswordHelp": "Jeśli jest ustawione, użytkownicy muszą wprowadzić to hasło, aby wyświetlić udostępnioną zawartość.",
|
||||
"viewMode": "Tryb widoku"
|
||||
"viewMode": "Tryb widoku",
|
||||
"renameMessage": "Wprowadź nową nazwę:",
|
||||
"renameMessageInvalid": "Plik nie może być kontenerem \"/\" lub \"\\\"",
|
||||
"source": "Źródło{suffix}",
|
||||
"deleteMessageMultiple": "Czy na pewno chcesz usunąć plik(i) {count}?",
|
||||
"deleteMessageShare": "Czy na pewno chcesz usunąć ten udział: {path} ?",
|
||||
"lastModified": "Ostatnia modyfikacja{suffix}",
|
||||
"numberDirs": "Liczba katalogów{suffix}",
|
||||
"numberFiles": "Liczba plików{suffix}",
|
||||
"renameMessageConflict": "Element o nazwie \"{filename}\" już istnieje!",
|
||||
"uploadSettingsChunked": "Maks. jednoczesne przesyłanie: {maxConcurrentUpload}, rozmiar fragmentu: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Maksymalny jednoczesny upload: {maxConcurrentUpload}, przesyłanie fragmentaryczne wyłączone"
|
||||
},
|
||||
"search": {
|
||||
"images": "Zdjęcia",
|
||||
"music": "Muzyka",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Wciśnij enter, aby wyszukać...",
|
||||
"search": "Szukaj...",
|
||||
"typeToSearch": "Zacznij pisać, aby wyszukać...",
|
||||
"types": "Typy",
|
||||
"video": "Wideo",
|
||||
"path": "Ścieżka:",
|
||||
"smallerThan": "Mniejszy niż:",
|
||||
"largerThan": "Większy niż:",
|
||||
"helpText1": "Wyszukiwanie odbywa się po każdym wpisanym znaku (minimum 3 znaki dla wyszukiwanych haseł).",
|
||||
"helpText2": "Indeks: Wyszukiwanie wykorzystuje indeks, który jest automatycznie aktualizowany w skonfigurowanych odstępach czasu (domyślnie: 5 minut). Wyszukiwanie po uruchomieniu programu może skutkować niekompletnymi wynikami."
|
||||
"helpText2": "Indeks: Wyszukiwanie wykorzystuje indeks, który jest automatycznie aktualizowany w skonfigurowanych odstępach czasu (domyślnie: 5 minut). Wyszukiwanie po uruchomieniu programu może skutkować niekompletnymi wynikami.",
|
||||
"noResults": "Nie znaleziono żadnych wyników w indeksowanym wyszukiwaniu.",
|
||||
"onlyFolders": "Tylko foldery",
|
||||
"onlyFiles": "Tylko pliki",
|
||||
"showOptions": "Pokaż opcje",
|
||||
"photos": "Zdjęcia",
|
||||
"audio": "Audio",
|
||||
"videos": "Filmy",
|
||||
"documents": "Dokumenty",
|
||||
"archives": "Archiwa",
|
||||
"number": "Liczba",
|
||||
"searchContext": "Kontekst wyszukiwania: {context}",
|
||||
"notEnoughCharacters": "Za mało znaków do wyszukania (min. {minSearchLength})",
|
||||
"typeToSearch": "Zacznij wpisywać {minSearchLength} lub więcej znaków, aby rozpocząć wyszukiwanie."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(pozostaw puste aby nie zosatało zmienione)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Folder brandingowy",
|
||||
"brandingHelp": "Możesz dostosować wygląd i doznania użytkownika swojej instancji File Browser poprzez zmianę jej nazwy, zmianę logo, dodanie własnych stylów, a nawet wyłączyć linki zewnętrzne do GitHuba.\nW celu pozyskania większej ilości informacji nt. osobistego brandingu, zapoznaj się z {0}.",
|
||||
"changePassword": "Zmień Hasło",
|
||||
"commandRunner": "Narzędzie do wykonywania poleceń",
|
||||
"commandRunnerHelp": "Tu możesz ustawić komendy, które będą wykonywane przy danych zdarzeniach. Musisz wpisywać po jednej na linjkę. Zmienne środowiskowe {0} i {1} będą dostępne, gdzie {0} jest względne wobec {1}. Więcej informacji o tej funkcji i dostępnych zmiennych środowiskowych znajdziesz tutaj: {2}.",
|
||||
"commandsUpdated": "Polecenie zaktualizowane!",
|
||||
"createUserDir": "Automatycznie utwórz katalog domowy użytkownika podczas dodania nowego użytkownika",
|
||||
"customStylesheet": "Własny arkusz stylów",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Opcje użytkownika administratora",
|
||||
"shareDuration": "Wygasa",
|
||||
"searchOptions": "Opcje wyszukiwania",
|
||||
"downloads": "Pliki do pobrania"
|
||||
"downloads": "Pliki do pobrania",
|
||||
"systemAdmin": "System i administrator",
|
||||
"configViewer": "Przeglądarka konfiguracji",
|
||||
"configViewerShowFull": "Pokaż pełną konfigurację",
|
||||
"configViewerShowComments": "Pokaż komentarze",
|
||||
"configViewerLoadConfig": "Konfiguracja ładowania",
|
||||
"brandingHelp": "Możesz dostosować wygląd i sposób działania swojej instancji przeglądarki plików, zmieniając jej nazwę, zastępując logo, dodając niestandardowe style, a nawet wyłączając zewnętrzne łącza do GitHub.\nWięcej informacji na temat niestandardowego brandingu można znaleźć na stronie {0}.",
|
||||
"commandRunnerHelp": "Tutaj można ustawić polecenia, które będą wykonywane w nazwanych zdarzeniach. Należy wpisać jedną komendę w wierszu. Dostępne będą zmienne środowiskowe {0} i {1}, przy czym {0} będzie relatywna do {1}. Więcej informacji na temat tej funkcji i dostępnych zmiennych środowiskowych można znaleźć na stronie {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Pomoc",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Ukryj przyciski nawigacyjne",
|
||||
"hideNavButtonsDescription": "Ukryj przyciski nawigacyjne na pasku nawigacyjnym w folderze udostępnionym, aby uzyskać minimalistyczny wygląd.",
|
||||
"viewModeDescription": "Domyślny układ strony udostępnionej.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Udział został wysłany do wyświetlenia lub pobrania.",
|
||||
"disableShareCard": "Wyłączanie karty udostępniania",
|
||||
"disableShareCardDescription": "Wyłącz kartę udostępniania na udostępnianej stronie na pasku bocznym lub u góry strony na urządzeniach mobilnych.",
|
||||
"disableSidebar": "Wyłącz pasek boczny",
|
||||
"disableSidebarDescription": "Wyłącz pasek boczny na udostępnionej stronie."
|
||||
"disableSidebarDescription": "Wyłącz pasek boczny na udostępnionej stronie.",
|
||||
"enforceDarkLightMode": "Wymuszanie trybu motywu",
|
||||
"enforceDarkLightModeDescription": "Wymusza określony tryb motywu (ciemny lub jasny) dla tego udziału, zastępując preferencje użytkownika.",
|
||||
"default": "Nie wymuszaj trybu",
|
||||
"dark": "Ciemny",
|
||||
"light": "Światło",
|
||||
"enableOnlyOffice": "Włącz przeglądarkę OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Zezwalaj na wyświetlanie plików biurowych przy użyciu OnlyOffice w tym udziale.",
|
||||
"enableOnlyOfficeEditing": "Włącz edycję OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Zezwalaj na edycję plików biurowych za pomocą OnlyOffice w tym udziale.",
|
||||
"titleDefault": "Udostępnione pliki - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Klucze API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Odmowa",
|
||||
"totalAllowed": "Dozwolone",
|
||||
"all": "Odmów wszystkim",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "To pokazuje, co się dzieje, gdy użytkownik próbuje uzyskać dostęp do ścieżki, dla której nie istnieją określone reguły dostępu. \"Zezwól\" oznacza, że użytkownicy mogą uzyskać dostęp domyślnie, \"Odmów\" oznacza, że użytkownicy są domyślnie blokowani, chyba że wyraźnie na to zezwolono."
|
||||
"defaultBehaviorDescription": "To pokazuje, co się dzieje, gdy użytkownik próbuje uzyskać dostęp do ścieżki, dla której nie istnieją określone reguły dostępu. \"Zezwól\" oznacza, że użytkownicy mogą uzyskać dostęp domyślnie, \"Odmów\" oznacza, że użytkownicy są domyślnie blokowani, chyba że wyraźnie na to zezwolono.",
|
||||
"defaultBehavior": "Działanie domyślne{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Przesyłanie i pobieranie",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - domyślny motyw",
|
||||
"customTheme": "Wybierz motyw",
|
||||
"showSelectMultiple": "Wyświetlanie wyboru wielokrotnego w menu kontekstowym na pulpicie",
|
||||
"showSelectMultipleDescription": "Domyślnie menu kontekstowe pulpitu nie ma opcji \"wybierz wiele\", tak jak w przypadku urządzeń mobilnych. Opcja ta jest zawsze widoczna w menu kontekstowym."
|
||||
"showSelectMultipleDescription": "Domyślnie menu kontekstowe pulpitu nie ma opcji \"wybierz wiele\", tak jak w przypadku urządzeń mobilnych. Opcja ta jest zawsze widoczna w menu kontekstowym.",
|
||||
"defaultMediaPlayer": "Użyj natywnego odtwarzacza multimediów w przeglądarce",
|
||||
"defaultMediaPlayerDescription": "Do odtwarzania plików wideo i audio należy używać natywnego odtwarzacza multimediów przeglądarki, a nie dołączonego do FileBrowser odtwarzacza multimediów \"vue-plyr\".",
|
||||
"debugOfficeEditor": "Włącz tryb debugowania OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Wyświetla wyskakujące okienko debugowania z dodatkowymi informacjami w edytorze OnlyOffice.",
|
||||
"fileViewerOptions": "Opcje przeglądarki plików",
|
||||
"themeAndLanguage": "Temat i język"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Nie udało się zainicjować edytora. Przeładuj ponownie.",
|
||||
"saveFailed": "Nie udało się zapisać pliku. Spróbuj ponownie.",
|
||||
"saveAborted": "Zapisywanie przerwane. Nazwa pliku nie pasuje do aktywnego pliku.",
|
||||
"saveDisabled": "Zapisywanie udostępnionych plików nie jest obsługiwane.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operacja zapisywania została przerwana. Aktywny plik aplikacji ({activeFile}) nie jest zgodny z plikiem, który próbujesz zapisać ({tryingToSave}).",
|
||||
"syncFailed": "Nie udało się zsynchronizować stanu z trasą dla \"{filename}\" po 5 próbach. Przerwano konfigurację edytora, aby zapobiec uszkodzeniu danych."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Włączona pętla",
|
||||
"LoopDisabled": "Pętla wyłączona"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Limpar concluído",
|
||||
"showMore": "Mostrar mais",
|
||||
"showLess": "Mostrar menos",
|
||||
"openParentFolder": "Abrir a pasta principal"
|
||||
"openParentFolder": "Abrir a pasta principal",
|
||||
"selectedCount": "Número de itens selecionados para executar uma ação"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Baixar arquivo",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "selecionar pasta ou arquivo",
|
||||
"ctrl": {
|
||||
"click": "selecionar várias pastas e arquivos",
|
||||
"f": "pesquisar",
|
||||
"s": "salvar um arquivo ou baixar a pasta que você está"
|
||||
"d": "baixar itens selecionados"
|
||||
},
|
||||
"del": "apagar os arquivos selecionados",
|
||||
"doubleClick": "abrir pasta ou arquivo",
|
||||
"esc": "limpar seleção e/ou fechar menu",
|
||||
"f1": "esta informação",
|
||||
"f2": "renomear arquivo",
|
||||
"help": "Ajuda"
|
||||
"help": "Ajuda",
|
||||
"f1": "mostrar prompt de ajuda",
|
||||
"description": "Você pode ver as opções básicas de navegação abaixo. Para obter informações adicionais, visite",
|
||||
"wiki": "Wiki do FileBrowser Quantum"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Copiar",
|
||||
"copyMessage": "Escolha um lugar para copiar os arquivos:",
|
||||
"deleteMessageMultiple": "Deseja apagar {count} arquivo(s)?",
|
||||
"deleteMessageSingle": "Deseja apagar esta pasta/arquivo?",
|
||||
"deleteMessageShare": "Deseja apagar este compartilhamento ({path})?",
|
||||
"deleteTitle": "Apagar arquivos",
|
||||
"displayName": "Nome:",
|
||||
"download": "Baixar arquivos",
|
||||
"downloadMessage": "Escolha o formato do arquivo.",
|
||||
"error": "Algo de errado ocorreu",
|
||||
"fileInfo": "Informação do arquivo",
|
||||
"lastModified": "Última modificação{suffix}",
|
||||
"move": "Mover",
|
||||
"moveMessage": "Escolha uma nova pasta para os seus arquivos:",
|
||||
"newArchetype": "Criar um novo post baseado num \"archetype\". O seu arquivo será criado na pasta \"content\".",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Escreva o nome da nova pasta.",
|
||||
"newFile": "Novo arquivo",
|
||||
"newFileMessage": "Escreva o nome do novo arquivo.",
|
||||
"numberDirs": "Número de pastas{suffix}",
|
||||
"numberFiles": "Número de arquivos{suffix}",
|
||||
"rename": "Renomear",
|
||||
"renameMessage": "Insira um novo nome para",
|
||||
"replace": "Substituir",
|
||||
"replaceMessage": "Já existe um arquivo com nome igual a um dos que está tentando enviar. Deseja substituir?\n",
|
||||
"schedule": "Agendar",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Arraste e solte os arquivos aqui",
|
||||
"completed": "Concluído",
|
||||
"conflictsDetected": "Conflitos detectados",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nome inválido fornecido: não pode conter \"/\" ou \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limite de número de downloads",
|
||||
"maxBandwidth": "Largura de banda máxima de download (kbps)",
|
||||
"shareTheme": "Compartilhar tema",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Compartilhar URL do banner",
|
||||
"shareFavicon": "Compartilhar URL do favicon",
|
||||
"optionalPasswordHelp": "Se definida, os usuários deverão digitar essa senha para visualizar o conteúdo compartilhado.",
|
||||
"viewMode": "Modo de exibição"
|
||||
"viewMode": "Modo de exibição",
|
||||
"renameMessage": "Digite um novo nome:",
|
||||
"renameMessageInvalid": "Um arquivo não pode ter o contêiner \"/\" ou \"\\\"",
|
||||
"source": "Fonte{suffix}",
|
||||
"deleteMessageMultiple": "Tem certeza de que deseja excluir o(s) arquivo(s) {count}?",
|
||||
"deleteMessageShare": "Tem certeza de que deseja excluir esse compartilhamento: {path} ?",
|
||||
"lastModified": "Última modificação{suffix}",
|
||||
"numberDirs": "Número de diretórios{suffix}",
|
||||
"numberFiles": "Número de arquivos{suffix}",
|
||||
"renameMessageConflict": "Já existe um item chamado \"{filename}\"!",
|
||||
"uploadSettingsChunked": "Máximo de uploads simultâneos: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Máximo de uploads simultâneos: {maxConcurrentUpload}, upload em pedaços desativado"
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagens",
|
||||
"music": "Músicas",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Pressione Enter para pesquisar...",
|
||||
"search": "Pesquise...",
|
||||
"typeToSearch": "Digite para pesquisar...",
|
||||
"types": "Tipos",
|
||||
"video": "Vídeos",
|
||||
"path": "Caminho:",
|
||||
"smallerThan": "Menor que:",
|
||||
"largerThan": "Maior que:",
|
||||
"helpText1": "A pesquisa ocorre em cada caractere que você digita (mínimo de 3 caracteres para termos de pesquisa).",
|
||||
"helpText2": "O índice: A pesquisa utiliza o índice que é atualizado automaticamente no intervalo configurado (padrão: 5 minutos). A pesquisa quando o programa acaba de ser iniciado pode resultar em resultados incompletos."
|
||||
"helpText2": "O índice: A pesquisa utiliza o índice que é atualizado automaticamente no intervalo configurado (padrão: 5 minutos). A pesquisa quando o programa acaba de ser iniciado pode resultar em resultados incompletos.",
|
||||
"noResults": "Não foram encontrados resultados na pesquisa indexada.",
|
||||
"onlyFolders": "Somente pastas",
|
||||
"onlyFiles": "Somente arquivos",
|
||||
"showOptions": "Mostrar opções",
|
||||
"photos": "Fotos",
|
||||
"audio": "Áudio",
|
||||
"videos": "Vídeos",
|
||||
"documents": "Documentos",
|
||||
"archives": "Arquivos",
|
||||
"number": "Número",
|
||||
"searchContext": "Contexto da pesquisa: {context}",
|
||||
"notEnoughCharacters": "Não há caracteres suficientes para pesquisar (mín. {minSearchLength})",
|
||||
"typeToSearch": "Comece a digitar {minSearchLength} ou mais caracteres para iniciar a busca."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(deixe em branco para manter)",
|
||||
"branding": "Customização",
|
||||
"brandingDirectoryPath": "Diretório de customização",
|
||||
"brandingHelp": "Você pode mudar a aparência e experiência de sua instância do File Browser alterando seu nome, logotipo, adicionando estilos customizados e até desabilitando links externos para o GitHub.\nPara mais informações sobre customizações, confira {0}.",
|
||||
"changePassword": "Alterar senha",
|
||||
"commandRunner": "Execução de comandos",
|
||||
"commandRunnerHelp": "Aqui você pode definir comandos que serão executados nos eventos descritos. Escreva um por linha. As variáveis de ambiente {0} e {1} estão disponíveis, sendo {0} relativo a {1}. Para mais informações sobre esta função e as variáveis de ambiente disponíveis, leia a {2}.",
|
||||
"commandsUpdated": "Comandos atualizados!",
|
||||
"createUserDir": "Criar diretório Home para novos usuários",
|
||||
"userHomeBasePath": "Caminho base para diretórios de usuários",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Opções do usuário administrador",
|
||||
"shareDuration": "Expirações",
|
||||
"searchOptions": "Opções de pesquisa",
|
||||
"downloads": "Downloads"
|
||||
"downloads": "Downloads",
|
||||
"systemAdmin": "Sistema e administração",
|
||||
"configViewer": "Visualizador de configuração",
|
||||
"configViewerShowFull": "Mostrar configuração completa",
|
||||
"configViewerShowComments": "Mostrar comentários",
|
||||
"configViewerLoadConfig": "Configuração de carga",
|
||||
"brandingHelp": "Você pode personalizar a aparência da instância do Navegador de arquivos alterando seu nome, substituindo o logotipo, adicionando estilos personalizados e até mesmo desativando links externos para o GitHub.\nPara obter mais informações sobre branding personalizado, consulte {0}.",
|
||||
"commandRunnerHelp": "Aqui você pode definir comandos que são executados nos eventos nomeados. Você deve escrever um por linha. As variáveis de ambiente {0} e {1} estarão disponíveis, sendo {0} relativo a {1}. Para obter mais informações sobre esse recurso e as variáveis de ambiente disponíveis, leia {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Ajuda",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Ocultar botões de navegação",
|
||||
"hideNavButtonsDescription": "Oculte os botões de navegação na barra de navegação do compartilhamento para criar uma aparência minimalista.",
|
||||
"viewModeDescription": "Layout padrão para a página compartilhada.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Um compartilhamento foi enviado a você para visualização ou download.",
|
||||
"disableShareCard": "Desativar cartão compartilhado",
|
||||
"disableShareCardDescription": "Desative o cartão de compartilhamento na página compartilhada na barra lateral ou na parte superior da página no celular.",
|
||||
"disableSidebar": "Desativar a barra lateral",
|
||||
"disableSidebarDescription": "Desative a barra lateral na página compartilhada."
|
||||
"disableSidebarDescription": "Desative a barra lateral na página compartilhada.",
|
||||
"enforceDarkLightMode": "Aplicar o modo de tema",
|
||||
"enforceDarkLightModeDescription": "Força um modo de tema específico (escuro ou claro) para esse compartilhamento, substituindo as preferências do usuário.",
|
||||
"default": "Não imponha um modo",
|
||||
"dark": "Escuro",
|
||||
"light": "Luz",
|
||||
"enableOnlyOffice": "Ativar o visualizador do OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Permitir a visualização de arquivos do Office usando o OnlyOffice nesse compartilhamento.",
|
||||
"enableOnlyOfficeEditing": "Ativar a edição do OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Permitir a edição de arquivos do Office usando o OnlyOffice nesse compartilhamento.",
|
||||
"titleDefault": "Arquivos compartilhados - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Chaves de API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Negado",
|
||||
"totalAllowed": "Permitido",
|
||||
"all": "Negar tudo",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Isso mostra o que acontece quando um usuário tenta acessar um caminho em que não existem regras de acesso específicas. \"Permitir\" significa que os usuários podem acessar por padrão, \"Negar\" significa que os usuários são bloqueados por padrão, a menos que sejam explicitamente permitidos."
|
||||
"defaultBehaviorDescription": "Isso mostra o que acontece quando um usuário tenta acessar um caminho em que não existem regras de acesso específicas. \"Permitir\" significa que os usuários podem acessar por padrão, \"Negar\" significa que os usuários são bloqueados por padrão, a menos que sejam explicitamente permitidos.",
|
||||
"defaultBehavior": "Ação padrão{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Uploads e downloads",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - O tema padrão",
|
||||
"customTheme": "Escolha seu tema",
|
||||
"showSelectMultiple": "Mostrar seleção múltipla no menu de contexto na área de trabalho",
|
||||
"showSelectMultipleDescription": "Por padrão, o menu de contexto do desktop não tem a opção \"selecionar vários\" como no celular. Essa opção sempre a mostra no menu de contexto."
|
||||
"showSelectMultipleDescription": "Por padrão, o menu de contexto do desktop não tem a opção \"selecionar vários\" como no celular. Essa opção sempre a mostra no menu de contexto.",
|
||||
"defaultMediaPlayer": "Use o reprodutor de mídia nativo do seu navegador",
|
||||
"defaultMediaPlayerDescription": "Use o reprodutor de mídia nativo do seu navegador para arquivos de vídeo e áudio em vez do reprodutor de mídia 'vue-plyr' incluído no FileBrowser.",
|
||||
"debugOfficeEditor": "Ativar o modo de depuração do OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Mostrar um pop-up de depuração com informações adicionais no editor do OnlyOffice.",
|
||||
"fileViewerOptions": "Opções do visualizador de arquivos",
|
||||
"themeAndLanguage": "Tema e linguagem"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Falha ao inicializar o editor. Favor recarregar.",
|
||||
"saveFailed": "Falha ao salvar o arquivo. Tente novamente.",
|
||||
"saveAborted": "Salvamento abortado. O nome do arquivo não corresponde ao arquivo ativo.",
|
||||
"saveDisabled": "Não há suporte para o salvamento de arquivos compartilhados.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operação de salvamento abortada. O arquivo ativo do aplicativo ({activeFile}) não corresponde ao arquivo que você está tentando salvar ({tryingToSave}).",
|
||||
"syncFailed": "Falha ao sincronizar o estado com a rota para \"{filename}\" após 5 tentativas. Abortar a configuração do editor para evitar a corrupção de dados."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Loop ativado",
|
||||
"LoopDisabled": "Loop desativado"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Limpar concluído",
|
||||
"showMore": "Mostrar mais",
|
||||
"showLess": "Mostrar menos",
|
||||
"openParentFolder": "Abrir a pasta principal"
|
||||
"openParentFolder": "Abrir a pasta principal",
|
||||
"selectedCount": "Número de itens selecionados para executar uma ação"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar ficheiro",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "selecionar pasta ou ficheiro",
|
||||
"ctrl": {
|
||||
"click": "selecionar várias pastas e ficheiros",
|
||||
"f": "pesquisar",
|
||||
"s": "guardar um ficheiro ou descarrega a pasta em que está a navegar"
|
||||
"d": "descarregar itens selecionados"
|
||||
},
|
||||
"del": "eliminar os ficheiros selecionados",
|
||||
"doubleClick": "abrir pasta ou ficheiro",
|
||||
"esc": "limpar seleção e/ou fechar menu",
|
||||
"f1": "esta informação",
|
||||
"f2": "alterar nome do ficheiro",
|
||||
"help": "Ajuda"
|
||||
"help": "Ajuda",
|
||||
"f1": "mostrar prompt de ajuda",
|
||||
"description": "Pode ver as opções básicas de navegação abaixo. Para obter informações adicionais, visite",
|
||||
"wiki": "Wiki do FileBrowser Quantum"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "Copiar",
|
||||
"copyMessage": "Escolha um lugar para onde copiar os ficheiros:",
|
||||
"deleteMessageMultiple": "Quer eliminar {count} ficheiro(s)?",
|
||||
"deleteMessageSingle": "Quer eliminar esta pasta/ficheiro?",
|
||||
"deleteTitle": "Eliminar ficheiros",
|
||||
"displayName": "Nome:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "Escolha o formato do ficheiro que quer descarregar.",
|
||||
"error": "Algo correu mal",
|
||||
"fileInfo": "Informação do ficheiro",
|
||||
"lastModified": "Última alteração{suffix}",
|
||||
"move": "Mover",
|
||||
"moveMessage": "Escolha uma nova casa para os seus ficheiros/pastas:",
|
||||
"newArchetype": "Criar um novo post baseado num \"archetype\". O seu ficheiro será criado na pasta \"content\".",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "Escreva o nome da nova pasta.",
|
||||
"newFile": "Novo ficheiro",
|
||||
"newFileMessage": "Escreva o nome do novo ficheiro.",
|
||||
"numberDirs": "Número de pastas{suffix}",
|
||||
"numberFiles": "Número de ficheiros{suffix}",
|
||||
"rename": "Alterar nome",
|
||||
"renameMessage": "Insira um novo nome para",
|
||||
"replace": "Substituir",
|
||||
"replaceMessage": "Já existe um ficheiro com nome igual a um dos que está a tentar enviar. Quer substituí-lo?\n",
|
||||
"schedule": "Agendar",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "Mostrar",
|
||||
"size": "Tamanho",
|
||||
"upload": "Carregar",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "Tem a certeza de que pretende apagar este utilizador?",
|
||||
"selected": "selecionado",
|
||||
"filesSelected": "ficheiros selecionados",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Arrastar e largar ficheiros aqui",
|
||||
"completed": "Concluído",
|
||||
"conflictsDetected": "Conflitos detectados",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nome inválido fornecido: não pode conter \"/\" ou \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limite do número de descarregamentos",
|
||||
"maxBandwidth": "Largura de banda máxima de descarregamento (kbps)",
|
||||
"shareTheme": "Partilhar o tema",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Partilhar URL do banner",
|
||||
"shareFavicon": "Partilhar URL do favicon",
|
||||
"optionalPasswordHelp": "Se estiver definida, os utilizadores têm de introduzir esta palavra-passe para ver o conteúdo partilhado.",
|
||||
"viewMode": "Modo de visualização"
|
||||
"viewMode": "Modo de visualização",
|
||||
"renameMessage": "Introduzir um novo nome:",
|
||||
"renameMessageInvalid": "Um ficheiro não pode conter \"/\" ou \"\\\"",
|
||||
"source": "Fonte{suffix}",
|
||||
"deleteMessageMultiple": "Tem a certeza de que pretende apagar o(s) ficheiro(s) {count}?",
|
||||
"deleteMessageShare": "Tem a certeza de que pretende apagar esta partilha: {path} ?",
|
||||
"lastModified": "Última modificação{suffix}",
|
||||
"numberDirs": "Número de diretórios{suffix}",
|
||||
"numberFiles": "Número de ficheiros{suffix}",
|
||||
"renameMessageConflict": "Já existe um item com o nome \"{filename}\"!",
|
||||
"uploadSettingsChunked": "Máximo de carregamentos simultâneos: {maxConcurrentUpload}, tamanho do pedaço: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Máximo de carregamentos simultâneos: {maxConcurrentUpload}, carregamento em pedaços desativado"
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagens",
|
||||
"music": "Música",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Tecla Enter para pesquisar...",
|
||||
"search": "Pesquisar...",
|
||||
"typeToSearch": "Escrever para pesquisar...",
|
||||
"types": "Tipos",
|
||||
"video": "Vídeos",
|
||||
"path": "Caminho:",
|
||||
"smallerThan": "Mais pequeno do que:",
|
||||
"largerThan": "Maior do que:",
|
||||
"helpText1": "A pesquisa ocorre em cada caractere digitado (mínimo de 3 caracteres para termos de pesquisa).",
|
||||
"helpText2": "O índice: A pesquisa utiliza o índice que é atualizado automaticamente no intervalo configurado (predefinição: 5 minutos). A pesquisa quando o programa acabou de ser iniciado pode resultar em resultados incompletos."
|
||||
"helpText2": "O índice: A pesquisa utiliza o índice que é atualizado automaticamente no intervalo configurado (predefinição: 5 minutos). A pesquisa quando o programa acabou de ser iniciado pode resultar em resultados incompletos.",
|
||||
"noResults": "Não foram encontrados resultados na pesquisa indexada.",
|
||||
"onlyFolders": "Apenas pastas",
|
||||
"onlyFiles": "Apenas ficheiros",
|
||||
"showOptions": "Mostrar opções",
|
||||
"photos": "Fotos",
|
||||
"audio": "Áudio",
|
||||
"videos": "Vídeos",
|
||||
"documents": "Documentos",
|
||||
"archives": "Arquivos",
|
||||
"number": "Número",
|
||||
"searchContext": "Contexto de pesquisa: {context}",
|
||||
"notEnoughCharacters": "Não há caracteres suficientes para pesquisar (min {minSearchLength})",
|
||||
"typeToSearch": "Comece a escrever {minSearchLength} ou mais caracteres para iniciar a pesquisa."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(deixe em branco para manter)",
|
||||
"branding": "Marca",
|
||||
"brandingDirectoryPath": "Caminho da pasta de marca",
|
||||
"brandingHelp": "Pode personalizar a aparência do seu Navegador de Ficheiros, alterar o nome, substituindo o logótipo, adicionando estilos personalizados e mesmo desativando links externos para o GitHub.\nPara mais informações sobre marca personalizada, por favor veja {0}.",
|
||||
"changePassword": "Alterar palavra-passe",
|
||||
"commandRunner": "Execução de comandos",
|
||||
"commandRunnerHelp": "Aqui pode definir comandos que são executados nos eventos nomeados. Tem de escrever um por linha. As variáveis de ambiente {0} e {1} estarão disponíveis, sendo {0} relativo a {1}. Para mais informações sobre esta funcionalidade e as variáveis de ambiente, veja {2}.",
|
||||
"commandsUpdated": "Comandos atualizados!",
|
||||
"createUserDir": "Criar automaticamente a pasta de início ao adicionar um novo utilizador",
|
||||
"customStylesheet": "Folha de estilos personalizada",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Opções do utilizador administrador",
|
||||
"shareDuration": "Expirações",
|
||||
"searchOptions": "Opções de pesquisa",
|
||||
"downloads": "Transferências"
|
||||
"downloads": "Transferências",
|
||||
"systemAdmin": "Sistema e administração",
|
||||
"configViewer": "Visualizador de configuração",
|
||||
"configViewerShowFull": "Mostrar configuração completa",
|
||||
"configViewerShowComments": "Mostrar comentários",
|
||||
"configViewerLoadConfig": "Configuração de carga",
|
||||
"brandingHelp": "É possível personalizar a aparência da instância do Navegador de arquivos alterando seu nome, substituindo o logotipo, adicionando estilos personalizados e até mesmo desativando links externos para o GitHub.\nPara obter mais informações sobre a marca personalizada, consulte {0}.",
|
||||
"commandRunnerHelp": "Aqui pode definir comandos que são executados nos eventos nomeados. Deve escrever um por linha. As variáveis de ambiente {0} e {1} estarão disponíveis, sendo {0} relativo a {1}. Para obter mais informações sobre este recurso e as variáveis de ambiente disponíveis, leia {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Ajuda",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Ocultar botões de navegação",
|
||||
"hideNavButtonsDescription": "Oculte os botões de navegação na barra de navegação na partilha para criar um aspeto minimalista.",
|
||||
"viewModeDescription": "Esquema predefinido para a página partilhada.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Foi-lhe enviada uma ação para visualizar ou descarregar.",
|
||||
"disableShareCard": "Desativar o cartão de partilha",
|
||||
"disableShareCardDescription": "Desativar o cartão de partilha na página partilhada na barra lateral ou na parte superior da página no telemóvel.",
|
||||
"disableSidebar": "Desativar a barra lateral",
|
||||
"disableSidebarDescription": "Desativar a barra lateral na página partilhada."
|
||||
"disableSidebarDescription": "Desativar a barra lateral na página partilhada.",
|
||||
"enforceDarkLightMode": "Aplicar o modo de tema",
|
||||
"enforceDarkLightModeDescription": "Força um modo de tema específico (escuro ou claro) para esta partilha, substituindo as preferências do utilizador.",
|
||||
"default": "Não impor um modo",
|
||||
"dark": "Escuro",
|
||||
"light": "Luz",
|
||||
"enableOnlyOffice": "Ativar o visualizador OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Permitir a visualização de ficheiros de escritório utilizando o OnlyOffice nesta partilha.",
|
||||
"enableOnlyOfficeEditing": "Ativar a edição OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Permitir a edição de ficheiros do Office utilizando o OnlyOffice nesta partilha.",
|
||||
"titleDefault": "Ficheiros partilhados - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Chaves da API",
|
||||
@@ -478,7 +505,13 @@
|
||||
"defaultThemeDescription": "default - O tema predefinido",
|
||||
"customTheme": "Escolha o seu tema",
|
||||
"showSelectMultiple": "Mostrar seleção múltipla no menu de contexto no ambiente de trabalho",
|
||||
"showSelectMultipleDescription": "Por predefinição, o menu de contexto do ambiente de trabalho não tem a opção \"selecionar vários\" como no telemóvel. Esta opção mostra-a sempre no menu de contexto."
|
||||
"showSelectMultipleDescription": "Por predefinição, o menu de contexto do ambiente de trabalho não tem a opção \"selecionar vários\" como no telemóvel. Esta opção mostra-a sempre no menu de contexto.",
|
||||
"defaultMediaPlayer": "Utilize o leitor multimédia nativo do seu navegador",
|
||||
"defaultMediaPlayerDescription": "Utilize o leitor multimédia nativo do seu navegador para ficheiros de vídeo e áudio em vez do leitor multimédia 'vue-plyr' incluído no FileBrowser.",
|
||||
"debugOfficeEditor": "Ativar o modo de depuração do OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Mostrar um popup de depuração com informações adicionais no editor OnlyOffice.",
|
||||
"fileViewerOptions": "Opções do visualizador de ficheiros",
|
||||
"themeAndLanguage": "Tema e linguagem"
|
||||
},
|
||||
"general": {
|
||||
"name": "Nome",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "Nome",
|
||||
"accessManagement": "Gestão do acesso",
|
||||
"all": "Recusar tudo",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Isto mostra o que acontece quando um utilizador tenta aceder a um caminho onde não existem regras de acesso específicas. \"Permitir\" significa que os utilizadores podem aceder por predefinição, \"Negar\" significa que os utilizadores são bloqueados por predefinição, a menos que sejam explicitamente autorizados."
|
||||
"defaultBehaviorDescription": "Isto mostra o que acontece quando um utilizador tenta aceder a um caminho onde não existem regras de acesso específicas. \"Permitir\" significa que os utilizadores podem aceder por predefinição, \"Negar\" significa que os utilizadores são bloqueados por predefinição, a menos que sejam explicitamente autorizados.",
|
||||
"defaultBehavior": "Ação por defeito{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Falha ao inicializar o editor. Por favor, recarregue.",
|
||||
"saveFailed": "Falha ao guardar o ficheiro. Por favor, tente novamente.",
|
||||
"saveAborted": "Gravação abortada. O nome do ficheiro não corresponde ao ficheiro ativo.",
|
||||
"saveDisabled": "A gravação de ficheiros partilhados não é suportada.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operação de gravação abortada. O ficheiro ativo da aplicação ({activeFile}) não corresponde ao ficheiro que está a tentar guardar ({tryingToSave}).",
|
||||
"syncFailed": "não conseguiu sincronizar o estado com a rota para \"{filename}\" após 5 tentativas. Abortar a configuração do editor para evitar a corrupção de dados."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Loop ativado",
|
||||
"LoopDisabled": "Loop desativado"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Golire finalizată",
|
||||
"showMore": "Arată mai multe",
|
||||
"showLess": "Arată mai puțin",
|
||||
"openParentFolder": "Deschideți folderul părinte"
|
||||
"openParentFolder": "Deschideți folderul părinte",
|
||||
"selectedCount": "Numărul de elemente selectate pentru a efectua o acțiune"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarcă fișier",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "alege fișier sau director",
|
||||
"ctrl": {
|
||||
"click": "alege multiple fișiere sau directoare",
|
||||
"f": "deschide căutarea",
|
||||
"s": "salvează un fișier sau descarcă directorul în care te afli"
|
||||
"d": "descărcați elementele selectate"
|
||||
},
|
||||
"del": "șterge elementele selectate",
|
||||
"doubleClick": "deschide un fișier sau director",
|
||||
"esc": "elibereaza selecția și/sau închide fereastra",
|
||||
"f1": "această informație",
|
||||
"f2": "redenumește fișierul",
|
||||
"help": "Ajutor"
|
||||
"help": "Ajutor",
|
||||
"f1": "arată ajutor prompt",
|
||||
"description": "Puteți vizualiza mai jos opțiunile de navigație de bază. Pentru informații suplimentare, vă rugăm să vizitați",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "Copiază",
|
||||
"copyMessage": "Alege unde vrei să copiezi fișierele:",
|
||||
"deleteMessageMultiple": "Ești sigur că vrei să ștergi aceste {count} fișier(e)?",
|
||||
"deleteMessageSingle": "Ești sigur că vrei să ștergi acest fișier/director?",
|
||||
"deleteTitle": "Șterge fișiere",
|
||||
"displayName": "Nume afișat:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "Alege formatul în care vrei să descarci.",
|
||||
"error": "Ceva n-a funcționat cum trebuie",
|
||||
"fileInfo": "Informații fișier",
|
||||
"lastModified": "Ultima modificare{suffix}",
|
||||
"move": "Mută",
|
||||
"moveMessage": "Alege destinația:",
|
||||
"newArchetype": "Crează o nouă postare pe baza unui șablon. Fișierul va fi creat in directorul conținut.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "Scrie numele noului director.",
|
||||
"newFile": "Fișier nou",
|
||||
"newFileMessage": "Scrie numele noului fișier.",
|
||||
"numberDirs": "Numărul directoarelor{suffix}",
|
||||
"numberFiles": "Numărul fișierelor{suffix}",
|
||||
"rename": "Redenumește",
|
||||
"renameMessage": "Redactează un nou nume pentru",
|
||||
"replace": "Înlocuiește",
|
||||
"replaceMessage": "Unul din fișierele încărcate intră în conflict din cauza denumrii. Vrei să înlocuiești fișierul existent?\n",
|
||||
"schedule": "Programare",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "Arată",
|
||||
"size": "Dimensiune",
|
||||
"upload": "Încărcare",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "Sunteți sigur că doriți să ștergeți acest utilizator?",
|
||||
"filesSelected": "fișiere selectate",
|
||||
"optionalPassword": "Parolă opțională",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Drag and drop fișiere aici",
|
||||
"completed": "Completat",
|
||||
"conflictsDetected": "Conflicte detectate",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Nume invalid furnizat: nu poate conține \"/\" sau \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limita numărului de descărcări",
|
||||
"maxBandwidth": "Lățime de bandă maximă de descărcare (kbps)",
|
||||
"shareTheme": "Temă de partajare",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Partajați URL-ul bannerului",
|
||||
"shareFavicon": "Partajați URL favicon",
|
||||
"optionalPasswordHelp": "Dacă este setată, utilizatorii trebuie să introducă această parolă pentru a vizualiza conținutul partajat.",
|
||||
"viewMode": "Mod de vizualizare"
|
||||
"viewMode": "Mod de vizualizare",
|
||||
"renameMessage": "Introduceți un nume nou:",
|
||||
"renameMessageInvalid": "Un fișier nu poate conține \"/\" sau \"\\\"",
|
||||
"source": "Sursa{suffix}",
|
||||
"deleteMessageMultiple": "Sunteți sigur că doriți să eliminați fișierul (fișierele) {count}?",
|
||||
"deleteMessageShare": "Sunteți sigur că doriți să ștergeți această partajare: {path} ?",
|
||||
"lastModified": "Ultima modificare{suffix}",
|
||||
"numberDirs": "Număr de directoare{suffix}",
|
||||
"numberFiles": "Număr de fișiere{suffix}",
|
||||
"renameMessageConflict": "Un element numit \"{filename}\" există deja!",
|
||||
"uploadSettingsChunked": "Încărcări simultane maxime: {maxConcurrentUpload}, dimensiunea bucăților: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Încărcări simultane maxime: {maxConcurrentUpload}, încărcare fragmentată dezactivată"
|
||||
},
|
||||
"search": {
|
||||
"images": "Imagini",
|
||||
"music": "Muzică",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Apasă enter pentru a căuta...",
|
||||
"search": "Caută...",
|
||||
"typeToSearch": "Scrie pentru a căuta...",
|
||||
"types": "Tipuri",
|
||||
"video": "Video",
|
||||
"path": "Calea:",
|
||||
"smallerThan": "Mai mic decât:",
|
||||
"largerThan": "Mai mare decât:",
|
||||
"helpText1": "Căutarea are loc pe fiecare caracter tastat (minim 3 caractere pentru termenii de căutare).",
|
||||
"helpText2": "Indexul: Căutarea utilizează indexul care se actualizează automat la intervalul configurat (implicit: 5 minute). Căutarea atunci când programul tocmai a început poate duce la rezultate incomplete."
|
||||
"helpText2": "Indexul: Căutarea utilizează indexul care se actualizează automat la intervalul configurat (implicit: 5 minute). Căutarea atunci când programul tocmai a început poate duce la rezultate incomplete.",
|
||||
"noResults": "Nu s-au găsit rezultate în căutarea indexată.",
|
||||
"onlyFolders": "Numai foldere",
|
||||
"onlyFiles": "Numai fișiere",
|
||||
"showOptions": "Afișare opțiuni",
|
||||
"photos": "Fotografii",
|
||||
"audio": "Audio",
|
||||
"videos": "Video",
|
||||
"documents": "Documente",
|
||||
"archives": "Arhive",
|
||||
"number": "Număr",
|
||||
"searchContext": "Context de căutare: {context}",
|
||||
"notEnoughCharacters": "Nu sunt suficiente caractere pentru căutare (min {minSearchLength})",
|
||||
"typeToSearch": "Începeți să tastați {minSearchLength} sau mai multe caractere pentru a începe căutarea."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(lasă gol pentru a nu schimba)",
|
||||
"branding": "Branding",
|
||||
"brandingDirectoryPath": "Calea către directorul de branding",
|
||||
"brandingHelp": "Poți personaliza cum arată instanța ta de File Browser modificându-i numele, înlocuindu-i logo-ul, adăugându-i stiluri personalizate si chiar dezactivând linkurile catre GitHub.\nPentru mai multe informații despre branding fă click aici",
|
||||
"changePassword": "Schimbă parola",
|
||||
"commandRunner": "Rulează comenzi",
|
||||
"commandRunnerHelp": "Aici poti seta comenzile care sunt executate in evenimente. Trebuie să scrii una pe linie. Variabilele de mediu {0} și {1} vor fi disponile, {0} fiind relativă la {1}. Pentru mai multe informații despre acest feature si variabilele de mediu disponibile, cititi {2}.",
|
||||
"commandsUpdated": "Comenzi actualizate!",
|
||||
"createUserDir": "Auto create user home dir while adding new user",
|
||||
"customStylesheet": "CSS personalizat",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Opțiuni utilizator administrator",
|
||||
"shareDuration": "Expiră",
|
||||
"searchOptions": "Opțiuni de căutare",
|
||||
"downloads": "Descărcări"
|
||||
"downloads": "Descărcări",
|
||||
"systemAdmin": "Sistem și administrare",
|
||||
"configViewer": "Vizualizator de configurare",
|
||||
"configViewerShowFull": "Afișați configurația completă",
|
||||
"configViewerShowComments": "Afișați comentariile",
|
||||
"configViewerLoadConfig": "Configurare încărcare",
|
||||
"brandingHelp": "Puteți personaliza aspectul instanței File Browser prin schimbarea numelui, înlocuirea logo-ului, adăugarea de stiluri personalizate și chiar dezactivarea linkurilor externe către GitHub.\nPentru mai multe informații despre brandingul personalizat, consultați {0}.",
|
||||
"commandRunnerHelp": "Aici puteți seta comenzi care sunt executate în cadrul evenimentelor numite. Trebuie să scrieți câte una pe linie. Variabilele de mediu {0} și {1} vor fi disponibile, {0} fiind relativ la {1}. Pentru mai multe informații despre această caracteristică și variabilele de mediu disponibile, vă rugăm să citiți {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Ajutor",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Ascundeți butoanele de navigare",
|
||||
"hideNavButtonsDescription": "Ascundeți butoanele de navigare de pe bara de navigare din share pentru a crea un aspect minimalist.",
|
||||
"viewModeDescription": "Layout implicit pentru pagina partajată.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "V-a fost trimisă o acțiune pentru a o vizualiza sau descărca.",
|
||||
"disableShareCard": "Dezactivați cardul de partajare",
|
||||
"disableShareCardDescription": "Dezactivați cardul de partajare pe pagina partajată în bara laterală sau în partea de sus a paginii pe mobil.",
|
||||
"disableSidebar": "Dezactivați bara laterală",
|
||||
"disableSidebarDescription": "Dezactivați bara laterală de pe pagina partajată."
|
||||
"disableSidebarDescription": "Dezactivați bara laterală de pe pagina partajată.",
|
||||
"enforceDarkLightMode": "Impuneți modul tematic",
|
||||
"enforceDarkLightModeDescription": "Forțează un anumit mod tematic (întunecat sau deschis) pentru această partajare, trecând peste preferințele utilizatorului.",
|
||||
"default": "Nu impuneți un mod",
|
||||
"dark": "Întuneric",
|
||||
"light": "Lumină",
|
||||
"enableOnlyOffice": "Activați vizualizatorul OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Permiteți vizualizarea fișierelor office utilizând OnlyOffice în această partajare.",
|
||||
"enableOnlyOfficeEditing": "Activați editarea OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Permiteți editarea fișierelor de birou utilizând OnlyOffice în această partajare.",
|
||||
"titleDefault": "Fișiere partajate - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Chei API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Refuzat",
|
||||
"totalAllowed": "Permise",
|
||||
"all": "Refuzați toate",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Aceasta arată ce se întâmplă atunci când un utilizator încearcă să acceseze o cale unde nu există reguli specifice de acces. \"Allow\" înseamnă că utilizatorii pot accesa în mod implicit, \"Deny\" înseamnă că utilizatorii sunt blocați în mod implicit dacă nu li se permite în mod explicit."
|
||||
"defaultBehaviorDescription": "Aceasta arată ce se întâmplă atunci când un utilizator încearcă să acceseze o cale unde nu există reguli specifice de acces. \"Allow\" înseamnă că utilizatorii pot accesa în mod implicit, \"Deny\" înseamnă că utilizatorii sunt blocați în mod implicit dacă nu li se permite în mod explicit.",
|
||||
"defaultBehavior": "Acțiune implicită{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Încărcări și descărcări",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Tema implicită",
|
||||
"customTheme": "Alege-ți tema",
|
||||
"showSelectMultiple": "Afișarea selecției multiple în meniul contextual de pe desktop",
|
||||
"showSelectMultipleDescription": "În mod implicit, meniul contextual de pe desktop nu are opțiunea \"select multiple\", ca pe mobil. Această opțiune se afișează întotdeauna în meniul contextual."
|
||||
"showSelectMultipleDescription": "În mod implicit, meniul contextual de pe desktop nu are opțiunea \"select multiple\", ca pe mobil. Această opțiune se afișează întotdeauna în meniul contextual.",
|
||||
"defaultMediaPlayer": "Utilizați playerul media nativ al browserului dvs.",
|
||||
"defaultMediaPlayerDescription": "Utilizați playerul media nativ al browserului dvs. pentru fișierele video și audio mai degrabă decât playerul media \"vue-plyr\" inclus în FileBrowser.",
|
||||
"debugOfficeEditor": "Activați modul de depanare OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Afișați o fereastră pop-up de depanare cu informații suplimentare în editorul OnlyOffice.",
|
||||
"fileViewerOptions": "Opțiuni vizualizator de fișiere",
|
||||
"themeAndLanguage": "Temă și limbaj"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Nu s-a reușit inițializarea editorului. Vă rugăm să reîncărcați.",
|
||||
"saveFailed": "Nu ați reușit să salvați fișierul. Vă rugăm să încercați din nou.",
|
||||
"saveAborted": "Salvare întreruptă. Numele fișierului nu corespunde fișierului activ.",
|
||||
"saveDisabled": "Salvarea fișierelor partajate nu este acceptată.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operațiunea de salvare a fost întreruptă. Fișierul activ al aplicației ({activeFile}) nu corespunde cu fișierul pe care încercați să îl salvați ({tryingToSave}).",
|
||||
"syncFailed": "nu a reușit să sincronizeze starea cu ruta pentru \"{filename}\" după 5 încercări. Întreruperea configurării editorului pentru a preveni coruperea datelor."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Bucla activată",
|
||||
"LoopDisabled": "Bucla dezactivată"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Очистка завершена",
|
||||
"showMore": "Показать еще",
|
||||
"showLess": "Показать меньше",
|
||||
"openParentFolder": "Откройте родительскую папку"
|
||||
"openParentFolder": "Откройте родительскую папку",
|
||||
"selectedCount": "Количество элементов, выбранных для выполнения действия"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Скачать файл",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "выбрать файл или каталог",
|
||||
"ctrl": {
|
||||
"click": "выбрать несколько файлов или каталогов",
|
||||
"f": "открыть поиск",
|
||||
"s": "скачать файл или текущий каталог"
|
||||
"d": "скачать выбранные элементы"
|
||||
},
|
||||
"del": "удалить выбранные элементы",
|
||||
"doubleClick": "открыть файл или каталог",
|
||||
"esc": "очистить выделение и/или закрыть окно",
|
||||
"f1": "помощь",
|
||||
"f2": "переименовать файл",
|
||||
"help": "Помощь"
|
||||
"help": "Помощь",
|
||||
"f1": "показать подсказку",
|
||||
"description": "Ниже представлены основные варианты навигации. Для получения дополнительной информации, пожалуйста, посетите",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Копировать",
|
||||
"copyMessage": "Копировать в:",
|
||||
"deleteMessageMultiple": "Удалить эти файлы ({count})?",
|
||||
"deleteMessageSingle": "Удалить этот файл/каталог?",
|
||||
"deleteMessageShare": "Удалить этот общий файл/каталог ({path})?",
|
||||
"deleteTitle": "Удалить файлы",
|
||||
"displayName": "Отображаемое имя:",
|
||||
"download": "Скачать файлы",
|
||||
"downloadMessage": "Выберите формат в котором хотите скачать.",
|
||||
"error": "Ошибка",
|
||||
"fileInfo": "Информация о файле",
|
||||
"lastModified": "Последнее изменение{suffix}",
|
||||
"move": "Переместить",
|
||||
"moveMessage": "Переместить в:",
|
||||
"newArchetype": "Создайте новую запись на основе архетипа. Файл будет создан в каталоге.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Имя нового каталога.",
|
||||
"newFile": "Новый файл",
|
||||
"newFileMessage": "Имя нового файла.",
|
||||
"numberDirs": "Количество каталогов{suffix}",
|
||||
"numberFiles": "Количество файлов{suffix}",
|
||||
"rename": "Переименовать",
|
||||
"renameMessage": "Новое имя",
|
||||
"replace": "Заменить",
|
||||
"replaceMessage": "Имя одного из загружаемых файлов совпадает с уже существующим файлом. Вы хотите заменить существующий?\n",
|
||||
"schedule": "Планировка",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Перетащите файлы сюда",
|
||||
"completed": "Завершено",
|
||||
"conflictsDetected": "Обнаруженные конфликты",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Неверное имя: не может содержать \"/\" или \"\\\".",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Ограничение на количество загрузок",
|
||||
"maxBandwidth": "Максимальная пропускная способность загрузки (кб/с)",
|
||||
"shareTheme": "Поделиться темой",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Поделиться URL-адресом баннера",
|
||||
"shareFavicon": "Поделиться URL-адресом favicon",
|
||||
"optionalPasswordHelp": "Если установлен, пользователи должны ввести этот пароль для просмотра общего содержимого.",
|
||||
"viewMode": "Режим просмотра"
|
||||
"viewMode": "Режим просмотра",
|
||||
"renameMessage": "Введите новое имя:",
|
||||
"renameMessageInvalid": "Файл не может содержать \"/\" или \"\\\".",
|
||||
"source": "Источник{suffix}",
|
||||
"deleteMessageMultiple": "Вы уверены, что хотите удалить файл(ы) {count}?",
|
||||
"deleteMessageShare": "Вы уверены, что хотите удалить этот ресурс: {path} ?",
|
||||
"lastModified": "Последнее изменение{suffix}",
|
||||
"numberDirs": "Количество каталогов{suffix}",
|
||||
"numberFiles": "Количество файлов{suffix}",
|
||||
"renameMessageConflict": "Элемент с именем \"{filename}\" уже существует!",
|
||||
"uploadSettingsChunked": "Максимальное количество одновременных загрузок: {maxConcurrentUpload}, размер чанка: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Максимальное количество одновременных загрузок: {maxConcurrentUpload}, загрузка в куски отключена"
|
||||
},
|
||||
"search": {
|
||||
"images": "Изображения",
|
||||
"music": "Музыка",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Нажмите Enter для поиска ...",
|
||||
"search": "Поиск...",
|
||||
"typeToSearch": "Введите имя файла ...",
|
||||
"types": "Типы",
|
||||
"video": "Видео",
|
||||
"path": "Путь:",
|
||||
"smallerThan": "Меньше, чем:",
|
||||
"largerThan": "Больше, чем:",
|
||||
"helpText1": "Поиск осуществляется по каждому введенному символу (минимум 3 символа для поисковых запросов).",
|
||||
"helpText2": "Индекс: Для поиска используется индекс, который автоматически обновляется с заданным интервалом (по умолчанию: 5 минут). Поиск, когда программа только что запущена, может привести к неполным результатам."
|
||||
"helpText2": "Индекс: Для поиска используется индекс, который автоматически обновляется с заданным интервалом (по умолчанию: 5 минут). Поиск, когда программа только что запущена, может привести к неполным результатам.",
|
||||
"noResults": "В проиндексированном поиске результатов не найдено.",
|
||||
"onlyFolders": "Только папки",
|
||||
"onlyFiles": "Только файлы",
|
||||
"showOptions": "Показать параметры",
|
||||
"photos": "Фотографии",
|
||||
"audio": "Аудио",
|
||||
"videos": "Видео",
|
||||
"documents": "Документы",
|
||||
"archives": "Архивы",
|
||||
"number": "Номер",
|
||||
"searchContext": "Контекст поиска: {context}",
|
||||
"notEnoughCharacters": "Недостаточно символов для поиска (мин. {minSearchLength})",
|
||||
"typeToSearch": "Начните вводить {minSearchLength} или более символов, чтобы начать поиск."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Админ",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(оставьте поле пустым, чтобы избежать изменений)",
|
||||
"branding": "Брендинг",
|
||||
"brandingDirectoryPath": "Путь к каталогу брендов",
|
||||
"brandingHelp": "Вы можете настроить внешний вид файлового браузера, изменив его имя, заменив логотип, добавив собственные стили и даже отключив внешние ссылки на GitHub.\nДополнительную информацию о персонализированном брендинге можно найти на странице {0}.",
|
||||
"changePassword": "Изменение пароля",
|
||||
"commandRunner": "Запуск команд",
|
||||
"commandRunnerHelp": "Здесь вы можете установить команды, которые будут выполняться в указанных событиях. Вы должны указать по одной команде в каждой строке. Переменные среды {0} и {1} будут доступны, будучи {0} относительно {1}. Дополнительные сведения об этой функции и доступных переменных среды см. В {2}.",
|
||||
"commandsUpdated": "Команды обновлены!",
|
||||
"createUserDir": "Автоматическое создание домашнего каталога пользователя при добавлении нового пользователя",
|
||||
"customStylesheet": "Свой стиль",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Параметры пользователя администратора",
|
||||
"shareDuration": "Срок действия:",
|
||||
"searchOptions": "Параметры поиска",
|
||||
"downloads": "Скачать"
|
||||
"downloads": "Скачать",
|
||||
"systemAdmin": "Система и администрирование",
|
||||
"configViewer": "Программа просмотра конфигурации",
|
||||
"configViewerShowFull": "Показать полную конфигурацию",
|
||||
"configViewerShowComments": "Показать комментарии",
|
||||
"configViewerLoadConfig": "Конфигурация нагрузки",
|
||||
"brandingHelp": "Вы можете настроить внешний вид экземпляра File Browser, изменив его название, заменив логотип, добавив пользовательские стили и даже отключив внешние ссылки на GitHub.\nБолее подробную информацию о пользовательском брендинге можно найти на сайте {0}.",
|
||||
"commandRunnerHelp": "Здесь вы можете задать команды, которые будут выполняться в названных событиях. Вы должны написать по одной команде в строке. Будут доступны переменные окружения {0} и {1}, причем {0} будет относиться к {1}. Для получения дополнительной информации об этой функции и доступных переменных окружения, пожалуйста, прочтите {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Помощь",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Скрыть кнопки навигации",
|
||||
"hideNavButtonsDescription": "Спрячьте кнопки навигации на навигационной панели в ресурсе, чтобы создать минималистичный вид.",
|
||||
"viewModeDescription": "Макет по умолчанию для общей страницы.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Вам отправлена акция для просмотра или загрузки.",
|
||||
"disableShareCard": "Отключение карты доступа",
|
||||
"disableShareCardDescription": "Отключите карточку общего доступа на странице общего доступа в боковой панели или в верхней части страницы на мобильных устройствах.",
|
||||
"disableSidebar": "Отключить боковую панель",
|
||||
"disableSidebarDescription": "Отключите боковую панель на общей странице."
|
||||
"disableSidebarDescription": "Отключите боковую панель на общей странице.",
|
||||
"enforceDarkLightMode": "Включение режима темы",
|
||||
"enforceDarkLightModeDescription": "Принудительно устанавливает определенный режим темы (темный или светлый) для этого ресурса, отменяя предпочтения пользователя.",
|
||||
"default": "Не вводите режим",
|
||||
"dark": "Темнота",
|
||||
"light": "Свет",
|
||||
"enableOnlyOffice": "Включите программу просмотра OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Разрешите просмотр офисных файлов с помощью OnlyOffice в этом ресурсе.",
|
||||
"enableOnlyOfficeEditing": "Включить редактирование OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Разрешите редактирование офисных файлов с помощью OnlyOffice в этом ресурсе.",
|
||||
"titleDefault": "Общие файлы - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Ключи API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Отказано",
|
||||
"totalAllowed": "Разрешено",
|
||||
"all": "Запретить все",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Здесь показано, что происходит, когда пользователь пытается получить доступ к пути, для которого не существует специальных правил доступа. 'Allow' означает, что пользователи могут получить доступ по умолчанию, 'Deny' означает, что пользователи блокируются по умолчанию, если только они не разрешены явным образом."
|
||||
"defaultBehaviorDescription": "Здесь показано, что происходит, когда пользователь пытается получить доступ к пути, для которого не существует специальных правил доступа. 'Allow' означает, что пользователи могут получить доступ по умолчанию, 'Deny' означает, что пользователи блокируются по умолчанию, если только они не разрешены явным образом.",
|
||||
"defaultBehavior": "Действие по умолчанию{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Загрузка и скачивание",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Тема по умолчанию",
|
||||
"customTheme": "Выберите тему",
|
||||
"showSelectMultiple": "Отображение выбора нескольких в контекстном меню на рабочем столе",
|
||||
"showSelectMultipleDescription": "По умолчанию в контекстном меню рабочего стола нет опции \"выбрать несколько\", как в мобильном. Эта опция всегда отображается в контекстном меню."
|
||||
"showSelectMultipleDescription": "По умолчанию в контекстном меню рабочего стола нет опции \"выбрать несколько\", как в мобильном. Эта опция всегда отображается в контекстном меню.",
|
||||
"defaultMediaPlayer": "Используйте встроенный медиаплеер вашего браузера",
|
||||
"defaultMediaPlayerDescription": "Для воспроизведения видео- и аудиофайлов используйте штатный медиаплеер браузера, а не медиаплеер 'vue-plyr', входящий в комплект поставки FileBrowser.",
|
||||
"debugOfficeEditor": "Включить режим отладки OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Отображение всплывающего окна отладки с дополнительной информацией в редакторе OnlyOffice.",
|
||||
"fileViewerOptions": "Параметры просмотра файлов",
|
||||
"themeAndLanguage": "Тема и язык"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Не удалось инициализировать редактор. Пожалуйста, перезагрузитесь.",
|
||||
"saveFailed": "Не удалось сохранить файл. Пожалуйста, попробуйте еще раз.",
|
||||
"saveAborted": "Сохранение прервано. Имя файла не совпадает с именем активного файла.",
|
||||
"saveDisabled": "Сохранение общих файлов не поддерживается.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Операция сохранения прервана. Активный файл приложения ({activeFile}) не совпадает с файлом, который вы пытаетесь сохранить ({tryingToSave}).",
|
||||
"syncFailed": "не удалось синхронизировать состояние с маршрутом для \"{filename}\" после 5 попыток. Прерываю настройку редактора, чтобы предотвратить повреждение данных."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Петля включена",
|
||||
"LoopDisabled": "Петля отключена"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Vyčistiť dokončené",
|
||||
"showMore": "Zobraziť viac",
|
||||
"showLess": "Zobraziť menej",
|
||||
"openParentFolder": "Otvorenie nadradeného priečinka"
|
||||
"openParentFolder": "Otvorenie nadradeného priečinka",
|
||||
"selectedCount": "Počet položiek vybraných na vykonanie akcie"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stiahnuť súbor",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "vyberie súbor alebo priečinok",
|
||||
"ctrl": {
|
||||
"click": "vyberie viac súborov alebo priečinkov",
|
||||
"f": "otvorí vyhľadávanie",
|
||||
"s": "uloží súbor alebo stiahne priečinok tam kde ste"
|
||||
"d": "stiahnuť vybrané položky"
|
||||
},
|
||||
"del": "odstráni vybraté položky",
|
||||
"doubleClick": "otvorí súbor alebo priečinok",
|
||||
"esc": "zruší výber a/alebo zavrie okno",
|
||||
"f1": "tieto informácie",
|
||||
"f2": "premenuje súbor",
|
||||
"help": "Pomoc"
|
||||
"help": "Pomoc",
|
||||
"f1": "zobraziť výzvu na pomoc",
|
||||
"description": "Základné možnosti navigácie si môžete pozrieť nižšie. Ďalšie informácie nájdete na stránke",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Kopírovať",
|
||||
"copyMessage": "Zvoľte miesto, kde chcete kopírovať súbory:",
|
||||
"deleteMessageMultiple": "Naozaj chcete odstrániť {count} súbor(ov)?",
|
||||
"deleteMessageSingle": "Naozaj chcete odstrániť tento súbor/priečinok?",
|
||||
"deleteMessageShare": "Naozaj chcete odstrániť toto zdieľanie({path})?",
|
||||
"deleteTitle": "Odstránenie súborov",
|
||||
"displayName": "Zobrazený názov:",
|
||||
"download": "Stiahnuť súbory",
|
||||
"downloadMessage": "Vyberte formát, ktorý chcete stiahnuť.",
|
||||
"error": "Niečo sa pokazilo",
|
||||
"fileInfo": "Informácie o súbore",
|
||||
"lastModified": "Dátum zmeny{suffix}",
|
||||
"move": "Presunúť",
|
||||
"moveMessage": "Zvoľte nový domov pre vaše súbory/priečinky:",
|
||||
"newArchetype": "Vytvorí nový príspevok z archetypu. Nový súbor sa vytvorí v priečinku s obsahom.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Napíšte názov nového priečinka.",
|
||||
"newFile": "Nový súbor",
|
||||
"newFileMessage": "Napíšte názov nového súboru.",
|
||||
"numberDirs": "Počet priečinkov{suffix}",
|
||||
"numberFiles": "Počet súborov{suffix}",
|
||||
"rename": "Premenovať",
|
||||
"renameMessage": "Zadajte nový názov pre",
|
||||
"replace": "Nahradiť",
|
||||
"replaceMessage": "Niektorý nahrávaný súbor je v konflikte názvov. Chcete nahradiť existujúci súbor?\n",
|
||||
"schedule": "Naplánovať",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Presuňte sem súbory",
|
||||
"completed": "Dokončené",
|
||||
"conflictsDetected": "Zistené konflikty",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Zadaný neplatný názov: nemôže byť kontajner \"/\" alebo \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Limit počtu stiahnutí",
|
||||
"maxBandwidth": "Maximálna šírka pásma sťahovania (kbps)",
|
||||
"shareTheme": "Zdieľať tému",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Zdieľanie adresy URL banneru",
|
||||
"shareFavicon": "Zdieľanie adresy URL favicon",
|
||||
"optionalPasswordHelp": "Ak je nastavené, používatelia musia zadať toto heslo, aby si mohli prezerať zdieľaný obsah.",
|
||||
"viewMode": "Režim zobrazenia"
|
||||
"viewMode": "Režim zobrazenia",
|
||||
"renameMessage": "Zadajte nový názov:",
|
||||
"renameMessageInvalid": "Súbor nemôže obsahovať \"/\" alebo \"\\\"",
|
||||
"source": "Zdroj{suffix}",
|
||||
"deleteMessageMultiple": "Ste si istí, že chcete odstrániť súbor(y) {count}?",
|
||||
"deleteMessageShare": "Ste si istí, že chcete odstrániť toto zdieľanie: {path} ?",
|
||||
"lastModified": "Posledná úprava{suffix}",
|
||||
"numberDirs": "Počet adresárov{suffix}",
|
||||
"numberFiles": "Počet súborov{suffix}",
|
||||
"renameMessageConflict": "Položka s názvom \"{filename}\" už existuje!",
|
||||
"uploadSettingsChunked": "Maximálne súbežné nahrávanie: {maxConcurrentUpload}, veľkosť chunk: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Maximálne súbežné nahrávanie: {maxConcurrentUpload}, chunked upload vypnuté"
|
||||
},
|
||||
"search": {
|
||||
"images": "Obrázky",
|
||||
"music": "Hudba",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Vyhľadáte stlačením Enter...",
|
||||
"search": "Hľadať...",
|
||||
"typeToSearch": "Vyhľadáte písaním...",
|
||||
"types": "Typy",
|
||||
"video": "Video",
|
||||
"path": "Cesta:",
|
||||
"smallerThan": "Menšie ako:",
|
||||
"largerThan": "Väčšie ako:",
|
||||
"helpText1": "Vyhľadávanie prebieha na základe každého zadaného znaku (minimálne 3 znaky pre hľadané výrazy).",
|
||||
"helpText2": "Index: Vyhľadávanie využíva index, ktorý sa automaticky aktualizuje v nastavenom intervale (predvolené: 5 minút). Vyhľadávanie, keď sa program práve spustil, môže viesť k neúplným výsledkom."
|
||||
"helpText2": "Index: Vyhľadávanie využíva index, ktorý sa automaticky aktualizuje v nastavenom intervale (predvolené: 5 minút). Vyhľadávanie, keď sa program práve spustil, môže viesť k neúplným výsledkom.",
|
||||
"noResults": "V indexovanom vyhľadávaní sa nenašli žiadne výsledky.",
|
||||
"onlyFolders": "Iba priečinky",
|
||||
"onlyFiles": "Iba súbory",
|
||||
"showOptions": "Zobraziť možnosti",
|
||||
"photos": "Fotografie",
|
||||
"audio": "Audio",
|
||||
"videos": "Videá",
|
||||
"documents": "Dokumenty",
|
||||
"archives": "Archív",
|
||||
"number": "Číslo",
|
||||
"searchContext": "Kontext vyhľadávania: {context}",
|
||||
"notEnoughCharacters": "Nedostatok znakov na vyhľadávanie (min. {minSearchLength})",
|
||||
"typeToSearch": "Začnite písať {minSearchLength} alebo viac znakov, aby ste začali vyhľadávať."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(nechajte prázdne, aby sa nezmenilo)",
|
||||
"branding": "Vlastný vzhľad",
|
||||
"brandingDirectoryPath": "Cesta k priečinku s vlastným vzhľadom",
|
||||
"brandingHelp": "Môžete si prispôsobiť ako bude vyzerá váš File Browser instance zmenou jeho názvu, výmenou loga a pridaním vlastný štýlov alebo vypnutím externých odkazov na GitHub.\nViac informácií o vlastnom vzhľade nájdete na {0}.",
|
||||
"changePassword": "Zmeniť heslo",
|
||||
"commandRunner": "Spúšťač príkazov",
|
||||
"commandRunnerHelp": "Sem môžete nastaviť príkazy, ktoré sa vykonajú pri určitých udalostiach. Musíte písať jeden na riadok. Premenné prostredia {0} a {1} sú k dispozícii, s tým že {0} relatívne k {1}. Viac informácií o tejto funkcionalite a dostupných premenných prostredia nájdete na {2}.",
|
||||
"commandsUpdated": "Príkazy upravené!",
|
||||
"createUserDir": "Automaticky vytvoriť domovský priečinok pri pridaní používateľa",
|
||||
"customStylesheet": "Vlastný Stylesheet",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Možnosti používateľa správcu",
|
||||
"shareDuration": "Platnosť končí na",
|
||||
"searchOptions": "Možnosti vyhľadávania",
|
||||
"downloads": "Na stiahnutie"
|
||||
"downloads": "Na stiahnutie",
|
||||
"systemAdmin": "Systém a administrátor",
|
||||
"configViewer": "Prehliadač konfigurácie",
|
||||
"configViewerShowFull": "Zobraziť úplnú konfiguráciu",
|
||||
"configViewerShowComments": "Zobraziť komentáre",
|
||||
"configViewerLoadConfig": "Konfigurácia načítania",
|
||||
"brandingHelp": "Vzhľad a pôsobenie inštancie Prieskumníka súborov môžete prispôsobiť zmenou názvu, výmenou loga, pridaním vlastných štýlov a dokonca aj zakázaním externých prepojení na GitHub.\nViac informácií o používaní vlastnej značky nájdete na stránke {0}.",
|
||||
"commandRunnerHelp": "Tu môžete nastaviť príkazy, ktoré sa vykonajú v pomenovaných udalostiach. Na každý riadok musíte napísať jeden. K dispozícii budú premenné prostredia {0} a {1}, pričom {0} bude relatívne k {1}. Viac informácií o tejto funkcii a dostupných premenných prostredia nájdete na stránke {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Pomoc",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Skrytie navigačných tlačidiel",
|
||||
"hideNavButtonsDescription": "Skryte navigačné tlačidlá na paneli navigácie v zdieľaní, aby ste vytvorili minimalistický vzhľad.",
|
||||
"viewModeDescription": "Predvolené rozloženie zdieľanej stránky.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Bola vám odoslaná akcia na zobrazenie alebo stiahnutie.",
|
||||
"disableShareCard": "Zakázanie zdieľanej karty",
|
||||
"disableShareCardDescription": "Zakážte kartu zdieľania na zdieľanej stránke v bočnom paneli alebo v hornej časti stránky na mobilnom zariadení.",
|
||||
"disableSidebar": "Zakázanie bočného panela",
|
||||
"disableSidebarDescription": "Zakážte bočný panel na zdieľanej stránke."
|
||||
"disableSidebarDescription": "Zakážte bočný panel na zdieľanej stránke.",
|
||||
"enforceDarkLightMode": "Vynútiť režim témy",
|
||||
"enforceDarkLightModeDescription": "Vynútenie konkrétneho režimu motívu (tmavý alebo svetlý) pre túto akciu, ktorý je nadradený používateľským nastaveniam.",
|
||||
"default": "Nevynucujte režim",
|
||||
"dark": "Dark",
|
||||
"light": "Svetlo",
|
||||
"enableOnlyOffice": "Povolenie prehliadača OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Povoľte zobrazovanie kancelárskych súborov pomocou OnlyOffice v tejto zdieľanej zložke.",
|
||||
"enableOnlyOfficeEditing": "Povolenie úprav OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Povoľte úpravu kancelárskych súborov pomocou aplikácie OnlyOffice v tejto zdieľanej zložke.",
|
||||
"titleDefault": "Zdieľané súbory - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Kľúče API:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Odmietnuté",
|
||||
"totalAllowed": "Povolené",
|
||||
"all": "Odmietnuť všetko",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Toto ukazuje, čo sa stane, keď sa používateľ pokúsi získať prístup k ceste, kde neexistujú žiadne špecifické pravidlá prístupu. \"Allow\" (Povoliť) znamená, že používatelia majú predvolený prístup, \"Deny\" (Odmietnuť) znamená, že používatelia sú predvolene blokovaní, pokiaľ to nie je výslovne povolené."
|
||||
"defaultBehaviorDescription": "Toto ukazuje, čo sa stane, keď sa používateľ pokúsi získať prístup k ceste, kde neexistujú žiadne špecifické pravidlá prístupu. \"Allow\" (Povoliť) znamená, že používatelia majú predvolený prístup, \"Deny\" (Odmietnuť) znamená, že používatelia sú predvolene blokovaní, pokiaľ to nie je výslovne povolené.",
|
||||
"defaultBehavior": "Predvolená akcia{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Nahrávanie a sťahovanie",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Predvolená téma",
|
||||
"customTheme": "Vyberte si tému",
|
||||
"showSelectMultiple": "Zobrazenie výberu viacerých v kontextovom menu na ploche",
|
||||
"showSelectMultipleDescription": "V predvolenom nastavení kontextová ponuka na ploche nemá možnosť \"vybrať viacero\" ako v mobilných zariadeniach. Táto možnosť sa v kontextovej ponuke zobrazuje vždy."
|
||||
"showSelectMultipleDescription": "V predvolenom nastavení kontextová ponuka na ploche nemá možnosť \"vybrať viacero\" ako v mobilných zariadeniach. Táto možnosť sa v kontextovej ponuke zobrazuje vždy.",
|
||||
"defaultMediaPlayer": "Používanie natívneho prehrávača médií prehliadača",
|
||||
"defaultMediaPlayerDescription": "Na prehrávanie video a zvukových súborov použite natívny prehrávač médií prehliadača, a nie mediálny prehrávač vue-plyr, ktorý je súčasťou aplikácie FileBrowser.",
|
||||
"debugOfficeEditor": "Povolenie režimu ladenia OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Zobrazenie vyskakovacieho okna ladenia s ďalšími informáciami v editore OnlyOffice.",
|
||||
"fileViewerOptions": "Možnosti prehliadača súborov",
|
||||
"themeAndLanguage": "Téma a jazyk"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Nepodarilo sa inicializovať editor. Prosím, načítajte ho znova.",
|
||||
"saveFailed": "Nepodarilo sa uložiť súbor. Skúste to prosím znova.",
|
||||
"saveAborted": "Uloženie prerušené. Názov súboru sa nezhoduje s aktívnym súborom.",
|
||||
"saveDisabled": "Ukladanie zdieľaných súborov nie je podporované.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Operácia uloženia sa prerušila. Aktívny súbor aplikácie ({activeFile}) sa nezhoduje so súborom, ktorý sa pokúšate uložiť ({tryingToSave}).",
|
||||
"syncFailed": "sa nepodarilo synchronizovať stav s trasou pre \"{filename}\" po 5 pokusoch. Prerušenie nastavenia editora, aby sa zabránilo poškodeniu údajov."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Povolená slučka",
|
||||
"LoopDisabled": "Vypnutá slučka"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Klart avslutat",
|
||||
"showMore": "Visa mer",
|
||||
"showLess": "Visa mindre",
|
||||
"openParentFolder": "Öppna överordnad mapp"
|
||||
"openParentFolder": "Öppna överordnad mapp",
|
||||
"selectedCount": "Antal objekt som valts för att utföra en åtgärd"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Ladda ner fil",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "välj fil eller mapp",
|
||||
"ctrl": {
|
||||
"click": "välj flera filer eller mappar",
|
||||
"f": "öppnar sök",
|
||||
"s": "Spara en fil eller ladda ner den katalog där du är"
|
||||
"d": "ladda ner valda artiklar"
|
||||
},
|
||||
"del": "ta bort valda objekt",
|
||||
"doubleClick": "öppna en fil eller mapp",
|
||||
"esc": "Rensa markeringen och/eller stänga prompten",
|
||||
"f1": "denna information",
|
||||
"f2": "ändra namnet på filen",
|
||||
"help": "Hjälp"
|
||||
"help": "Hjälp",
|
||||
"f1": "visa hjälp prompt",
|
||||
"description": "Du kan se de grundläggande navigeringsalternativen nedan. För ytterligare information, vänligen besök",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "Kopiera",
|
||||
"copyMessage": "Välj var dina filer skall sparas:",
|
||||
"deleteMessageMultiple": "Är du säker på att du vill radera {count} filer(na)?",
|
||||
"deleteMessageSingle": "Är du säker på att du vill radera denna fil/mapp",
|
||||
"deleteTitle": "Ta bort filer",
|
||||
"displayName": "Visningsnamn:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "Välj format på det du vill lada ner.",
|
||||
"error": "Något gick fel",
|
||||
"fileInfo": "Fil information",
|
||||
"lastModified": "Senast ändrad{suffix}",
|
||||
"move": "Flytta",
|
||||
"moveMessage": "Välj ny plats för din fil (er)/mapp (ar):",
|
||||
"newArchetype": "Skapa ett nytt inlägg baserat på en arketyp. Din fil kommer att skapas på innehållsmapp.",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "Ange namn på din nya mapp.",
|
||||
"newFile": "Ny fil",
|
||||
"newFileMessage": "Ange namn på din nya fil.",
|
||||
"numberDirs": "Antal kataloger{suffix}",
|
||||
"numberFiles": "Antal filer{suffix}",
|
||||
"rename": "Ändra namn",
|
||||
"renameMessage": "Infoga ett nytt namn för",
|
||||
"replace": "Ersätt",
|
||||
"replaceMessage": "En av filerna som du försöker överföra är i konflikt på grund av dess namn. Vill du ersätta den befintliga?\n",
|
||||
"schedule": "Schema",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "Visa",
|
||||
"size": "Storlek",
|
||||
"upload": "Ladda upp",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"deleteUserMessage": "Är du säker på att du vill ta bort den här användaren?",
|
||||
"selected": "utvalda",
|
||||
"filesSelected": "valda filer",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Dra och släpp filer här",
|
||||
"completed": "Färdigställd",
|
||||
"conflictsDetected": "Konflikter upptäckta",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Ogiltigt namn anges: kan inte innehålla \"/\" eller \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Begränsning av antal nedladdningar",
|
||||
"maxBandwidth": "Max bandbredd för nedladdning (kbps)",
|
||||
"shareTheme": "Dela tema",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Dela bannerns URL",
|
||||
"shareFavicon": "Dela URL för favicon",
|
||||
"optionalPasswordHelp": "Om det är inställt måste användarna ange lösenordet för att visa det delade innehållet.",
|
||||
"viewMode": "Visa läge"
|
||||
"viewMode": "Visa läge",
|
||||
"renameMessage": "Ange ett nytt namn:",
|
||||
"renameMessageInvalid": "En fil kan inte innehålla \"/\" eller \"\\\"",
|
||||
"source": "Källa{suffix}",
|
||||
"deleteMessageMultiple": "Är du säker att du vill ta bort {count} fil(er)?",
|
||||
"deleteMessageShare": "Är du säker på att du vill ta bort den här aktien: {path} ?",
|
||||
"lastModified": "Senast ändrad{suffix}",
|
||||
"numberDirs": "Antal kataloger{suffix}",
|
||||
"numberFiles": "Antal filer{suffix}",
|
||||
"renameMessageConflict": "Ett objekt med namnet \"{filename}\" finns redan!",
|
||||
"uploadSettingsChunked": "Max samtidiga uppladdningar: {maxConcurrentUpload}, chunkstorlek: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max samtidiga uppladdningar: {maxConcurrentUpload}, chunked uppladdning inaktiverad"
|
||||
},
|
||||
"search": {
|
||||
"images": "Bilder",
|
||||
"music": "Musik",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Tryck på enter för att söka...",
|
||||
"search": "Sök...",
|
||||
"typeToSearch": "Skriv för att söka....",
|
||||
"types": "Typ",
|
||||
"video": "Video",
|
||||
"path": "Väg:",
|
||||
"smallerThan": "Mindre än:",
|
||||
"largerThan": "Större än:",
|
||||
"helpText1": "Sökningen sker på varje tecken du skriver (minst 3 tecken för sökbegrepp).",
|
||||
"helpText2": "Indexet: Sökningen använder indexet som automatiskt uppdateras med det inställda intervallet (standard: 5 minuter). Om du söker när programmet precis har startat kan det leda till ofullständiga resultat."
|
||||
"helpText2": "Indexet: Sökningen använder indexet som automatiskt uppdateras med det inställda intervallet (standard: 5 minuter). Om du söker när programmet precis har startat kan det leda till ofullständiga resultat.",
|
||||
"noResults": "Inga resultat hittades i indexerad sökning.",
|
||||
"onlyFolders": "Endast mappar",
|
||||
"onlyFiles": "Endast filer",
|
||||
"showOptions": "Visa alternativ",
|
||||
"photos": "Bilder",
|
||||
"audio": "Ljud",
|
||||
"videos": "Videor",
|
||||
"documents": "Dokument",
|
||||
"archives": "Arkiv",
|
||||
"number": "Antal",
|
||||
"searchContext": "Sök kontext: {context}",
|
||||
"notEnoughCharacters": "Inte tillräckligt med tecken för att söka (min {minSearchLength})",
|
||||
"typeToSearch": "Börja skriva {minSearchLength} eller fler tecken för att påbörja sökningen."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Admin",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(lämna blankt för att undvika ändringar)",
|
||||
"branding": "Varumärke",
|
||||
"brandingDirectoryPath": "Sökväg till varumärkes katalog",
|
||||
"brandingHelp": "Du kan skräddarsyr hur din fil hanterar instansen ser ut och känns genom att ändra dess namn, ersätter logo typen, lägga till egna stilar och även inaktivera externa länkar till GitHub.\nMer information om anpassad varumärkes profilering finns i {0}.",
|
||||
"changePassword": "Ändra lösenord",
|
||||
"commandRunner": "Kommando körare",
|
||||
"commandRunnerHelp": "Här kan du ange kommandon som körs i de namngivna händelserna. Du måste skriva en per rad. Miljövariablerna {0} och {1} kommer att vara tillgängliga, och vara {0} i förhållande till {1}. För mer information om den här funktionen och de tillgängliga miljövariablerna, vänligen läs {2}.",
|
||||
"commandsUpdated": "Kommandon uppdaterade!",
|
||||
"createUserDir": "Auto skapa användarens hemkatalog när du lägger till nya användare",
|
||||
"customStylesheet": "Anpassad formatmall",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Alternativ för administratörsanvändare",
|
||||
"shareDuration": "Upphör att gälla",
|
||||
"searchOptions": "Sökalternativ",
|
||||
"downloads": "Nedladdningar"
|
||||
"downloads": "Nedladdningar",
|
||||
"systemAdmin": "System & Administration",
|
||||
"configViewer": "Konfig-visare",
|
||||
"configViewerShowFull": "Visa fullständig konfiguration",
|
||||
"configViewerShowComments": "Visa kommentarer",
|
||||
"configViewerLoadConfig": "Ladda konfig",
|
||||
"brandingHelp": "Du kan anpassa hur din File Browser-instans ser ut och känns genom att ändra dess namn, byta ut logotypen, lägga till anpassade stilar och till och med inaktivera externa länkar till GitHub.\nFör mer information om anpassad branding, se {0}.",
|
||||
"commandRunnerHelp": "Här kan du ställa in kommandon som utförs i de namngivna händelserna. Du måste skriva ett per rad. Miljövariablerna {0} och {1} kommer att vara tillgängliga, {0} i förhållande till {1}. För mer information om denna funktion och de tillgängliga miljövariablerna, läs {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Hjälp",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Dölj navigeringsknappar",
|
||||
"hideNavButtonsDescription": "Dölj navigeringsknapparna i navigeringsfältet i aktien för att skapa ett minimalistiskt utseende.",
|
||||
"viewModeDescription": "Standardlayout för den delade sidan.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "En aktie har skickats till dig för visning eller nedladdning.",
|
||||
"disableShareCard": "Inaktivera aktiekort",
|
||||
"disableShareCardDescription": "Inaktivera delningskortet på den delade sidan i sidofältet eller högst upp på sidan på mobilen.",
|
||||
"disableSidebar": "Inaktivera sidofält",
|
||||
"disableSidebarDescription": "Inaktivera sidofältet på den delade sidan."
|
||||
"disableSidebarDescription": "Inaktivera sidofältet på den delade sidan.",
|
||||
"enforceDarkLightMode": "Genomför temaläge",
|
||||
"enforceDarkLightModeDescription": "Tvinga fram ett specifikt temaläge (mörkt eller ljust) för den här aktien, utan hänsyn till användarens preferenser.",
|
||||
"default": "Inte tvinga fram ett läge",
|
||||
"dark": "Mörk",
|
||||
"light": "Ljus",
|
||||
"enableOnlyOffice": "Aktivera OnlyOffice-visningsprogram",
|
||||
"enableOnlyOfficeDescription": "Tillåt visning av kontorsfiler med OnlyOffice i denna delning.",
|
||||
"enableOnlyOfficeEditing": "Aktivera redigering av OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Tillåt redigering av kontorsfiler med hjälp av OnlyOffice i denna andel.",
|
||||
"titleDefault": "Delade filer {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API-nycklar",
|
||||
@@ -478,7 +505,13 @@
|
||||
"defaultThemeDescription": "default - Standardtema",
|
||||
"customTheme": "Välj ditt tema",
|
||||
"showSelectMultiple": "Visa markera flera i snabbmenyn på skrivbordet",
|
||||
"showSelectMultipleDescription": "Som standard har snabbmenyn på skrivbordet inte alternativet \"välj flera\" som på mobilen. Det här alternativet visas alltid i snabbmenyn."
|
||||
"showSelectMultipleDescription": "Som standard har snabbmenyn på skrivbordet inte alternativet \"välj flera\" som på mobilen. Det här alternativet visas alltid i snabbmenyn.",
|
||||
"defaultMediaPlayer": "Använd den inbyggda mediaspelaren i din webbläsare",
|
||||
"defaultMediaPlayerDescription": "Använd webbläsarens inbyggda mediaspelare för video- och ljudfiler i stället för FileBrowsers medföljande \"vue-plyr\"-mediaspelare.",
|
||||
"debugOfficeEditor": "Aktivera OnlyOffice felsökningsläge",
|
||||
"debugOfficeEditorDescription": "Visa en debug-popup med ytterligare information i OnlyOffice-redigeraren.",
|
||||
"fileViewerOptions": "Alternativ för filvisare",
|
||||
"themeAndLanguage": "Tema & språk"
|
||||
},
|
||||
"general": {
|
||||
"name": "Namn",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "Namn",
|
||||
"accessManagement": "Hantering av åtkomst",
|
||||
"all": "Avvisa alla",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Detta visar vad som händer när en användare försöker komma åt en sökväg där det inte finns några specifika åtkomstregler. \"Allow\" innebär att användare kan komma åt som standard, \"Deny\" innebär att användare blockeras som standard om de inte uttryckligen tillåts."
|
||||
"defaultBehaviorDescription": "Detta visar vad som händer när en användare försöker komma åt en sökväg där det inte finns några specifika åtkomstregler. \"Allow\" innebär att användare kan komma åt som standard, \"Deny\" innebär att användare blockeras som standard om de inte uttryckligen tillåts.",
|
||||
"defaultBehavior": "Standardåtgärd{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Redigeraren kunde inte initieras. Vänligen ladda om.",
|
||||
"saveFailed": "Misslyckades med att spara filen. Vänligen försök igen.",
|
||||
"saveAborted": "Spara avbröts. Filnamnet stämmer inte överens med den aktiva filen.",
|
||||
"saveDisabled": "Sparande av delade filer stöds inte.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Spara åtgärden avbröts. Programmets aktiva fil ({activeFile}) stämmer inte överens med den fil du försöker spara ({tryingToSave}).",
|
||||
"syncFailed": "lyckades inte synkronisera tillstånd med rutten för \"{filename}\" efter 5 försök. Avbryter redigeringsinställningen för att förhindra datakorruption."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Slinga aktiverad",
|
||||
"LoopDisabled": "Slinga avaktiverad"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Temizleme tamamlandı",
|
||||
"showMore": "Daha fazla göster",
|
||||
"showLess": "Daha az göster",
|
||||
"openParentFolder": "Üst klasörü açın"
|
||||
"openParentFolder": "Üst klasörü açın",
|
||||
"selectedCount": "Bir eylemi gerçekleştirmek için seçilen öğe sayısı"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Dosyayı indir",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "dosya veya klasör seçin",
|
||||
"ctrl": {
|
||||
"click": "çoklu dosya ve klasör seçin",
|
||||
"f": "Aramayı aç",
|
||||
"s": "bir dosyayı kaydedin veya bulunduğunuz dizini indirin"
|
||||
"d": "seçili öğeleri indirin"
|
||||
},
|
||||
"del": "seçilileri sil",
|
||||
"doubleClick": "dosya veya dizini açın",
|
||||
"esc": "seçimi temizle veya kapatın",
|
||||
"f1": "bu bilgi",
|
||||
"f2": "dosyayı yeniden adlandır",
|
||||
"help": "Yardım"
|
||||
"help": "Yardım",
|
||||
"f1": "yardım istemini göster",
|
||||
"description": "Temel navigasyon seçeneklerini aşağıda görüntüleyebilirsiniz. Ek bilgi için lütfen şu adresi ziyaret edin",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Kopyala",
|
||||
"copyMessage": "Dosyalarınızı kopyalayacağınız yeri seçin:",
|
||||
"deleteMessageMultiple": "{count} dosyayı/dosyaları silmek istediğinizden emin misiniz?",
|
||||
"deleteMessageSingle": "Bu dosyayı/klasörü silmek istediğinizden emin misiniz?",
|
||||
"deleteMessageShare": "Bu paylaşımı({path}) silmek istediğinizden emin misiniz?",
|
||||
"deleteTitle": "Dosyaları sil",
|
||||
"displayName": "Görünen Ad:",
|
||||
"download": "Dosyaları indirŞ",
|
||||
"downloadMessage": "İndirmek istediğiniz formatı seçin.",
|
||||
"error": "Bir şeyler yanlış gitti",
|
||||
"fileInfo": "Dosya bilgisi",
|
||||
"lastModified": "Son güncellenme{suffix}",
|
||||
"move": "Taşı",
|
||||
"moveMessage": "Dosya(lar)ınız/klasör(ler)iniz için yeni ana dizin seçin:",
|
||||
"newArchetype": "Bir prototip temelinde yeni bir gönderi oluşturun. Dosyanız içerik klasöründe oluşturulacaktır.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Yeni dizinin adını yazın.",
|
||||
"newFile": "Yeni dosya",
|
||||
"newFileMessage": "Yeni dosyanın adını yazın.",
|
||||
"numberDirs": "Dizin sayısı{suffix}",
|
||||
"numberFiles": "Dosya sayısı{suffix}",
|
||||
"rename": "Yeniden adlandır",
|
||||
"renameMessage": "için yeni bir ad girin",
|
||||
"replace": "Değiştir",
|
||||
"replaceMessage": "Yüklemeye çalıştığınız dosyalardan biri, adı nedeniyle çakışıyor. Mevcut olanı değiştirmek istiyor musunuz?\n",
|
||||
"schedule": "Planla",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Dosyaları buraya sürükleyip bırakın",
|
||||
"completed": "Tamamlandı",
|
||||
"conflictsDetected": "Tespit edilen çatışmalar",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Geçersiz ad sağlandı: \"/\" veya \"\\\" kapsayıcı olamaz",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "İndirme sayısı sınırı",
|
||||
"maxBandwidth": "Maksimum indirme bant genişliği (kbps)",
|
||||
"shareTheme": "Temayı paylaş",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Banner URL'sini paylaş",
|
||||
"shareFavicon": "Favicon URL'sini paylaş",
|
||||
"optionalPasswordHelp": "Ayarlanırsa, kullanıcılar paylaşılan içeriği görüntülemek için bu parolayı girmelidir.",
|
||||
"viewMode": "Görünüm modu"
|
||||
"viewMode": "Görünüm modu",
|
||||
"renameMessage": "Yeni bir ad girin:",
|
||||
"renameMessageInvalid": "Bir dosya \"/\" veya \"\\\" içeremez",
|
||||
"source": "Kaynak{suffix}",
|
||||
"deleteMessageMultiple": "{count} dosya(lar)ını silmek istediğinizden emin misiniz?",
|
||||
"deleteMessageShare": "Bu paylaşımı silmek istediğinizden emin misiniz? {path} ?",
|
||||
"lastModified": "Son Değişiklik{suffix}",
|
||||
"numberDirs": "Dizin sayısı{suffix}",
|
||||
"numberFiles": "Dosya sayısı{suffix}",
|
||||
"renameMessageConflict": "\"{filename}\" adında bir öğe zaten var!",
|
||||
"uploadSettingsChunked": "Maksimum eşzamanlı yükleme: {maxConcurrentUpload}, yığın boyutu: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Maksimum eşzamanlı yükleme: {maxConcurrentUpload}, yığınlanmış yükleme devre dışı"
|
||||
},
|
||||
"search": {
|
||||
"images": "Görseller",
|
||||
"music": "Müzik",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Aramak için enter'a basın...",
|
||||
"search": "Ara...",
|
||||
"typeToSearch": "Aramak için yazın...",
|
||||
"types": "Türler",
|
||||
"video": "Video",
|
||||
"path": "Yol:",
|
||||
"smallerThan": "Daha küçük:",
|
||||
"largerThan": "Daha büyük:",
|
||||
"helpText1": "Arama, yazdığınız her karakterde gerçekleşir (arama terimleri için minimum 3 karakter).",
|
||||
"helpText2": "Dizin: Arama, yapılandırılan aralıkta (varsayılan: 5 dakika) otomatik olarak güncellenen dizini kullanır. Program yeni başladığında arama yapmak eksik sonuçlara neden olabilir."
|
||||
"helpText2": "Dizin: Arama, yapılandırılan aralıkta (varsayılan: 5 dakika) otomatik olarak güncellenen dizini kullanır. Program yeni başladığında arama yapmak eksik sonuçlara neden olabilir.",
|
||||
"noResults": "Dizinlenmiş aramada sonuç bulunamadı.",
|
||||
"onlyFolders": "Yalnızca Klasörler",
|
||||
"onlyFiles": "Sadece Dosyalar",
|
||||
"showOptions": "Seçenekleri Göster",
|
||||
"photos": "Fotoğraflar",
|
||||
"audio": "Ses",
|
||||
"videos": "Videolar",
|
||||
"documents": "Belgeler",
|
||||
"archives": "Arşivler",
|
||||
"number": "Sayı",
|
||||
"searchContext": "Arama Bağlamı: {context}",
|
||||
"notEnoughCharacters": "Arama yapmak için yeterli karakter yok (min {minSearchLength})",
|
||||
"typeToSearch": "Aramaya başlamak için {minSearchLength} veya daha fazla karakter yazmaya başlayın."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Yönetim",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(değişiklikleri önlemek için boş bırakın)",
|
||||
"branding": "Marka",
|
||||
"brandingDirectoryPath": "Marka dizin yolu",
|
||||
"brandingHelp": "Adını değiştirerek, logoyu değiştirerek, özel stiller ekleyerek ve hatta GitHub'a harici bağlantıları devre dışı bırakarak FileBrowser örneğinizin görünüşünü ve hissini özelleştirebilirsiniz.\nÖzel marka bilinci oluşturma hakkında daha fazla bilgi için lütfen {0} sayfasına göz atın.",
|
||||
"changePassword": "Şifre Değiştir",
|
||||
"commandRunner": "Komut satırı",
|
||||
"commandRunnerHelp": "Burada, adlandırılmış olaylarda yürütülen komutları ayarlayabilirsiniz. Her satıra bir tane yazmalısınız. {0} ve {1} ortam değişkenleri, {1}'ye göre {0} olacak şekilde kullanılabilir olacaktır. Bu özellik ve mevcut ortam değişkenleri hakkında daha fazla bilgi için lütfen {2}'yi okuyun.",
|
||||
"commandsUpdated": "Komutlar güncellendi!",
|
||||
"createUserDir": "Kullanıcı eklerken, kullanıcı ana dizinini otomatik oluştur",
|
||||
"customStylesheet": "Özel CSS",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Yönetici kullanıcı seçenekleri",
|
||||
"shareDuration": "Sona erdi",
|
||||
"searchOptions": "Arama seçenekleri",
|
||||
"downloads": "İndirmeler"
|
||||
"downloads": "İndirmeler",
|
||||
"systemAdmin": "Sistem ve Yönetici",
|
||||
"configViewer": "Konfigürasyon Görüntüleyici",
|
||||
"configViewerShowFull": "Tam yapılandırmayı göster",
|
||||
"configViewerShowComments": "Yorumları göster",
|
||||
"configViewerLoadConfig": "Yük Konfigürasyonu",
|
||||
"brandingHelp": "Adını değiştirerek, logoyu değiştirerek, özel stiller ekleyerek ve hatta GitHub'a harici bağlantıları devre dışı bırakarak Dosya Tarayıcı örneğinizin nasıl göründüğünü ve hissettirdiğini özelleştirebilirsiniz.\nÖzel markalama hakkında daha fazla bilgi için lütfen {0} adresine göz atın.",
|
||||
"commandRunnerHelp": "Burada, adlandırılmış olaylarda yürütülecek komutları ayarlayabilirsiniz. Her satıra bir tane yazmalısınız. {0} ve {1} ortam değişkenleri kullanılabilir olacaktır, {0} ise {1} ile ilişkilidir. Bu özellik ve mevcut ortam değişkenleri hakkında daha fazla bilgi için lütfen {2} adresini okuyun."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Yardım",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Gezinme düğmelerini gizle",
|
||||
"hideNavButtonsDescription": "Minimalist bir görünüm oluşturmak için paylaşımdaki gezinme çubuğundaki gezinme düğmelerini gizleyin.",
|
||||
"viewModeDescription": "Paylaşılan sayfa için varsayılan düzen.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Görüntülemeniz veya indirmeniz için size bir paylaşım gönderildi.",
|
||||
"disableShareCard": "Paylaşım kartını devre dışı bırak",
|
||||
"disableShareCardDescription": "Kenar çubuğunda paylaşılan sayfadaki veya mobil cihazlarda sayfanın üst kısmındaki paylaşım kartını devre dışı bırakın.",
|
||||
"disableSidebar": "Kenar çubuğunu devre dışı bırak",
|
||||
"disableSidebarDescription": "Paylaşılan sayfadaki kenar çubuğunu devre dışı bırakın."
|
||||
"disableSidebarDescription": "Paylaşılan sayfadaki kenar çubuğunu devre dışı bırakın.",
|
||||
"enforceDarkLightMode": "Tema modunu zorla",
|
||||
"enforceDarkLightModeDescription": "Kullanıcı tercihlerini geçersiz kılarak bu paylaşım için belirli bir tema modunu (koyu veya açık) zorlayın.",
|
||||
"default": "Bir modu zorlamayın",
|
||||
"dark": "Karanlık",
|
||||
"light": "Işık",
|
||||
"enableOnlyOffice": "OnlyOffice görüntüleyiciyi etkinleştirin",
|
||||
"enableOnlyOfficeDescription": "Bu paylaşımda OnlyOffice kullanarak ofis dosyalarının görüntülenmesine izin verin.",
|
||||
"enableOnlyOfficeEditing": "OnlyOffice düzenlemesini etkinleştirin",
|
||||
"enableOnlyOfficeEditingDescription": "Bu paylaşımda OnlyOffice kullanarak ofis dosyalarının düzenlenmesine izin verin.",
|
||||
"titleDefault": "Paylaşılan dosyalar - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API Anahtarları:",
|
||||
@@ -447,8 +474,8 @@
|
||||
"totalDenied": "Reddedildi",
|
||||
"totalAllowed": "İzin verildi",
|
||||
"all": "Tümünü Reddet",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Bu, bir kullanıcı belirli erişim kurallarının bulunmadığı bir yola erişmeye çalıştığında ne olacağını gösterir. 'Allow' kullanıcıların varsayılan olarak erişebileceği anlamına gelir, 'Deny' ise açıkça izin verilmediği sürece kullanıcıların varsayılan olarak engellendiği anlamına gelir."
|
||||
"defaultBehaviorDescription": "Bu, bir kullanıcı belirli erişim kurallarının bulunmadığı bir yola erişmeye çalıştığında ne olacağını gösterir. 'Allow' kullanıcıların varsayılan olarak erişebileceği anlamına gelir, 'Deny' ise açıkça izin verilmediği sürece kullanıcıların varsayılan olarak engellendiği anlamına gelir.",
|
||||
"defaultBehavior": "Varsayılan eylem{suffix}"
|
||||
},
|
||||
"fileLoading": {
|
||||
"title": "Yüklemeler & İndirmeler",
|
||||
@@ -514,14 +541,24 @@
|
||||
"defaultThemeDescription": "default - Varsayılan tema",
|
||||
"customTheme": "Temanızı seçin",
|
||||
"showSelectMultiple": "Masaüstündeki bağlam menüsünde çoklu seçmeyi göster",
|
||||
"showSelectMultipleDescription": "Varsayılan olarak, masaüstü içerik menüsünde mobil cihazlardaki gibi \"çoklu seç\" seçeneği yoktur. Bu seçenek her zaman içerik menüsünde gösterilir."
|
||||
"showSelectMultipleDescription": "Varsayılan olarak, masaüstü içerik menüsünde mobil cihazlardaki gibi \"çoklu seç\" seçeneği yoktur. Bu seçenek her zaman içerik menüsünde gösterilir.",
|
||||
"defaultMediaPlayer": "Tarayıcınızın yerel medya oynatıcısını kullanın",
|
||||
"defaultMediaPlayerDescription": "Video ve ses dosyaları için FileBrowser'ın dahil ettiği 'vue-plyr' medya oynatıcısı yerine tarayıcınızın yerel medya oynatıcısını kullanın.",
|
||||
"debugOfficeEditor": "OnlyOffice Hata Ayıklama Modunu Etkinleştir",
|
||||
"debugOfficeEditorDescription": "OnlyOffice düzenleyicisinde ek bilgiler içeren bir hata ayıklama açılır penceresi gösterin.",
|
||||
"fileViewerOptions": "Dosya Görüntüleyici Seçenekleri",
|
||||
"themeAndLanguage": "Tema ve Dil"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Düzenleyici başlatılamadı. Lütfen yeniden yükleyin.",
|
||||
"saveFailed": "Dosya kaydedilemedi. Lütfen tekrar deneyin.",
|
||||
"saveAborted": "Kaydetme işlemi iptal edildi. Dosya adı etkin dosyayla eşleşmiyor.",
|
||||
"saveDisabled": "Paylaşılan dosyaların kaydedilmesi desteklenmez.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Kaydetme işlemi iptal edildi. Uygulamanın etkin dosyası ({activeFile}) kaydetmeye çalıştığınız dosyayla ({tryingToSave}) eşleşmiyor.",
|
||||
"syncFailed": "\"{filename}\" için 5 denemeden sonra rota ile durum senkronize edilemedi. Veri bozulmasını önlemek için düzenleyici kurulumu iptal ediliyor."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Döngü Etkin",
|
||||
"LoopDisabled": "Döngü Devre Dışı"
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "Очищення завершено",
|
||||
"showMore": "Показати більше",
|
||||
"showLess": "Показати менше",
|
||||
"openParentFolder": "Відкрийте батьківську папку"
|
||||
"openParentFolder": "Відкрийте батьківську папку",
|
||||
"selectedCount": "Кількість елементів, вибраних для виконання дії"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Завантажити файл",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "вибрати файл чи каталог",
|
||||
"ctrl": {
|
||||
"click": "вибрати кілька файлів чи каталогів",
|
||||
"f": "відкрити пошук",
|
||||
"s": "скачати файл або поточний каталог"
|
||||
"d": "завантажити вибрані елементи"
|
||||
},
|
||||
"del": "видалити вибрані елементи",
|
||||
"doubleClick": "відкрити файл чи каталог",
|
||||
"esc": "очистити виділення та/або закрити вікно",
|
||||
"f1": "допомога",
|
||||
"f2": "перейменувати файл",
|
||||
"help": "Допомога"
|
||||
"help": "Допомога",
|
||||
"f1": "показати запит на допомогу",
|
||||
"description": "Нижче ви можете переглянути основні параметри навігації. Для отримання додаткової інформації, будь ласка, відвідайте",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "Копіювати",
|
||||
"copyMessage": "Копіювати в:",
|
||||
"deleteMessageMultiple": "Видалити ці файли ({count})?",
|
||||
"deleteMessageSingle": "Видалити цей файл/каталог?",
|
||||
"deleteMessageShare": "Видалити цей спільний файл/каталог ({path})?",
|
||||
"deleteTitle": "Видалити файлы",
|
||||
"displayName": "Відображене ім'я:",
|
||||
"download": "Завантажити файлы",
|
||||
"downloadMessage": "Виберіть формат, в якому хочете завантажити.",
|
||||
"error": "Помилка",
|
||||
"fileInfo": "Інформація про файл",
|
||||
"lastModified": "Останній раз змінено{suffix}",
|
||||
"move": "Перемістити",
|
||||
"moveMessage": "Перемістити в:",
|
||||
"newArchetype": "Створіть новий запис на основі архетипу. Файл буде створено у каталозі.",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "Ім'я нового каталогу.",
|
||||
"newFile": "Новий файл",
|
||||
"newFileMessage": "Ім'я нового файлу.",
|
||||
"numberDirs": "Кількість каталогів{suffix}",
|
||||
"numberFiles": "Кількість файлів{suffix}",
|
||||
"rename": "Перейменувати",
|
||||
"renameMessage": "Нове ім'я",
|
||||
"replace": "Замінити",
|
||||
"replaceMessage": "Ім'я одного з файлів, що завантажуються, збігається з вже існуючим файлом. Ви бажаєте замінити існуючий?\n",
|
||||
"schedule": "Планування",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "Перетягніть файли сюди",
|
||||
"completed": "Завершено",
|
||||
"conflictsDetected": "Виявлені конфлікти",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "Вказано невірне ім'я: не можна використовувати символи \"/\" або \"\\\"",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "Обмеження на кількість завантажень",
|
||||
"maxBandwidth": "Максимальна швидкість завантаження (кбіт/с)",
|
||||
"shareTheme": "Поділіться темою",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "Поділіться URL банера",
|
||||
"shareFavicon": "Поділитися URL-адресою іконки",
|
||||
"optionalPasswordHelp": "Якщо його встановлено, користувачі повинні ввести цей пароль, щоб переглянути спільний вміст.",
|
||||
"viewMode": "Режим перегляду"
|
||||
"viewMode": "Режим перегляду",
|
||||
"renameMessage": "Введіть нове ім'я:",
|
||||
"renameMessageInvalid": "Файл не може містити символи \"/\" або \"\\\"",
|
||||
"source": "Джерело{suffix}",
|
||||
"deleteMessageMultiple": "Ви дійсно хочете видалити файл(и) {count}?",
|
||||
"deleteMessageShare": "Ви впевнені, що хочете видалити цей ресурс? {path} ?",
|
||||
"lastModified": "Останній раз змінено{suffix}",
|
||||
"numberDirs": "Кількість каталогів{suffix}",
|
||||
"numberFiles": "Кількість файлів{suffix}",
|
||||
"renameMessageConflict": "Елемент з назвою \"{filename}\" вже існує!",
|
||||
"uploadSettingsChunked": "Максимальна кількість одночасних завантажень: {maxConcurrentUpload}, розмір фрагмента: {uploadChunkSizeMb} МБ",
|
||||
"uploadSettingsNoChunk": "Максимальне одночасне завантаження: {maxConcurrentUpload}, chunked upload вимкнено"
|
||||
},
|
||||
"search": {
|
||||
"images": "Зображення",
|
||||
"music": "Музика",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Натисніть ENTER для пошуку",
|
||||
"search": "Пошук...",
|
||||
"typeToSearch": "Введіть ім'я файлу...",
|
||||
"types": "Типи",
|
||||
"video": "Відео",
|
||||
"path": "Шлях:",
|
||||
"smallerThan": "Менше, ніж:",
|
||||
"largerThan": "Більше, ніж:",
|
||||
"helpText1": "Пошук відбувається за кожним введеним символом (мінімум 3 символи для пошукових термінів).",
|
||||
"helpText2": "Індекс: Пошук використовує індекс, який автоматично оновлюється з заданим інтервалом (за замовчуванням: 5 хвилин). Пошук, коли програма щойно запущена, може призвести до неповних результатів."
|
||||
"helpText2": "Індекс: Пошук використовує індекс, який автоматично оновлюється з заданим інтервалом (за замовчуванням: 5 хвилин). Пошук, коли програма щойно запущена, може призвести до неповних результатів.",
|
||||
"noResults": "В індексованому пошуку результатів не знайдено.",
|
||||
"onlyFolders": "Тільки папки",
|
||||
"onlyFiles": "Тільки файли",
|
||||
"showOptions": "Показати параметри",
|
||||
"photos": "Фотографії",
|
||||
"audio": "Аудіо",
|
||||
"videos": "Відео",
|
||||
"documents": "Документи",
|
||||
"archives": "Архіви",
|
||||
"number": "Номер",
|
||||
"searchContext": "Пошуковий контекст: {context}",
|
||||
"notEnoughCharacters": "Недостатньо символів для пошуку (мінімум {minSearchLength})",
|
||||
"typeToSearch": "Почніть вводити {minSearchLength} або більше символів, щоб почати пошук."
|
||||
},
|
||||
"settings": {
|
||||
"admin": "Адмін",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(залишіть поле порожнім, щоб уникнути змін)",
|
||||
"branding": "Брендинг",
|
||||
"brandingDirectoryPath": "Шлях до каталогу брендів",
|
||||
"brandingHelp": "Ви можете налаштувати зовнішній вигляд файлового браузера, змінивши його ім'я, замінивши логотип, додавши власні стилі та навіть відключивши зовнішні посилання на GitHub.\nДодаткову інформацію про персоналізований брендинг можна знайти на сторінці {0}.",
|
||||
"changePassword": "Зміна пароля",
|
||||
"commandRunner": "Запуск команд",
|
||||
"commandRunnerHelp": "Тут ви можете встановити команди, які будуть виконуватися у зазначених подіях. Ви повинні вказати по одній команді в кожному рядку. Змінні середовища {0} та {1} будуть доступні, будучи {0} щодо {1}. Додаткові відомості про цю функцію та доступні змінні середовища див. у {2}.",
|
||||
"commandsUpdated": "Команди оновлені!",
|
||||
"createUserDir": "Автоматичне створення домашнього каталогу користувача при додаванні нового користувача",
|
||||
"customStylesheet": "Свій стиль",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "Параметри користувача адміністрування",
|
||||
"shareDuration": "Закінчується",
|
||||
"searchOptions": "Параметри пошуку",
|
||||
"downloads": "Завантаження"
|
||||
"downloads": "Завантаження",
|
||||
"systemAdmin": "Система та адміністрування",
|
||||
"configViewer": "Переглядач конфігурацій",
|
||||
"configViewerShowFull": "Показати повну конфігурацію",
|
||||
"configViewerShowComments": "Показати коментарі",
|
||||
"configViewerLoadConfig": "Завантажити конфігурацію",
|
||||
"brandingHelp": "Ви можете налаштувати зовнішній вигляд вашого екземпляра File Browser, змінивши його назву, замінивши логотип, додавши власні стилі і навіть вимкнувши зовнішні посилання на GitHub.\nЩоб дізнатися більше про кастомне брендування, будь ласка, відвідайте сторінку {0}.",
|
||||
"commandRunnerHelp": "Тут ви можете задати команди, які будуть виконуватися у названих подіях. Ви повинні писати по одній у рядку. Змінні оточення {0} і {1} будуть доступні, будучи {0} відносно {1}. Для отримання додаткової інформації про цю можливість і доступні змінні оточення, будь ласка, прочитайте {2}."
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Допомога",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "Приховати кнопки навігації",
|
||||
"hideNavButtonsDescription": "Приховайте кнопки навігації на панелі навігації у спільному доступі, щоб створити мінімалістичний вигляд.",
|
||||
"viewModeDescription": "Макет за замовчуванням для спільної сторінки.",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "Вам надіслано файл для перегляду або завантаження.",
|
||||
"disableShareCard": "Вимкнути картку спільного доступу",
|
||||
"disableShareCardDescription": "Вимкніть картку спільного доступу на сторінці, до якої надається спільний доступ, на бічній панелі або вгорі сторінки на мобільних пристроях.",
|
||||
"disableSidebar": "Вимкнути бічну панель",
|
||||
"disableSidebarDescription": "Вимкнути бічну панель на спільній сторінці."
|
||||
"disableSidebarDescription": "Вимкнути бічну панель на спільній сторінці.",
|
||||
"enforceDarkLightMode": "Увімкнути режим теми",
|
||||
"enforceDarkLightModeDescription": "Примусово встановлює певний режим теми (темний або світлий) для цього ресурсу, перекриваючи налаштування користувача.",
|
||||
"default": "Не застосовуйте примусовий режим",
|
||||
"dark": "Темнота.",
|
||||
"light": "Світло",
|
||||
"enableOnlyOffice": "Увімкнути переглядач OnlyOffice",
|
||||
"enableOnlyOfficeDescription": "Дозволити перегляд офісних файлів за допомогою OnlyOffice у цьому ресурсі.",
|
||||
"enableOnlyOfficeEditing": "Увімкнути редагування OnlyOffice",
|
||||
"enableOnlyOfficeEditingDescription": "Дозволити редагування офісних файлів за допомогою OnlyOffice у цьому ресурсі.",
|
||||
"titleDefault": "Спільні файли - {title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "Ключі API",
|
||||
@@ -478,7 +505,13 @@
|
||||
"defaultThemeDescription": "default - тема за замовчуванням",
|
||||
"customTheme": "Виберіть свою тему",
|
||||
"showSelectMultiple": "Показувати вибране кілька в контекстному меню на робочому столі",
|
||||
"showSelectMultipleDescription": "За замовчуванням, контекстне меню десктопа не має опції \"вибрати декілька\", як у мобільному. Ця опція завжди відображається у контекстному меню."
|
||||
"showSelectMultipleDescription": "За замовчуванням, контекстне меню десктопа не має опції \"вибрати декілька\", як у мобільному. Ця опція завжди відображається у контекстному меню.",
|
||||
"defaultMediaPlayer": "Використовуйте вбудований медіаплеєр вашого браузера",
|
||||
"defaultMediaPlayerDescription": "Використовуйте власний медіаплеєр вашого браузера для відео- та аудіофайлів, а не медіаплеєр \"vue-plyr\", що входить до складу FileBrowser.",
|
||||
"debugOfficeEditor": "Увімкнути режим налагодження OnlyOffice",
|
||||
"debugOfficeEditorDescription": "Показати налагоджувальне спливаюче вікно з додатковою інформацією в редакторі OnlyOffice.",
|
||||
"fileViewerOptions": "Параметри перегляду файлів",
|
||||
"themeAndLanguage": "Тема та мова"
|
||||
},
|
||||
"general": {
|
||||
"name": "Ім'я",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "Ім'я",
|
||||
"accessManagement": "Керування доступом",
|
||||
"all": "Заперечувати все.",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "Тут показано, що відбувається, коли користувач намагається отримати доступ до шляху, для якого не існує певних правил доступу. \"Дозволити\" означає, що користувачі можуть отримати доступ за замовчуванням, \"Заборонити\" означає, що користувачі за замовчуванням заблоковані, якщо тільки це не дозволено явно."
|
||||
"defaultBehaviorDescription": "Тут показано, що відбувається, коли користувач намагається отримати доступ до шляху, для якого не існує певних правил доступу. \"Дозволити\" означає, що користувачі можуть отримати доступ за замовчуванням, \"Заборонити\" означає, що користувачі за замовчуванням заблоковані, якщо тільки це не дозволено явно.",
|
||||
"defaultBehavior": "Дія за замовчуванням{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "Не вдалося ініціалізувати редактор. Будь ласка, перезавантажте.",
|
||||
"saveFailed": "Не вдалося зберегти файл. Спробуйте ще раз.",
|
||||
"saveAborted": "Збереження перервано. Ім'я файлу не збігається з активним файлом.",
|
||||
"saveDisabled": "Збереження спільних файлів не підтримується.",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "Операцію збереження перервано. Активний файл програми ({activeFile}) не відповідає файлу, який ви намагаєтеся зберегти ({tryingToSave}).",
|
||||
"syncFailed": "не вдалося синхронізувати стан з маршрутом для \"{filename}\" після 5 спроб. Переривання налаштування редактора для запобігання пошкодженню даних."
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "Цикл увімкнено",
|
||||
"LoopDisabled": "Цикл вимкнено"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "清除完毕",
|
||||
"showMore": "显示更多",
|
||||
"showLess": "显示更少",
|
||||
"openParentFolder": "打开父文件夹"
|
||||
"openParentFolder": "打开父文件夹",
|
||||
"selectedCount": "选择执行操作的项目数"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下载文件",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "选择文件或目录",
|
||||
"ctrl": {
|
||||
"click": "选择多个文件或目录",
|
||||
"f": "打开搜索框",
|
||||
"s": "保存文件或下载当前文件夹"
|
||||
"d": "下载所选项目"
|
||||
},
|
||||
"del": "删除所选的文件/文件夹",
|
||||
"doubleClick": "打开文件/文件夹",
|
||||
"esc": "清除已选项或关闭提示信息",
|
||||
"f1": "显示该帮助信息",
|
||||
"f2": "重命名文件/文件夹",
|
||||
"help": "帮助"
|
||||
"help": "帮助",
|
||||
"f1": "显示帮助提示",
|
||||
"description": "您可以查看下面的基本导航选项。有关其他信息,请访问",
|
||||
"wiki": "文件浏览器量子维基"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,16 +135,13 @@
|
||||
"prompts": {
|
||||
"copy": "复制",
|
||||
"copyMessage": "请选择欲复制至的目录:",
|
||||
"deleteMessageMultiple": "你确定要删除这 {count} 个文件吗?",
|
||||
"deleteMessageSingle": "你确定要删除这个文件/文件夹吗?",
|
||||
"deleteMessageShare": "你确定要删除这个分享({path})吗?",
|
||||
"deleteTitle": "删除文件",
|
||||
"displayName": "名称:",
|
||||
"download": "下载文件",
|
||||
"downloadMessage": "请选择要下载的压缩格式。",
|
||||
"error": "出了一点问题...",
|
||||
"fileInfo": "文件信息",
|
||||
"lastModified": "最后修改{suffix}",
|
||||
"move": "移动",
|
||||
"moveMessage": "请选择欲移动至的目录:",
|
||||
"newArchetype": "创建一个基于原型的新帖子。您的文件将会创建在内容文件夹中。",
|
||||
@@ -151,10 +149,7 @@
|
||||
"newDirMessage": "请输入新目录的名称。",
|
||||
"newFile": "新建文件",
|
||||
"newFileMessage": "请输入新文件的名称。",
|
||||
"numberDirs": "目录数{suffix}",
|
||||
"numberFiles": "文件数{suffix}",
|
||||
"rename": "重命名",
|
||||
"renameMessage": "请输入新名称,旧名称为:",
|
||||
"replace": "替换",
|
||||
"replaceMessage": "您尝试上传的文件中有一个与现有文件的名称存在冲突。是否替换现有的同名文件?\n",
|
||||
"schedule": "计划",
|
||||
@@ -172,10 +167,6 @@
|
||||
"dragAndDrop": "在此处拖放文件",
|
||||
"completed": "已完成",
|
||||
"conflictsDetected": "检测到冲突",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "提供的名称无效:不能包含\"/\"或\"\\\"。",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "下载次数限制",
|
||||
"maxBandwidth": "最大下载带宽(千字节/秒)",
|
||||
"shareTheme": "分享主题",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "共享横幅 URL",
|
||||
"shareFavicon": "共享图标 URL",
|
||||
"optionalPasswordHelp": "如果设置了该密码,用户必须输入该密码才能查看共享内容。",
|
||||
"viewMode": "查看模式"
|
||||
"viewMode": "查看模式",
|
||||
"renameMessage": "输入新名称:",
|
||||
"renameMessageInvalid": "文件不能包含\"/\"或\"\\\"。",
|
||||
"source": "资料来源{suffix}",
|
||||
"deleteMessageMultiple": "您确定要删除{count} 文件吗?",
|
||||
"deleteMessageShare": "您确定要删除此共享? {path} ?",
|
||||
"lastModified": "最后修改{suffix}",
|
||||
"numberDirs": "目录数量{suffix}",
|
||||
"numberFiles": "文件数量{suffix}",
|
||||
"renameMessageConflict": "名为 \"{filename}\" 的项目已经存在!",
|
||||
"uploadSettingsChunked": "最大并发上传:{maxConcurrentUpload}块大小 {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "最大并发上传:{maxConcurrentUpload}已禁用分块上传"
|
||||
},
|
||||
"search": {
|
||||
"images": "图像",
|
||||
"music": "音乐",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "输入回车以搜索...",
|
||||
"search": "搜索...",
|
||||
"typeToSearch": "输入以搜索...",
|
||||
"types": "类型",
|
||||
"video": "视频",
|
||||
"path": "路径",
|
||||
"smallerThan": "小于",
|
||||
"largerThan": "大于:",
|
||||
"helpText1": "您键入的每个字符都会进行搜索(搜索词最少 3 个字符)。",
|
||||
"helpText2": "索引:利用索引进行搜索,索引会根据配置的时间间隔(默认为 5 分钟)自动更新。程序刚启动时进行搜索可能会导致搜索结果不完整。"
|
||||
"helpText2": "索引:利用索引进行搜索,索引会根据配置的时间间隔(默认为 5 分钟)自动更新。程序刚启动时进行搜索可能会导致搜索结果不完整。",
|
||||
"noResults": "索引搜索未找到结果。",
|
||||
"onlyFolders": "仅文件夹",
|
||||
"onlyFiles": "仅文件",
|
||||
"showOptions": "显示选项",
|
||||
"photos": "照片",
|
||||
"audio": "音频",
|
||||
"videos": "视频",
|
||||
"documents": "文件",
|
||||
"archives": "档案",
|
||||
"number": "数量",
|
||||
"searchContext": "搜索上下文:{context}",
|
||||
"notEnoughCharacters": "搜索字符数不足(最少{minSearchLength})",
|
||||
"typeToSearch": "开始键入{minSearchLength} 或更多字符开始搜索。"
|
||||
},
|
||||
"settings": {
|
||||
"scopes": "定义允许的来源及其中的用户范围",
|
||||
@@ -215,10 +228,8 @@
|
||||
"avoidChanges": "(留空以避免更改)",
|
||||
"branding": "品牌",
|
||||
"brandingDirectoryPath": "品牌信息文件夹路径",
|
||||
"brandingHelp": "您可以通过改变实例名称,更换 Logo,加入自定义样式,甚至禁用到 Github 的外部链接来自定义 File Browser 的外观和感觉。\n想获得更多信息,请查看 {0}。",
|
||||
"changePassword": "更改密码",
|
||||
"commandRunner": "命令执行器",
|
||||
"commandRunnerHelp": "你可以在此设置在下列事件中执行的命令。每行必须写一条命令。可以在命令中使用环境变量 {0} 和 {1},使 {0} 与 {1} 相关联。关于此功能和可用环境变量的更多信息,请阅读 {2}。",
|
||||
"commandsUpdated": "命令已更新!",
|
||||
"createUserDir": "在添加新用户的同时自动创建用户的个人目录",
|
||||
"customStylesheet": "自定义样式表(CSS)",
|
||||
@@ -300,7 +311,14 @@
|
||||
"adminOptions": "管理员用户选项",
|
||||
"shareDuration": "到期",
|
||||
"searchOptions": "搜索选项",
|
||||
"downloads": "下载"
|
||||
"downloads": "下载",
|
||||
"systemAdmin": "系统与管理",
|
||||
"configViewer": "配置查看器",
|
||||
"configViewerShowFull": "显示完整配置",
|
||||
"configViewerShowComments": "显示评论",
|
||||
"configViewerLoadConfig": "加载配置",
|
||||
"brandingHelp": "您可以更改文件浏览器实例的名称、更换徽标、添加自定义样式,甚至禁用指向 GitHub 的外部链接,从而自定义文件浏览器实例的外观和感觉。\n有关自定义品牌的更多信息,请查阅{0} 。",
|
||||
"commandRunnerHelp": "您可以在此设置在命名事件中执行的命令。每行必须写一条。环境变量{0} 和{1} 将可用,{0} 与{1} 相对。有关此功能和可用环境变量的更多信息,请阅读{2} 。"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "帮助",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "隐藏导航按钮",
|
||||
"hideNavButtonsDescription": "隐藏共享导航栏上的导航按钮,打造简约外观。",
|
||||
"viewModeDescription": "共享页面的默认布局。",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "您已收到一份共享文件,供您查看或下载。",
|
||||
"disableShareCard": "禁用共享卡",
|
||||
"disableShareCardDescription": "禁用共享页面侧边栏或手机页面顶部的共享卡。",
|
||||
"disableSidebar": "禁用侧边栏",
|
||||
"disableSidebarDescription": "禁用共享页面上的边栏。"
|
||||
"disableSidebarDescription": "禁用共享页面上的边栏。",
|
||||
"enforceDarkLightMode": "执行主题模式",
|
||||
"enforceDarkLightModeDescription": "为该共享强制设置特定主题模式(深色或浅色),覆盖用户偏好。",
|
||||
"default": "不执行模式",
|
||||
"dark": "黑暗",
|
||||
"light": "灯光",
|
||||
"enableOnlyOffice": "启用 OnlyOffice 查看器",
|
||||
"enableOnlyOfficeDescription": "允许在此共享中使用 OnlyOffice 查看办公文件。",
|
||||
"enableOnlyOfficeEditing": "启用 OnlyOffice 编辑功能",
|
||||
"enableOnlyOfficeEditingDescription": "允许在此共享中使用 OnlyOffice 编辑办公文件。",
|
||||
"titleDefault": "共享文件{title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "应用程序接口密钥",
|
||||
@@ -478,7 +505,13 @@
|
||||
"defaultThemeDescription": "default - 默认主题",
|
||||
"customTheme": "选择主题",
|
||||
"showSelectMultiple": "在桌面右键菜单中显示多个选择",
|
||||
"showSelectMultipleDescription": "默认情况下,桌面右键菜单不像手机那样有 \"选择多个 \"选项。该选项始终显示在右键菜单中。"
|
||||
"showSelectMultipleDescription": "默认情况下,桌面右键菜单不像手机那样有 \"选择多个 \"选项。该选项始终显示在右键菜单中。",
|
||||
"defaultMediaPlayer": "使用浏览器的本地媒体播放器",
|
||||
"defaultMediaPlayerDescription": "使用浏览器的本地媒体播放器播放视频和音频文件,而不是 FileBrowser 附带的 \"vue-plyr \"媒体播放器。",
|
||||
"debugOfficeEditor": "启用 OnlyOffice 调试模式",
|
||||
"debugOfficeEditorDescription": "在 OnlyOffice 编辑器中显示包含附加信息的调试弹出窗口。",
|
||||
"fileViewerOptions": "文件查看器选项",
|
||||
"themeAndLanguage": "主题与语言"
|
||||
},
|
||||
"general": {
|
||||
"name": "名称",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "名称",
|
||||
"accessManagement": "访问管理",
|
||||
"all": "全部拒绝",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "这显示了当用户试图访问没有特定访问规则的路径时会发生什么。允许 \"表示默认情况下用户可以访问,\"拒绝 \"表示除非明确允许,否则默认情况下用户将被阻止。"
|
||||
"defaultBehaviorDescription": "这显示了当用户试图访问没有特定访问规则的路径时会发生什么。允许 \"表示默认情况下用户可以访问,\"拒绝 \"表示除非明确允许,否则默认情况下用户将被阻止。",
|
||||
"defaultBehavior": "默认操作{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "编辑器初始化失败。请重新加载。",
|
||||
"saveFailed": "保存文件失败。请重试。",
|
||||
"saveAborted": "保存失败。文件名与活动文件不匹配。",
|
||||
"saveDisabled": "不支持保存共享文件。",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "保存操作中止。应用程序的活动文件 ({activeFile}) 与您要保存的文件 ({tryingToSave}) 不匹配。",
|
||||
"syncFailed": "经过 5 次尝试后,未能将 \"{filename}\" 的状态与路由同步。终止编辑器设置,以防数据损坏。"
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "启用环路",
|
||||
"LoopDisabled": "环路禁用"
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,8 @@
|
||||
"clearCompleted": "清除完成",
|
||||
"showMore": "顯示更多",
|
||||
"showLess": "顯示更少",
|
||||
"openParentFolder": "開啟父資料夾"
|
||||
"openParentFolder": "開啟父資料夾",
|
||||
"selectedCount": "選取執行動作的項目數量"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下載檔案",
|
||||
@@ -84,15 +85,15 @@
|
||||
"click": "選擇檔案或目錄",
|
||||
"ctrl": {
|
||||
"click": "選擇多個檔案或目錄",
|
||||
"f": "打開搜尋列",
|
||||
"s": "儲存檔案或下載目前資料夾"
|
||||
"d": "下載所選項目"
|
||||
},
|
||||
"del": "刪除所選的檔案/資料夾",
|
||||
"doubleClick": "打開檔案/資料夾",
|
||||
"esc": "清除已選項或關閉提示資訊",
|
||||
"f1": "顯示該幫助資訊",
|
||||
"f2": "重新命名檔案/資料夾",
|
||||
"help": "幫助"
|
||||
"help": "幫助",
|
||||
"f1": "顯示幫助提示",
|
||||
"description": "您可以查看以下的基本導航選項。如需其他資訊,請造訪",
|
||||
"wiki": "FileBrowser Quantum Wiki"
|
||||
},
|
||||
"languages": {
|
||||
"he": "עברית",
|
||||
@@ -134,7 +135,6 @@
|
||||
"prompts": {
|
||||
"copy": "複製",
|
||||
"copyMessage": "請選擇欲複製至的目錄:",
|
||||
"deleteMessageMultiple": "你確定要刪除這 {count} 個檔案嗎?",
|
||||
"deleteMessageSingle": "你確定要刪除這個檔案/資料夾嗎?",
|
||||
"deleteTitle": "刪除檔案",
|
||||
"displayName": "名稱:",
|
||||
@@ -142,7 +142,6 @@
|
||||
"downloadMessage": "請選擇要下載的壓縮格式。",
|
||||
"error": "發出了一點錯誤...",
|
||||
"fileInfo": "檔案資訊",
|
||||
"lastModified": "最後修改{suffix}",
|
||||
"move": "移動",
|
||||
"moveMessage": "請選擇欲移動至的目錄:",
|
||||
"newArchetype": "建立一個基於原型的新貼文。您的檔案將會建立在內容資料夾中。",
|
||||
@@ -150,10 +149,7 @@
|
||||
"newDirMessage": "請輸入新目錄的名稱。",
|
||||
"newFile": "建立檔案",
|
||||
"newFileMessage": "請輸入新檔案的名稱。",
|
||||
"numberDirs": "目錄數{suffix}",
|
||||
"numberFiles": "檔案數{suffix}",
|
||||
"rename": "重新命名",
|
||||
"renameMessage": "請輸入新名稱,舊名稱為:",
|
||||
"replace": "替換",
|
||||
"replaceMessage": "您嘗試上傳的檔案中有一個與現有檔案的名稱存在衝突。是否取代現有的同名檔案?",
|
||||
"schedule": "計畫",
|
||||
@@ -161,7 +157,6 @@
|
||||
"show": "顯示",
|
||||
"size": "大小",
|
||||
"upload": "上傳",
|
||||
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
|
||||
"destinationSource": "目的地來源:",
|
||||
"deleteUserMessage": "您確定要刪除此使用者嗎?",
|
||||
"dragAndDrop": "將檔案拖放至此",
|
||||
@@ -172,10 +167,6 @@
|
||||
"typeName": "類型:",
|
||||
"deleted": "已成功刪除!",
|
||||
"conflictsDetected": "偵測到衝突",
|
||||
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
|
||||
"invalidName": "提供的名稱無效:不能包含 \"/「 或 」\\\" 的容器",
|
||||
"source": "Source{suffix}",
|
||||
"downloadsLimit": "下載次數限制",
|
||||
"maxBandwidth": "最大下載頻寬 (kbps)",
|
||||
"shareTheme": "分享主題",
|
||||
@@ -186,22 +177,44 @@
|
||||
"shareBanner": "分享橫幅網址",
|
||||
"shareFavicon": "分享圖示 URL",
|
||||
"optionalPasswordHelp": "如果設定,使用者必須輸入此密碼才能檢視共用內容。",
|
||||
"viewMode": "檢視模式"
|
||||
"viewMode": "檢視模式",
|
||||
"renameMessage": "輸入新名稱:",
|
||||
"renameMessageInvalid": "檔案不能包含\"/「或」\\\"",
|
||||
"source": "來源{suffix}",
|
||||
"deleteMessageMultiple": "您確定要刪除{count} 檔案嗎?",
|
||||
"deleteMessageShare": "您確定要刪除此共用嗎? {path} ?",
|
||||
"lastModified": "最後修改{suffix}",
|
||||
"numberDirs": "目錄數量{suffix}",
|
||||
"numberFiles": "檔案數量{suffix}",
|
||||
"renameMessageConflict": "名為 \"{filename}\" 的項目已經存在!",
|
||||
"uploadSettingsChunked": "最大同時上傳數量:{maxConcurrentUpload}, 分塊大小: {uploadChunkSizeMb} MB",
|
||||
"uploadSettingsNoChunk": "最大同時上傳數量:{maxConcurrentUpload}, 已停用分塊上傳"
|
||||
},
|
||||
"search": {
|
||||
"images": "影像",
|
||||
"music": "音樂",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "按確認鍵搜尋...",
|
||||
"search": "搜尋...",
|
||||
"typeToSearch": "輸入以搜尋...",
|
||||
"types": "類型",
|
||||
"video": "影片",
|
||||
"path": "路徑:",
|
||||
"smallerThan": "小於:",
|
||||
"largerThan": "大於:",
|
||||
"helpText1": "您輸入的每個字元都會進行搜尋(搜尋字詞最少 3 個字元)。",
|
||||
"helpText2": "索引:搜尋會使用索引,該索引會在設定的間隔 (預設值:5 分鐘) 自動更新。當程式剛開始搜尋時,可能會導致結果不完整。"
|
||||
"helpText2": "索引:搜尋會使用索引,該索引會在設定的間隔 (預設值:5 分鐘) 自動更新。當程式剛開始搜尋時,可能會導致結果不完整。",
|
||||
"noResults": "在索引搜尋中未找到任何結果。",
|
||||
"onlyFolders": "僅文件夾",
|
||||
"onlyFiles": "僅檔案",
|
||||
"showOptions": "顯示選項",
|
||||
"photos": "圖片",
|
||||
"audio": "音訊",
|
||||
"videos": "影片",
|
||||
"documents": "文件",
|
||||
"archives": "檔案",
|
||||
"number": "數量",
|
||||
"searchContext": "搜尋範圍:{context}",
|
||||
"notEnoughCharacters": "搜尋字元不足 (最少{minSearchLength})",
|
||||
"typeToSearch": "開始輸入{minSearchLength} 或更多字元以開始搜尋。"
|
||||
},
|
||||
"settings": {
|
||||
"admin": "管理員",
|
||||
@@ -214,10 +227,8 @@
|
||||
"avoidChanges": "(留空以避免更改)",
|
||||
"branding": "品牌",
|
||||
"brandingDirectoryPath": "品牌資訊資料夾路徑",
|
||||
"brandingHelp": "您可以通過改變例項名稱,更換Logo,加入自定義樣式,甚至禁用到Github的外部連結來自定義File Browser的外觀和給人的感覺。\n想獲得更多資訊,請檢視 {0} 。",
|
||||
"changePassword": "更改密碼",
|
||||
"commandRunner": "命令執行器",
|
||||
"commandRunnerHelp": "在這裡你可以設定在下面的事件中執行的命令。每行必須寫一條命令。可以在命令中使用環境變數 {0} 和 {1}。關於此功能和可用環境變數的更多資訊,請閱讀{2}.",
|
||||
"commandsUpdated": "命令已更新!",
|
||||
"createUserDir": "在新增新使用者的同時自動建立使用者的個人目錄",
|
||||
"customStylesheet": "自定義樣式表",
|
||||
@@ -300,7 +311,14 @@
|
||||
"shareDuration": "到期",
|
||||
"shareDeleted": "分享已刪除!",
|
||||
"adminOptions": "管理員使用者選項",
|
||||
"downloads": "下載"
|
||||
"downloads": "下載",
|
||||
"systemAdmin": "系統與管理",
|
||||
"configViewer": "組態檢視器",
|
||||
"configViewerShowFull": "顯示完整設定",
|
||||
"configViewerShowComments": "顯示評論",
|
||||
"configViewerLoadConfig": "載入組態",
|
||||
"brandingHelp": "您可以透過更改名稱、更換標誌、新增自訂樣式,甚至停用 GitHub 的外部連結,自訂檔案瀏覽器實例的外觀和感覺。\n有關自訂品牌的詳細資訊,請參閱{0} 。",
|
||||
"commandRunnerHelp": "您可以在此設定在命名事件中執行的指令。您必須每行寫一個。環境變數{0} 和{1} 將可用,是相對於{1} 的{0} 。如需更多關於此功能和可用環境變數的資訊,請閱讀{2} 。"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "幫助",
|
||||
@@ -358,12 +376,21 @@
|
||||
"hideNavButtons": "隱藏導覽按鈕",
|
||||
"hideNavButtonsDescription": "隱藏分享中導覽列上的導覽按鈕,以創造簡約的外觀。",
|
||||
"viewModeDescription": "共用頁面的預設版面。",
|
||||
"titleDefault": "Shared files - {title}",
|
||||
"descriptionDefault": "已向您發送一份共用檔案,供您檢視或下載。",
|
||||
"disableShareCard": "停用分享卡",
|
||||
"disableShareCardDescription": "停用分享頁面側邊欄或行動版頁面頂端的分享卡。",
|
||||
"disableSidebar": "停用側邊欄",
|
||||
"disableSidebarDescription": "停用共用頁面上的側邊欄。"
|
||||
"disableSidebarDescription": "停用共用頁面上的側邊欄。",
|
||||
"enforceDarkLightMode": "強制執行主題模式",
|
||||
"enforceDarkLightModeDescription": "強制此共用的特定主題模式 (深色或淺色),凌駕使用者偏好設定。",
|
||||
"default": "不要強制執行模式",
|
||||
"dark": "黑暗",
|
||||
"light": "燈光",
|
||||
"enableOnlyOffice": "啟用 OnlyOffice 檢視器",
|
||||
"enableOnlyOfficeDescription": "允許在此共用中使用 OnlyOffice 檢視辦公室檔案。",
|
||||
"enableOnlyOfficeEditing": "啟用 OnlyOffice 編輯",
|
||||
"enableOnlyOfficeEditingDescription": "允許在此共用中使用 OnlyOffice 編輯辦公室檔案。",
|
||||
"titleDefault": "共用檔案 -{title}"
|
||||
},
|
||||
"api": {
|
||||
"title": "API 金鑰",
|
||||
@@ -452,7 +479,13 @@
|
||||
"defaultThemeDescription": "default - 預設主題",
|
||||
"customTheme": "選擇您的主題",
|
||||
"showSelectMultiple": "在桌面的上下文功能表中顯示多重選擇",
|
||||
"showSelectMultipleDescription": "預設情況下,桌上型電腦的上下文功能表不像行動裝置一樣有「選取多個」選項。此選項總是在上下文功能表中顯示。"
|
||||
"showSelectMultipleDescription": "預設情況下,桌上型電腦的上下文功能表不像行動裝置一樣有「選取多個」選項。此選項總是在上下文功能表中顯示。",
|
||||
"defaultMediaPlayer": "使用瀏覽器的本機媒體播放器",
|
||||
"defaultMediaPlayerDescription": "使用瀏覽器的本機媒體播放器來播放視訊和音訊檔案,而非 FileBrowser 隨附的「vue-plyr」媒體播放器。",
|
||||
"debugOfficeEditor": "啟用 OnlyOffice 除錯模式",
|
||||
"debugOfficeEditorDescription": "在 OnlyOffice 編輯器中顯示包含其他資訊的除錯彈出式視窗。",
|
||||
"fileViewerOptions": "檔案檢視器選項",
|
||||
"themeAndLanguage": "主題與語言"
|
||||
},
|
||||
"otp": {
|
||||
"name": "雙因素驗證",
|
||||
@@ -513,15 +546,19 @@
|
||||
"name": "名稱",
|
||||
"accessManagement": "存取管理",
|
||||
"all": "全部拒絕",
|
||||
"defaultBehavior": "Default action{suffix}",
|
||||
"defaultBehaviorDescription": "這顯示使用者嘗試存取沒有特定存取規則的路徑時會發生的情況。允許」表示使用者預設可以存取,「拒絕」表示使用者預設會被阻擋,除非明確允許。"
|
||||
"defaultBehaviorDescription": "這顯示使用者嘗試存取沒有特定存取規則的路徑時會發生的情況。允許」表示使用者預設可以存取,「拒絕」表示使用者預設會被阻擋,除非明確允許。",
|
||||
"defaultBehavior": "預設動作{suffix}"
|
||||
},
|
||||
"editor": {
|
||||
"uninitialized": "編輯器初始化失敗。請重新載入。",
|
||||
"saveFailed": "儲存檔案失敗。請重試。",
|
||||
"saveAborted": "儲存中止。檔案名稱與使用中的檔案不符。",
|
||||
"saveDisabled": "不支援儲存共用檔案。",
|
||||
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
|
||||
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption."
|
||||
"saveAbortedMessage": "儲存作業中止。應用程式的作用中檔案 ({activeFile}) 與您試圖儲存的檔案 ({tryingToSave}) 不匹配。",
|
||||
"syncFailed": "經過 5 次嘗試後,未能與 \"{filename}\" 的路由同步狀態。中止編輯器設定以防止資料損毀。"
|
||||
},
|
||||
"player": {
|
||||
"LoopEnabled": "啟用循環",
|
||||
"LoopDisabled": "迴路停用"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { createApp } from 'vue';
|
||||
import router from './router'; // Adjust the path as per your setup
|
||||
import App from './App.vue'; // Adjust the path as per your setup
|
||||
import { state } from '@/store'; // Adjust the path as per your setup
|
||||
import { createApp } from "vue";
|
||||
import router from "./router"; // Adjust the path as per your setup
|
||||
import App from "./App.vue"; // Adjust the path as per your setup
|
||||
import { state } from "@/store"; // Adjust the path as per your setup
|
||||
import i18n from "@/i18n";
|
||||
import VueLazyload from "vue-lazyload";
|
||||
import VuePlyr from "@skjnldsv/vue-plyr"; // Custom media player
|
||||
|
||||
import './css/styles.css';
|
||||
import "./css/styles.css";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
@@ -13,9 +14,10 @@ const app = createApp(App);
|
||||
app.use(VueLazyload);
|
||||
app.use(i18n);
|
||||
app.use(router);
|
||||
app.use(VuePlyr);
|
||||
|
||||
// Provide state to the entire application
|
||||
app.provide('state', state);
|
||||
app.provide("state", state);
|
||||
|
||||
// provide v-focus for components
|
||||
app.directive("focus", {
|
||||
@@ -35,4 +37,4 @@ app.mixin({
|
||||
},
|
||||
});
|
||||
|
||||
router.isReady().then(() => app.mount("#app"));
|
||||
router.isReady().then(() => app.mount("#app"));
|
||||
@@ -138,7 +138,7 @@ const router = createRouter({
|
||||
|
||||
// Helper function to check if a route resolves to itself
|
||||
function isSameRoute(to: RouteLocation, from: RouteLocation) {
|
||||
return to.path === from.path;
|
||||
return to.path === from.path && to.hash === from.hash;
|
||||
}
|
||||
|
||||
router.beforeResolve(async (to, from, next) => {
|
||||
|
||||
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user