Compare commits

...
98 changed files with 1866 additions and 800 deletions

No files matched your search

+29
View File
@@ -2,6 +2,35 @@
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.7.12-beta
Happy 4th of July!
The most noteworthy change is that no sources will be automatically enabled for any user. In order for a user to use a source, it needs to be added for that user. Or to keep a source available for all users, you can specify `defaultEnabled` in the source config to maintain the same behavior. See the wiki
**New Features**:
- setting added `deleteWithoutConfirming`, useful for quickly deleting files -- does not apply to folders.
- more options for minimal UI https://github.com/gtsteffaniak/filebrowser/issues/745
- dedicated section for sidebar customization in profile settings https://github.com/gtsteffaniak/filebrowser/issues/437
**Notes**:
- Filebrowser no longer requires a default source, users can be created without any sources.
- Disables changing login type fallback behavior https://github.com/gtsteffaniak/filebrowser/issues/620
- Uses calculated index size as "used" and total partition size as "total" https://github.com/gtsteffaniak/filebrowser/issues/875
- Select multiple won't show up in context menu when using a desktop browser (with keyboard), opting for keyboard shortcuts
- Updated translations that were not complete, such as simplified chinese https://github.com/gtsteffaniak/filebrowser/issues/895
- larger min drop target size https://github.com/gtsteffaniak/filebrowser/issues/902
- refresh page after file actions https://github.com/gtsteffaniak/filebrowser/issues/894
- improved user PUT handler for easier user modification via API https://github.com/gtsteffaniak/filebrowser/issues/897
- optional sidebar actions for upload/create https://github.com/gtsteffaniak/filebrowser/issues/885
**BugFixes**:
- fix delete in preview when moving between pictures. https://github.com/gtsteffaniak/filebrowser/issues/456
- getting file info issue when indexing is disabled.
- fixed initial sort order https://github.com/gtsteffaniak/filebrowser/issues/551
- incorrect filename Drag and Drop fixes https://github.com/gtsteffaniak/filebrowser/issues/880
- fix share duration always showing just now https://github.com/gtsteffaniak/filebrowser/issues/896
## v0.7.11-beta
**Breaking Changes**:
+3 -3
View File
@@ -6,7 +6,7 @@
[![DockerHub Pulls](https://img.shields.io/docker/pulls/gtstef/filebrowser?label=latest%20Docker%20pulls)](https://hub.docker.com/r/gtstef/filebrowser)
[![Apache-2.0 License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/donate/?business=W5XKNXHJM2WPE&no_recurring=0&currency_code=USD)
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://github.com/gtsteffaniak/filebrowser/wiki/Q&A#is-there-a-way-to-donate-or-support-this-project)
<img width="150" src="https://github.com/user-attachments/assets/59986a2a-f960-4536-aa35-4a9a7c98ad48" title="Logo">
<h3>FileBrowser Quantum</h3>
@@ -18,7 +18,7 @@
> [!WARNING]
> There is no stable version -- :construction: planned for 2025. ([Read more](https://github.com/gtsteffaniak/filebrowser/discussions/628))
Poll: [how much RAM does your install use?](https://github.com/gtsteffaniak/filebrowser/discussions/787)
[FYI: how much RAM does it require?](https://github.com/gtsteffaniak/filebrowser/discussions/787)
## About
@@ -120,7 +120,7 @@ webdav support | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ |
FTP support | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ |
Dedicated docs site? | :construction: | ✅ | ✅ | ✅ | ❌ | ✅ |
Multiple sources at once | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
Docker image size | 200 MB (with ffmpeg) | 31 MB | 240 MB (main image) | 250 MB | ❌ | > 2 GB |
Docker image size | 180 MB (with ffmpeg) | 31 MB | 240 MB (main image) | 250 MB | ❌ | > 2 GB |
Min. Memory Requirements | 256 MB | 128 MB | 128 MB (main image) | 512 MB | ❌ | 512 MB |
has standalone binary | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
price | free | free | free | free tier | free tier | $99+ |
+1 -1
View File
@@ -13,4 +13,4 @@ services:
- "./src/proxy/backend/config.yaml:/home/filebrowser/config.yaml"
build:
context: ../
dockerfile: ./_docker/Dockerfile
dockerfile: ./_docker/Dockerfile.slim
+4
View File
@@ -5,8 +5,12 @@ server:
- levels: "info|error|debug"
sources:
- path: "../frontend/tests/playwright-files"
config:
defaultEnabled: true
- path: "."
name: "docker"
config:
defaultEnabled: true
frontend:
name: "Graham's Filebrowser"
disableDefaultLinks: true
+1
View File
@@ -6,6 +6,7 @@ server:
sources:
- path: "../frontend/tests/playwright-files"
config:
defaultEnabled: true
exclude:
folderPaths:
- "excluded"
+25 -65
View File
@@ -29,9 +29,6 @@ import (
func FileInfoFaster(opts iteminfo.FileOptions) (iteminfo.ExtendedFileInfo, error) {
response := iteminfo.ExtendedFileInfo{}
if opts.Source == "" {
opts.Source = settings.Config.Server.DefaultSource.Name
}
index := indexing.GetIndex(opts.Source)
if index == nil {
return response, fmt.Errorf("could not get index: %v ", opts.Source)
@@ -136,6 +133,9 @@ func DeleteFiles(source, absPath string, absDirPath string) error {
if index == nil {
return fmt.Errorf("could not get index: %v ", source)
}
if index.Config.DisableIndexing {
return nil
}
refreshConfig := iteminfo.FileOptions{Path: index.MakeIndexPath(absDirPath), IsDir: true}
err = index.RefreshFileInfo(refreshConfig)
if err != nil {
@@ -144,38 +144,28 @@ func DeleteFiles(source, absPath string, absDirPath string) error {
return nil
}
func RefreshIndex(source string, path string, isDir bool) error {
idx := indexing.GetIndex(source)
if idx == nil {
return fmt.Errorf("could not get index: %v ", source)
}
if idx.Config.DisableIndexing {
return nil
}
path = idx.MakeIndexPath(path)
return idx.RefreshFileInfo(iteminfo.FileOptions{Path: path, IsDir: isDir})
}
func MoveResource(sourceIndex, destIndex, realsrc, realdst string) error {
err := fileutils.MoveFile(realsrc, realdst)
if err != nil {
return err
}
idxSrc := indexing.GetIndex(sourceIndex)
if idxSrc == nil {
return fmt.Errorf("could not get index: %v ", sourceIndex)
}
idxDst := indexing.GetIndex(destIndex)
if idxDst == nil {
return fmt.Errorf("could not get index: %v ", sourceIndex)
}
refreshSourceDir := idxSrc.MakeIndexPath(filepath.Dir(realsrc))
refreshDestDir := idxDst.MakeIndexPath(filepath.Dir(realdst))
// refresh info for source and dest
err = idxSrc.RefreshFileInfo(iteminfo.FileOptions{
Path: refreshSourceDir,
IsDir: true,
})
err = RefreshIndex(sourceIndex, filepath.Dir(realsrc), true)
if err != nil {
return fmt.Errorf("could not refresh index for source: %v", err)
return err
}
if refreshSourceDir == refreshDestDir {
return nil
}
refreshConfig := iteminfo.FileOptions{Path: refreshDestDir, IsDir: true}
err = idxDst.RefreshFileInfo(refreshConfig)
if err != nil {
return fmt.Errorf("could not refresh index for dest: %v", err)
}
return nil
return RefreshIndex(destIndex, filepath.Dir(realdst), true)
}
func CopyResource(sourceIndex, destIndex, realsrc, realdst string) error {
@@ -183,33 +173,11 @@ func CopyResource(sourceIndex, destIndex, realsrc, realdst string) error {
if err != nil {
return err
}
idxSrc := indexing.GetIndex(sourceIndex)
if idxSrc == nil {
return fmt.Errorf("could not get index: %v ", sourceIndex)
}
idxDst := indexing.GetIndex(destIndex)
if idxDst == nil {
return fmt.Errorf("could not get index: %v ", sourceIndex)
}
refreshSourceDir := idxSrc.MakeIndexPath(filepath.Dir(realsrc))
refreshDestDir := idxDst.MakeIndexPath(filepath.Dir(realdst))
index := indexing.GetIndex(sourceIndex)
if index == nil {
return fmt.Errorf("could not get index: %v ", sourceIndex)
}
refreshConfig := iteminfo.FileOptions{Path: refreshSourceDir, IsDir: true}
// refresh info for source and dest
err = index.RefreshFileInfo(refreshConfig)
err = RefreshIndex(sourceIndex, filepath.Dir(realsrc), true)
if err != nil {
return fmt.Errorf("could not refresh index for source: %v", err)
return err
}
refreshConfig.Path = refreshDestDir
err = index.RefreshFileInfo(refreshConfig)
if err != nil {
return errors.ErrEmptyKey
}
return nil
return RefreshIndex(destIndex, filepath.Dir(realdst), true)
}
func WriteDirectory(opts iteminfo.FileOptions) error {
@@ -223,11 +191,7 @@ func WriteDirectory(opts iteminfo.FileOptions) error {
if err != nil {
return err
}
err = idx.RefreshFileInfo(opts)
if err != nil {
return errors.ErrEmptyKey
}
return nil
return RefreshIndex(idx.Name, opts.Path, true)
}
func WriteFile(opts iteminfo.FileOptions, in io.Reader) error {
@@ -236,13 +200,11 @@ func WriteFile(opts iteminfo.FileOptions, in io.Reader) error {
return fmt.Errorf("could not get index: %v ", opts.Source)
}
dst, _, _ := idx.GetRealPath(opts.Path)
parentDir := filepath.Dir(dst)
// Create the directory and all necessary parents
err := os.MkdirAll(parentDir, 0775)
// Ensure the parent directories exist
err := os.MkdirAll(filepath.Dir(dst), 0775)
if err != nil {
return err
}
// Open the file for writing (create if it doesn't exist, truncate if it does)
file, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
if err != nil {
@@ -255,9 +217,7 @@ func WriteFile(opts iteminfo.FileOptions, in io.Reader) error {
if err != nil {
return err
}
opts.Path = idx.MakeIndexPath(parentDir)
opts.IsDir = true
return idx.RefreshFileInfo(opts)
return RefreshIndex(opts.Source, opts.Path, false)
}
// getContent reads and returns the file content if it's considered an editable text file.
+6 -6
View File
@@ -20,12 +20,12 @@ func MakeUserDir(fullPath string) error {
func MakeUserDirs(u *users.User, disableScopeChange bool) error {
cleanedUserName := users.CleanUsername(u.Username)
if cleanedUserName == "" || cleanedUserName == "-" || cleanedUserName == "." {
return fmt.Errorf("create user: invalid user for home dir creation: [%s]", u.Username)
return fmt.Errorf("MakeUserDirs: invalid user for home dir creation: [%s]", u.Username)
}
for i, scope := range u.Scopes {
source, ok := settings.Config.Server.SourceMap[scope.Name]
if !ok {
return fmt.Errorf("create user: source not found: %s", scope.Name)
return fmt.Errorf("MakeUserDirs: source not found: %s", scope.Name)
}
// create directory and append user name
if filepath.Base(scope.Scope) != cleanedUserName && source.Config.CreateUserDir && !disableScopeChange {
@@ -33,19 +33,19 @@ func MakeUserDirs(u *users.User, disableScopeChange bool) error {
parentDir := filepath.Join(source.Path, scope.Scope)
// validate that scope path exists
if !Exists(parentDir) {
return fmt.Errorf("create user: scope path does not exist: %s", scope.Scope)
return fmt.Errorf("MakeUserDirs: scope path does not exist: %s", scope.Scope)
}
scope.Scope = filepath.Join(scope.Scope, cleanedUserName)
err := MakeUserDir(fullPath)
if err != nil {
return fmt.Errorf("create user: failed to create user home dir: %s", err)
return fmt.Errorf("MakeUserDirs: failed to create user home dir: %s", err)
}
} else if filepath.Base(scope.Scope) == cleanedUserName && source.Config.CreateUserDir {
// create directory exactly as specified
fullPath := filepath.Join(source.Path, scope.Scope)
parentDir := filepath.Dir(fullPath)
if !Exists(parentDir) {
return fmt.Errorf("create user: scope folder does not exist: %s", parentDir)
return fmt.Errorf("MakeUserDirs: scope folder does not exist: %s", parentDir)
}
err := MakeUserDir(fullPath)
if err != nil {
@@ -55,7 +55,7 @@ func MakeUserDirs(u *users.User, disableScopeChange bool) error {
// just assigning scope to path provided, so just check that it exists
path := filepath.Join(source.Path, scope.Scope)
if !Exists(path) {
return fmt.Errorf("create user: scope folder does not exist: %s", path)
return fmt.Errorf("MakeUserDirs: scope folder does not exist: %s", path)
}
}
u.Scopes[i] = scope
+1 -10
View File
@@ -10,7 +10,6 @@ import (
"github.com/gtsteffaniak/filebrowser/backend/common/errors"
"github.com/gtsteffaniak/filebrowser/backend/database/users"
"github.com/gtsteffaniak/go-logger/logger"
)
// JSONAuth is a json implementation of an Auther.
@@ -59,15 +58,7 @@ func (auther JSONAuth) Auth(r *http.Request, userStore *users.Storage) (*users.U
}
if user.LoginMethod != users.LoginMethodPassword {
logger.Warningf("user %s is not allowed to login with password authentication, bypassing and updating login method", user.Username)
user.LoginMethod = users.LoginMethodPassword
// Perform the user update
err = userStore.Update(user, true, "LoginMethod")
if err != nil {
logger.Debug(err.Error())
}
//http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
//return
return nil, errors.ErrWrongLoginMethod
}
return user, nil
+2 -3
View File
@@ -58,12 +58,11 @@ func runCLI() bool {
// Create a new FlagSet for the 'set' subcommand
setCmd := flag.NewFlagSet("set", flag.ExitOnError)
var user, scope, dbConfig string
var user, dbConfig string
var asAdmin bool
setCmd.StringVar(&user, "u", "", "Comma-separated username and password: \"set -u <username>,<password>\"")
setCmd.BoolVar(&asAdmin, "a", false, "Create a user as admin user, used in combination with -u")
setCmd.StringVar(&scope, "s", "", "Specify a user scope, otherwise default user config scope is used")
setCmd.StringVar(&dbConfig, "c", "config.yaml", "Path to the config file, default: config.yaml")
// Parse subcommand flags only if a subcommand is specified
@@ -121,7 +120,7 @@ func runCLI() bool {
return false
}
if user.LoginMethod != users.LoginMethodPassword {
logger.Warningf("user %s is not allowed to login with password authentication, bypassing and updating login method", user.Username)
logger.Fatalf("user %s is not allowed to login with password authentication, cannot set password", username)
}
user.Password = password
user.TOTPSecret = "" // reset TOTP secret if it exists
-2
View File
@@ -17,7 +17,6 @@ func TestUpdateUserScopes_Phases(t *testing.T) {
}}
sourceB := settings.Source{Path: "/pathB", Name: "sourceB", Config: settings.SourceConfig{DefaultEnabled: true, DefaultUserScope: "/defaultB"}}
settings.Config.Server.Sources = []settings.Source{sourceA, sourceB}
settings.Config.Server.DefaultSource = sourceB
settings.Config.Server.SourceMap = map[string]settings.Source{
"/pathA": sourceA,
"/pathB": sourceB,
@@ -161,7 +160,6 @@ func TestUpdateUserScopes_Phases(t *testing.T) {
}}
sourceB = settings.Source{Path: "/pathB", Name: "sourceB", Config: settings.SourceConfig{DefaultEnabled: true, DefaultUserScope: "/defaultB"}}
settings.Config.Server.Sources = []settings.Source{sourceA, sourceB}
settings.Config.Server.DefaultSource = sourceB
settings.Config.Server.SourceMap = map[string]settings.Source{
"/pathA": sourceA,
"/pathB": sourceB,
+1
View File
@@ -21,4 +21,5 @@ var (
ErrNoTotpConfigured = errors.New("OTP is enforced, but user is not yet configured")
ErrUnauthorized = errors.New("user unauthorized")
ErrNotIndexed = errors.New("directory or item excluded from indexing")
ErrWrongLoginMethod = errors.New("user attempted to login with wrong login method")
)
+4 -4
View File
@@ -30,10 +30,10 @@ type LoginMethods struct {
type PasswordAuthConfig struct {
Enabled bool `json:"enabled"`
MinLength int `json:"minLength" validate:"omitempty,min=5"` // minimum pasword length required.
Signup bool `json:"signup" validate:"omitempty"` // allow signups on login page if enabled -- not secure.
Recaptcha Recaptcha `json:"recaptcha" validate:"omitempty"` // recaptcha config, only used if signup is enabled
EnforcedOtp bool `json:"enforcedOtp"` // if set to true, TOTP is enforced for all password users users. Otherwise, users can choose to enable TOTP.
MinLength int `json:"minLength" validate:"omitempty"` // minimum pasword length required, default is 5.
Signup bool `json:"signup" validate:"omitempty"` // allow signups on login page if enabled -- not secure.
Recaptcha Recaptcha `json:"recaptcha" validate:"omitempty"` // recaptcha config, only used if signup is enabled
EnforcedOtp bool `json:"enforcedOtp"` // if set to true, TOTP is enforced for all password users users. Otherwise, users can choose to enable TOTP.
}
type ProxyAuthConfig struct {
-25
View File
@@ -95,9 +95,6 @@ func setupSources(generate bool) {
} else {
source.Name = name
}
if Config.Server.DefaultSource.Path == "" {
Config.Server.DefaultSource = source
}
}
modifyExcludeInclude(&source)
@@ -112,8 +109,6 @@ func setupSources(generate bool) {
sourceList := []Source{}
defaultScopes := []users.SourceScope{}
allSourceNames := []string{}
first := true
potentialDefaultSource := Config.Server.DefaultSource
for _, sourcePathOnly := range Config.Server.Sources {
realPath := getRealPath(sourcePathOnly.Path)
if generate {
@@ -121,15 +116,8 @@ func setupSources(generate bool) {
}
source, ok := Config.Server.SourceMap[realPath]
if ok && !slices.Contains(allSourceNames, source.Name) {
if first {
potentialDefaultSource = source
}
first = false
sourceList = append(sourceList, source)
if source.Config.DefaultEnabled {
if Config.Server.DefaultSource.Path == "" {
Config.Server.DefaultSource = source
}
defaultScopes = append(defaultScopes, users.SourceScope{
Name: source.Path,
Scope: source.Config.DefaultUserScope,
@@ -140,19 +128,6 @@ func setupSources(generate bool) {
logger.Warningf("source %v is not configured correctly, skipping", sourcePathOnly.Path)
}
}
if Config.Server.DefaultSource.Path == "" {
Config.Server.DefaultSource = potentialDefaultSource
}
sourceList2 := []Source{}
for _, s := range sourceList {
if s.Path == Config.Server.DefaultSource.Path {
s.Config.DefaultEnabled = true
Config.Server.SourceMap[s.Path] = s
Config.Server.NameToSource[s.Name] = s
}
sourceList2 = append(sourceList2, s)
}
sourceList = sourceList2
Config.UserDefaults.DefaultScopes = defaultScopes
Config.Server.Sources = sourceList
}
+7 -2
View File
@@ -41,8 +41,7 @@ type Server struct {
// not exposed to config
SourceMap map[string]Source `json:"-" validate:"omitempty"` // uses realpath as key
NameToSource map[string]Source `json:"-" validate:"omitempty"` // uses name as key
DefaultSource Source `json:"-" validate:"omitempty"`
MuPdfAvailable bool `json:"-"` // used internally if compiled with mupdf support
MuPdfAvailable bool `json:"-"` // used internally if compiled with mupdf support
}
type Integrations struct {
@@ -110,6 +109,7 @@ type Frontend struct {
DisableDefaultLinks bool `json:"disableDefaultLinks"` // disable default links in the sidebar
DisableUsedPercentage bool `json:"disableUsedPercentage"` // disable used percentage for the sources in the sidebar
ExternalLinks []ExternalLink `json:"externalLinks"`
DisableNavButtons bool `json:"disableNavButtons"` // disable the nav buttons in the sidebar
}
type ExternalLink struct {
@@ -121,6 +121,9 @@ type ExternalLink struct {
// UserDefaults is a type that holds the default values
// for some fields on User.
type UserDefaults struct {
HideSidebarFileActions bool `json:"hideSidebarFileActions"` // hide the file actions in the sidebar
DisableQuickToggles bool `json:"disableQuickToggles"` // disable the quick toggles in the sidebar
DisableSearchOptions bool `json:"disableSearchOptions"` // disable the search options in the search bar
StickySidebar bool `json:"stickySidebar"` // keep sidebar open when navigating
DarkMode bool `json:"darkMode"` // should dark mode be enabled
Locale string `json:"locale"` // language to use: eg. de, en, or fr
@@ -140,4 +143,6 @@ type UserDefaults struct {
Permissions users.Permissions `json:"permissions"`
LoginMethod string `json:"loginMethod,omitempty"` // login method to use: eg. password, proxy, oidc
DisableUpdateNotifications bool `json:"disableUpdateNotifications"` // disable update notifications banner for admin users
DeleteWithoutConfirming bool `json:"deleteWithoutConfirming"` // delete files without confirmation
}
+4
View File
@@ -77,6 +77,10 @@ type SourceScope struct {
// json tags must match variable name with smaller case first letter
type NonAdminEditable struct {
HideSidebarFileActions bool `json:"hideSidebarFileActions"` // hide the file actions in the sidebar
DisableQuickToggles bool `json:"disableQuickToggles"` // disable the quick toggles in the sidebar
DisableSearchOptions bool `json:"disableSearchOptions"` // disable the search options in the search bar
DeleteWithoutConfirming bool `json:"deleteWithoutConfirming"` // delete files without confirmation
Preview Preview `json:"preview"`
StickySidebar bool `json:"stickySidebar"` // keep sidebar open when navigating
DarkMode bool `json:"darkMode"` // should dark mode be enabled
+4 -8
View File
@@ -7,7 +7,7 @@ require (
github.com/coreos/go-oidc/v3 v3.14.1
github.com/dsoprea/go-exif/v3 v3.0.1
github.com/gen2brain/go-fitz v1.24.15
github.com/go-playground/validator/v10 v10.26.0
github.com/go-playground/validator/v10 v10.27.0
github.com/goccy/go-yaml v1.18.0
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/google/go-cmp v0.7.0
@@ -15,7 +15,6 @@ require (
github.com/gtsteffaniak/go-logger v0.1.2
github.com/kovidgoyal/imaging v1.6.4
github.com/pquerna/otp v1.5.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/afero v1.14.0
github.com/stretchr/testify v1.10.0
github.com/swaggo/http-swagger v1.3.4
@@ -91,8 +90,7 @@ require (
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
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
@@ -110,7 +108,7 @@ require (
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-20250613135800-9e8e59d779cc // indirect
github.com/golang/geo v0.0.0-20250704082316-8c117c302233 // 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
@@ -177,7 +175,6 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polyfloyd/go-errorlint v1.8.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
@@ -228,12 +225,11 @@ require (
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.3.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // 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.augendre.info/fatcontext v0.8.0 // indirect
go.etcd.io/bbolt v1.4.1 // indirect
go.etcd.io/bbolt v1.4.2 // 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
+8 -20
View File
@@ -219,8 +219,8 @@ github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY=
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw=
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -229,9 +229,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
@@ -246,8 +243,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -288,8 +285,8 @@ github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
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-20250613135800-9e8e59d779cc h1:HeTandKJybEWXv6wW9o3ybXTNqGGV4Q6kW3S+8RsXbk=
github.com/golang/geo v0.0.0-20250613135800-9e8e59d779cc/go.mod h1:Vaw7L5b+xa3Rj4/pRtrQkymn3lSBRB/NAEdbF9YEVLA=
github.com/golang/geo v0.0.0-20250704082316-8c117c302233 h1:awuPENW9GP/dvbO712KkZVg88z8CFNo0XPoEj0zT/1Q=
github.com/golang/geo v0.0.0-20250704082316-8c117c302233/go.mod h1:Vaw7L5b+xa3Rj4/pRtrQkymn3lSBRB/NAEdbF9YEVLA=
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=
@@ -546,8 +543,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q=
github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
@@ -611,8 +606,6 @@ github.com/securego/gosec/v2 v2.22.3 h1:mRrCNmRF2NgZp4RJ8oJ6yPJ7G4x6OCiAXHd8x4tr
github.com/securego/gosec/v2 v2.22.3/go.mod h1:42M9Xs0v1WseinaB/BmNGO8AVqG8vRfhC2686ACY48k=
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/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -713,8 +706,6 @@ 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/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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=
@@ -726,8 +717,8 @@ go-simpler.org/sloglint v0.11.0/go.mod h1:CFDO8R1i77dlciGfPEPvYke2ZMx4eyGiEIWkye
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=
go.etcd.io/bbolt v1.4.1 h1:5mOV+HWjIPLEAlUGMsveaUvK2+byZMFOzojoi7bh7uI=
go.etcd.io/bbolt v1.4.1/go.mod h1:c8zu2BnXWTu2XM4XcICtbGSl9cFwsXtcf9zLt2OncM8=
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -886,7 +877,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -908,7 +898,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -925,7 +914,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
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.1.0/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=
+5 -9
View File
@@ -91,14 +91,7 @@ func setupProxyUser(r *http.Request, data *requestContext, proxyUser string) (*u
}
}
if data.user.LoginMethod != users.LoginMethodProxy {
logger.Warningf("user %s is not allowed to login with proxy authentication, bypassing and updating login method", data.user.Username)
data.user.LoginMethod = users.LoginMethodProxy
// Perform the user update
err := store.Users.Update(data.user, true, "LoginMethod")
if err != nil {
logger.Debug(err.Error())
}
//return nil, fmt.Errorf("user %s is not allowed to login with proxy authentication", proxyUser)
return nil, errors.ErrWrongLoginMethod
}
return data.user, nil
}
@@ -143,7 +136,10 @@ func logoutHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (i
logoutUrl = oidcRedirectUrl
}
}
if logoutUrl == "" {
logger.Debug("no logout url found, using default")
logoutUrl = fmt.Sprintf("%vlogin", config.Server.BaseURL)
}
response := map[string]string{
"logoutUrl": logoutUrl,
}
+1 -1
View File
@@ -105,7 +105,7 @@ func StartHttp(ctx context.Context, storage *storage.Storage, shutdownComplete c
api.HandleFunc("DELETE /share", withPermShare(shareDeleteHandler))
// Public routes
api.HandleFunc("GET /public/publicUser", publicUserGetHandler)
api.HandleFunc("GET /public/publicUser", withoutUser(publicUserGetHandler))
api.HandleFunc("GET /public/dl", withHashFile(publicRawHandler))
api.HandleFunc("GET /public/share", withHashFile(publicShareHandler))
api.HandleFunc("GET /public/preview", withHashFile(publicPreviewHandler))
+1 -2
View File
@@ -22,7 +22,6 @@ import (
type requestContext struct {
user *users.User
raw interface{}
fileInfo iteminfo.ExtendedFileInfo
path string
token string
@@ -84,7 +83,7 @@ func withHashFileHelper(fn handleFunc) handleFunc {
return errToStatus(err), fmt.Errorf("error fetching share from server")
}
// Set the file info in the `data` object
data.raw = file
data.fileInfo = file
// Call the next handler with the data
return fn(w, r, data)
}
+2 -7
View File
@@ -10,6 +10,7 @@ import (
"time"
"github.com/coreos/go-oidc/v3/oidc"
"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/storage"
@@ -262,13 +263,7 @@ func loginWithOidcUser(w http.ResponseWriter, r *http.Request, username string,
}
}
if user.LoginMethod != users.LoginMethodOidc {
logger.Warningf("user %s is not allowed to login with oidc authentication, bypassing and updating login method", user.Username)
user.LoginMethod = users.LoginMethodOidc
err = store.Users.Update(user, true, "LoginMethod")
if err != nil {
logger.Debug(err.Error())
}
//return http.StatusForbidden, fmt.Errorf("user %s is not allowed to login with OIDC", username)
return http.StatusForbidden, errors.ErrWrongLoginMethod
}
signed, err := makeSignedTokenAPI(user, "WEB_TOKEN_"+utils.InsecureRandomIdentifier(4), time.Hour*time.Duration(config.Auth.TokenExpirationHours), user.Permissions)
if err != nil {
-3
View File
@@ -140,9 +140,6 @@ func onlyofficeCallbackHandler(w http.ResponseWriter, r *http.Request, d *reques
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
}
source := pathParts[0]
if source == "" {
source = settings.Config.Server.DefaultSource.Name
}
// Decode the URL-encoded path
path, err := url.QueryUnescape(pathParts[1])
if err != nil {
+5 -9
View File
@@ -46,15 +46,11 @@ func previewHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (
}
path := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = settings.Config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
if path == "" {
return http.StatusBadRequest, fmt.Errorf("invalid request path")
+7 -32
View File
@@ -8,7 +8,6 @@ import (
"strings"
"github.com/gtsteffaniak/filebrowser/backend/adapters/fs/files"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
"github.com/gtsteffaniak/filebrowser/backend/database/users"
"github.com/gtsteffaniak/filebrowser/backend/indexing/iteminfo"
@@ -40,14 +39,9 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
}
fileInfo, ok := d.raw.(iteminfo.ExtendedFileInfo)
if !ok {
logger.Error("public share handler: failed to assert type files.FileInfo")
return http.StatusInternalServerError, fmt.Errorf("failed to assert type files.FileInfo")
}
fileList := []string{}
for _, file := range strings.Split(f, "||") {
fileList = append(fileList, fileInfo.Source+"::"+fileInfo.Path+file)
fileList = append(fileList, d.fileInfo.Source+"::"+d.fileInfo.Path+file)
}
var status int
status, err = rawFilesHandler(w, r, d, fileList)
@@ -59,21 +53,12 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
}
func publicShareHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
file, ok := d.raw.(iteminfo.ExtendedFileInfo)
if !ok {
return http.StatusInternalServerError, fmt.Errorf("failed to assert type iteminfo.FileInfo")
}
file.Path = strings.TrimPrefix(file.Path, d.share.Path)
return renderJSON(w, r, file)
d.fileInfo.Path = strings.TrimPrefix(d.fileInfo.Path, d.share.Path)
return renderJSON(w, r, d.fileInfo)
}
func publicUserGetHandler(w http.ResponseWriter, r *http.Request) {
// Call the actual handler logic here (e.g., renderJSON, etc.)
// You may need to replace `fn` with the actual handler logic.
status, err := renderJSON(w, r, users.PublicUser)
if err != nil {
http.Error(w, http.StatusText(status), status)
}
func publicUserGetHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
return renderJSON(w, r, users.PublicUser)
}
// health godoc
@@ -115,24 +100,14 @@ func publicPreviewHandler(w http.ResponseWriter, r *http.Request, d *requestCont
return http.StatusNotImplemented, fmt.Errorf("preview is disabled")
}
path := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = settings.Config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
}
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: d.user.Permissions.Modify,
Source: source,
Source: d.fileInfo.Source,
Expand: true,
})
if err != nil {
+19 -38
View File
@@ -28,7 +28,7 @@ import (
// @Accept json
// @Produce json
// @Param path query string true "Path to the resource"
// @Param source query string false "Source name for the desired source, default is used if not provided"
// @Param source query string true "Source name for the desired source, default is used if not provided"
// @Param content query string false "Include file content if true"
// @Param checksum query string false "Optional checksum validation"
// @Success 200 {object} iteminfo.FileInfo "Resource metadata"
@@ -38,9 +38,6 @@ import (
func resourceGetHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
encodedPath := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
}
// Decode the URL-encoded path
path, err := url.QueryUnescape(encodedPath)
if err != nil {
@@ -95,7 +92,7 @@ func resourceGetHandler(w http.ResponseWriter, r *http.Request, d *requestContex
// @Accept json
// @Produce json
// @Param path query string true "Path to the resource"
// @Param source query string false "Source name for the desired source, default is used if not provided"
// @Param source query string true "Source name for the desired source, default is used if not provided"
// @Success 200 "Resource deleted successfully"
// @Failure 403 {object} map[string]string "Forbidden"
// @Failure 404 {object} map[string]string "Resource not found"
@@ -105,15 +102,11 @@ func resourceDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestCon
// TODO source := r.URL.Query().Get("source")
encodedPath := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
// Decode the URL-encoded path
path, err := url.QueryUnescape(encodedPath)
@@ -155,7 +148,7 @@ func resourceDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestCon
// @Accept json
// @Produce json
// @Param path query string true "url encoded destination path where to place the files inside the destination source, a directory must end in / to create a directory"
// @Param source query string false "Name for the desired filebrowser destination source name, default is used if not provided"
// @Param source query string true "Name for the desired filebrowser destination source name, default is used if not provided"
// @Param override query bool false "Override existing file if true"
// @Success 200 "Resource created successfully"
// @Failure 403 {object} map[string]string "Forbidden"
@@ -166,17 +159,12 @@ func resourceDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestCon
func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
path := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
fmt.Println("resourcePostHandler: source:", source, "path:", path)
if !d.user.Permissions.Modify {
return http.StatusForbidden, fmt.Errorf("user is not allowed to create or modify")
}
@@ -227,7 +215,7 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
// @Accept json
// @Produce json
// @Param path query string true "Destination path where to place the files inside the destination source"
// @Param source query string false "Source name for the desired source, default is used if not provided"
// @Param source query string true "Source name for the desired source, default is used if not provided"
// @Success 200 "Resource updated successfully"
// @Failure 403 {object} map[string]string "Forbidden"
// @Failure 404 {object} map[string]string "Resource not found"
@@ -236,15 +224,11 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
// @Router /api/resources [put]
func resourcePutHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
if !d.user.Permissions.Modify {
return http.StatusForbidden, fmt.Errorf("user is not allowed to create or modify")
@@ -421,9 +405,6 @@ func patchAction(ctx context.Context, action, src, dst string, d *requestContext
func inspectIndex(w http.ResponseWriter, r *http.Request) {
encodedPath := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
}
// Decode the URL-encoded path
path, _ := url.QueryUnescape(encodedPath)
isNotDir := r.URL.Query().Get("isDir") == "false" // default to isDir true
+1 -3
View File
@@ -50,6 +50,7 @@ import (
// @Accept json
// @Produce json
// @Param query query string true "Search query"
// @Param source query string true "Source name for the desired source"
// @Param scope query string false "path within user scope to search, for example '/first/second' to search within the second directory only"
// @Param SessionId header string false "User session ID, add unique value to prevent collisions"
// @Success 200 {array} indexing.SearchResult "List of search results"
@@ -58,9 +59,6 @@ import (
func searchHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
query := r.URL.Query().Get("query")
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
}
scope := r.URL.Query().Get("scope")
unencodedScope, err := url.QueryUnescape(scope)
if err != nil {
-6
View File
@@ -68,9 +68,6 @@ func shareListHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
func shareGetHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
encodedPath := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
if source == "" {
source = settings.Config.Server.DefaultSource.Name
}
// Decode the URL-encoded path
path, err := url.QueryUnescape(encodedPath)
if err != nil {
@@ -194,9 +191,6 @@ func sharePostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
}
sourceName := r.URL.Query().Get("source")
if sourceName == "" {
sourceName = config.Server.DefaultSource.Name
}
source := config.Server.NameToSource[sourceName]
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source.Name)
if err != nil {
+1
View File
@@ -61,6 +61,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentT
"MediaAvailable": config.Integrations.Media.FfmpegPath != "",
"MuPdfAvailable": config.Server.MuPdfAvailable,
"UpdateAvailable": utils.GetUpdateAvailableUrl(),
"DisableNavButtons": settings.Config.Frontend.DisableNavButtons,
}
b, err := json.Marshal(data)
+33 -14
View File
@@ -15,9 +15,8 @@ import (
)
type UserRequest struct {
What string `json:"what"`
Which []string `json:"which"`
Data *users.User `json:"data"`
Which []string `json:"which"`
User users.User `json:"data"`
}
// userGetHandler retrieves a user by ID.
@@ -153,20 +152,24 @@ func usersPostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
}
r.Body.Close()
if req.User.Username == "" {
return http.StatusBadRequest, errors.ErrEmptyUsername
}
if len(req.Which) != 0 {
return http.StatusBadRequest, nil
}
if req.Data.Password == "" && req.Data.LoginMethod == "password" {
if req.User.Password == "" && req.User.LoginMethod == "password" {
return http.StatusBadRequest, errors.ErrEmptyPassword
}
err = storage.CreateUser(*req.Data, req.Data.Permissions.Admin)
err = storage.CreateUser(req.User, req.User.Permissions.Admin)
if err != nil {
return http.StatusInternalServerError, err
}
w.Header().Set("Location", "/settings/users/"+strconv.FormatUint(uint64(req.Data.ID), 10))
w.Header().Set("Location", "/settings/users/"+strconv.FormatUint(uint64(req.User.ID), 10))
return http.StatusCreated, nil
}
@@ -176,7 +179,8 @@ func usersPostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
// @Tags Users
// @Accept json
// @Produce json
// @Param id query string true "User ID"
// @Param id query string false "user ID to update"
// @Param id query string false "usename to update"
// @Param data body users.User true "User data to update"
// @Success 200 {object} users.User "Updated user details"
// @Failure 400 {object} map[string]string "Bad Request"
@@ -185,6 +189,7 @@ func usersPostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
// @Router /api/users [put]
func userPutHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
givenUserIdString := r.URL.Query().Get("id")
username := r.URL.Query().Get("username")
num, _ := strconv.ParseUint(givenUserIdString, 10, 32)
givenUserId := uint(num)
@@ -204,18 +209,32 @@ func userPutHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (
if err = json.Unmarshal(body, &req); err != nil {
return http.StatusBadRequest, err
}
if !req.Data.OtpEnabled {
req.Data.TOTPSecret = ""
req.Data.TOTPNonce = ""
if givenUserId != 0 {
u, err2 := store.Users.Get(givenUserId)
if err2 != nil {
return http.StatusBadRequest, fmt.Errorf("no user not found, please provide a valid id or username")
}
req.User.ID = u.ID
req.User.Username = u.Username
} else {
u, err2 := store.Users.Get(username)
if err2 != nil {
return http.StatusBadRequest, fmt.Errorf("no user not found, please provide a valid id or username")
}
req.User.ID = u.ID
req.User.Username = u.Username
}
if !req.User.OtpEnabled {
req.User.TOTPSecret = ""
req.User.TOTPNonce = ""
}
// Perform the user update
err = store.Users.Update(req.Data, d.user.Permissions.Admin, req.Which...)
err = store.Users.Update(&req.User, d.user.Permissions.Admin, req.Which...)
if err != nil {
return http.StatusBadRequest, err
}
// Return the updated user (with the password hidden) as JSON response
req.Data.Password = ""
return renderJSON(w, r, req.Data)
req.User.Password = ""
return renderJSON(w, r, req.User)
}
+13
View File
@@ -3,7 +3,20 @@
package indexing
import "syscall"
func CheckWindowsHidden(realpath string) bool {
// Non-Windows platforms don't support hidden attributes in the same way
return false
}
func getPartitionSize(path string) (uint64, error) {
var stat syscall.Statfs_t
err := syscall.Statfs(path, &stat)
if err != nil {
return 0, err
}
// Total size in bytes: Blocks * Block size
total := stat.Blocks * uint64(stat.Bsize)
return total, nil
}
+13
View File
@@ -31,3 +31,16 @@ func CheckWindowsHidden(realpath string) bool {
}
return false
}
func getPartitionSize(path string) (uint64, error) {
pathPtr, err := windows.UTF16PtrFromString(path)
if err != nil {
return 0, err
}
var freeBytes, totalBytes, totalFreeBytes uint64
err = windows.GetDiskFreeSpaceEx(pathPtr, &freeBytes, &totalBytes, &totalFreeBytes)
if err != nil {
return 0, err
}
return totalBytes, nil
}
+24 -9
View File
@@ -23,8 +23,8 @@ var RealPathCache = cache.NewCache(48*time.Hour, 72*time.Hour)
// reduced index is json exposed to the client
type ReducedIndex struct {
IdxName string `json:"name"`
DiskUsed int64 `json:"used"`
DiskTotal int64 `json:"total"`
DiskUsed uint64 `json:"used"`
DiskTotal uint64 `json:"total"`
Status IndexStatus `json:"status"`
NumDirs uint64 `json:"numDirs"`
NumFiles uint64 `json:"numFiles"`
@@ -168,10 +168,7 @@ func (idx *Index) GetFsDirInfo(adjustedPath string) (*iteminfo.FileInfo, error)
return nil, err
}
originalPath := realPath
if !isDir {
// get parent directory info
realPath = filepath.Dir(realPath)
}
dir, err := os.Open(realPath)
if err != nil {
return nil, err
@@ -182,6 +179,19 @@ func (idx *Index) GetFsDirInfo(adjustedPath string) (*iteminfo.FileInfo, error)
if err != nil {
return nil, err
}
if !dirInfo.IsDir() {
fileInfo := iteminfo.FileInfo{
Path: adjustedPath,
ItemInfo: iteminfo.ItemInfo{
Name: filepath.Base(originalPath),
Size: dirInfo.Size(),
ModTime: dirInfo.ModTime(),
},
}
fileInfo.DetectType(adjustedPath, false)
return &fileInfo, nil
}
combinedPath := adjustedPath + "/"
if adjustedPath == "/" {
combinedPath = "/"
@@ -291,6 +301,12 @@ func (idx *Index) GetDirInfo(dirInfo *os.File, stat os.FileInfo, realPath, adjus
return nil, errors.ErrNotIndexed
}
if adjustedPath == "/" {
idx.mu.Lock()
idx.DiskUsed = uint64(totalSize)
idx.mu.Unlock()
}
// Create FileInfo for the current directory
dirFileInfo := &iteminfo.FileInfo{
Path: adjustedPath,
@@ -500,14 +516,13 @@ type DiskUsage struct {
Used uint64 `json:"used"`
}
func (idx *Index) SetUsage(usage DiskUsage) {
func (idx *Index) SetUsage(totalBytes uint64) {
if settings.Config.Frontend.DisableUsedPercentage {
return
}
idx.mu.Lock()
defer idx.mu.Unlock()
idx.DiskUsed = int64(usage.Used)
idx.DiskTotal = int64(usage.Total)
idx.DiskTotal = totalBytes
}
func (idx *Index) SetStatus(status IndexStatus) {
+3 -8
View File
@@ -8,8 +8,6 @@ import (
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
"github.com/gtsteffaniak/filebrowser/backend/indexing/iteminfo"
"github.com/gtsteffaniak/go-logger/logger"
"github.com/shirou/gopsutil/v3/disk"
)
// UpdateFileMetadata updates the FileInfo for the specified directory in the index.
@@ -100,17 +98,14 @@ func GetIndexInfo(sourceName string) (ReducedIndex, error) {
cacheKey := "usageCache-" + sourceName
_, ok = utils.DiskUsageCache.Get(cacheKey).(bool)
if !ok {
usage, err := disk.Usage(sourcePath)
totalBytes, err := getPartitionSize(sourcePath)
if err != nil {
logger.Errorf("error getting disk usage for %s: %v", sourcePath, err)
idx.SetStatus(UNAVAILABLE)
return ReducedIndex{}, fmt.Errorf("error getting disk usage for %s: %v", sourcePath, err)
}
latestUsage := DiskUsage{
Total: usage.Total,
Used: usage.Used,
}
idx.SetUsage(latestUsage)
idx.SetUsage(totalBytes)
utils.DiskUsageCache.Set(cacheKey, true)
}
return idx.ReducedIndex, nil
+61 -10
View File
@@ -703,7 +703,8 @@ const docTemplate = `{
"type": "string",
"description": "Source name for the desired source, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
},
{
"type": "string",
@@ -769,7 +770,8 @@ const docTemplate = `{
"type": "string",
"description": "Source name for the desired source, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
}
],
"responses": {
@@ -838,7 +840,8 @@ const docTemplate = `{
"type": "string",
"description": "Name for the desired filebrowser destination source name, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
},
{
"type": "boolean",
@@ -913,7 +916,8 @@ const docTemplate = `{
"type": "string",
"description": "Source name for the desired source, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
}
],
"responses": {
@@ -1060,6 +1064,13 @@ const docTemplate = `{
"in": "query",
"required": true
},
{
"type": "string",
"description": "Source name for the desired source",
"name": "source",
"in": "query",
"required": true
},
{
"type": "string",
"description": "path within user scope to search, for example '/first/second' to search within the second directory only",
@@ -1454,10 +1465,15 @@ const docTemplate = `{
"parameters": [
{
"type": "string",
"description": "User ID",
"description": "user ID to update",
"name": "id",
"in": "query",
"required": true
"in": "query"
},
{
"type": "string",
"description": "usename to update",
"name": "id",
"in": "query"
},
{
"description": "User data to update",
@@ -1995,6 +2011,10 @@ const docTemplate = `{
"description": "disable default links in the sidebar",
"type": "boolean"
},
"disableNavButtons": {
"description": "disable the nav buttons in the sidebar",
"type": "boolean"
},
"disableUsedPercentage": {
"description": "disable used percentage for the sources in the sidebar",
"type": "boolean"
@@ -2173,9 +2193,8 @@ const docTemplate = `{
"type": "boolean"
},
"minLength": {
"description": "minimum pasword length required.",
"type": "integer",
"minimum": 5
"description": "minimum pasword length required, default is 5.",
"type": "integer"
},
"recaptcha": {
"description": "recaptcha config, only used if signup is enabled",
@@ -2412,6 +2431,10 @@ const docTemplate = `{
"description": "when false, the date is relative, when true, the date is an exact timestamp",
"type": "boolean"
},
"deleteWithoutConfirming": {
"description": "delete files without confirmation",
"type": "boolean"
},
"disableOfficePreviewExt": {
"description": "comma separated list of file extensions to disable office preview for",
"type": "string"
@@ -2420,6 +2443,14 @@ const docTemplate = `{
"description": "comma separated list of file extensions to disable onlyoffice preview for",
"type": "string"
},
"disableQuickToggles": {
"description": "disable the quick toggles in the sidebar",
"type": "boolean"
},
"disableSearchOptions": {
"description": "disable the search options in the search bar",
"type": "boolean"
},
"disableSettings": {
"description": "disable the user from viewing the settings page",
"type": "boolean"
@@ -2432,6 +2463,10 @@ const docTemplate = `{
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
},
"hideSidebarFileActions": {
"description": "hide the file actions in the sidebar",
"type": "boolean"
},
"locale": {
"description": "language to use: eg. de, en, or fr",
"type": "string"
@@ -2635,6 +2670,10 @@ const docTemplate = `{
"description": "when false, the date is relative, when true, the date is an exact timestamp",
"type": "boolean"
},
"deleteWithoutConfirming": {
"description": "delete files without confirmation",
"type": "boolean"
},
"disableOfficePreviewExt": {
"description": "comma separated list of file extensions to disable office preview for",
"type": "string"
@@ -2643,6 +2682,14 @@ const docTemplate = `{
"description": "comma separated list of file extensions to disable onlyoffice preview for",
"type": "string"
},
"disableQuickToggles": {
"description": "disable the quick toggles in the sidebar",
"type": "boolean"
},
"disableSearchOptions": {
"description": "disable the search options in the search bar",
"type": "boolean"
},
"disableSettings": {
"type": "boolean"
},
@@ -2654,6 +2701,10 @@ const docTemplate = `{
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
},
"hideSidebarFileActions": {
"description": "hide the file actions in the sidebar",
"type": "boolean"
},
"id": {
"type": "integer"
},
+61 -10
View File
@@ -692,7 +692,8 @@
"type": "string",
"description": "Source name for the desired source, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
},
{
"type": "string",
@@ -758,7 +759,8 @@
"type": "string",
"description": "Source name for the desired source, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
}
],
"responses": {
@@ -827,7 +829,8 @@
"type": "string",
"description": "Name for the desired filebrowser destination source name, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
},
{
"type": "boolean",
@@ -902,7 +905,8 @@
"type": "string",
"description": "Source name for the desired source, default is used if not provided",
"name": "source",
"in": "query"
"in": "query",
"required": true
}
],
"responses": {
@@ -1049,6 +1053,13 @@
"in": "query",
"required": true
},
{
"type": "string",
"description": "Source name for the desired source",
"name": "source",
"in": "query",
"required": true
},
{
"type": "string",
"description": "path within user scope to search, for example '/first/second' to search within the second directory only",
@@ -1443,10 +1454,15 @@
"parameters": [
{
"type": "string",
"description": "User ID",
"description": "user ID to update",
"name": "id",
"in": "query",
"required": true
"in": "query"
},
{
"type": "string",
"description": "usename to update",
"name": "id",
"in": "query"
},
{
"description": "User data to update",
@@ -1984,6 +2000,10 @@
"description": "disable default links in the sidebar",
"type": "boolean"
},
"disableNavButtons": {
"description": "disable the nav buttons in the sidebar",
"type": "boolean"
},
"disableUsedPercentage": {
"description": "disable used percentage for the sources in the sidebar",
"type": "boolean"
@@ -2162,9 +2182,8 @@
"type": "boolean"
},
"minLength": {
"description": "minimum pasword length required.",
"type": "integer",
"minimum": 5
"description": "minimum pasword length required, default is 5.",
"type": "integer"
},
"recaptcha": {
"description": "recaptcha config, only used if signup is enabled",
@@ -2401,6 +2420,10 @@
"description": "when false, the date is relative, when true, the date is an exact timestamp",
"type": "boolean"
},
"deleteWithoutConfirming": {
"description": "delete files without confirmation",
"type": "boolean"
},
"disableOfficePreviewExt": {
"description": "comma separated list of file extensions to disable office preview for",
"type": "string"
@@ -2409,6 +2432,14 @@
"description": "comma separated list of file extensions to disable onlyoffice preview for",
"type": "string"
},
"disableQuickToggles": {
"description": "disable the quick toggles in the sidebar",
"type": "boolean"
},
"disableSearchOptions": {
"description": "disable the search options in the search bar",
"type": "boolean"
},
"disableSettings": {
"description": "disable the user from viewing the settings page",
"type": "boolean"
@@ -2421,6 +2452,10 @@
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
},
"hideSidebarFileActions": {
"description": "hide the file actions in the sidebar",
"type": "boolean"
},
"locale": {
"description": "language to use: eg. de, en, or fr",
"type": "string"
@@ -2624,6 +2659,10 @@
"description": "when false, the date is relative, when true, the date is an exact timestamp",
"type": "boolean"
},
"deleteWithoutConfirming": {
"description": "delete files without confirmation",
"type": "boolean"
},
"disableOfficePreviewExt": {
"description": "comma separated list of file extensions to disable office preview for",
"type": "string"
@@ -2632,6 +2671,14 @@
"description": "comma separated list of file extensions to disable onlyoffice preview for",
"type": "string"
},
"disableQuickToggles": {
"description": "disable the quick toggles in the sidebar",
"type": "boolean"
},
"disableSearchOptions": {
"description": "disable the search options in the search bar",
"type": "boolean"
},
"disableSettings": {
"type": "boolean"
},
@@ -2643,6 +2690,10 @@
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
},
"hideSidebarFileActions": {
"description": "hide the file actions in the sidebar",
"type": "boolean"
},
"id": {
"type": "integer"
},
+42 -4
View File
@@ -207,6 +207,9 @@ definitions:
disableDefaultLinks:
description: disable default links in the sidebar
type: boolean
disableNavButtons:
description: disable the nav buttons in the sidebar
type: boolean
disableUsedPercentage:
description: disable used percentage for the sources in the sidebar
type: boolean
@@ -342,8 +345,7 @@ definitions:
Otherwise, users can choose to enable TOTP.
type: boolean
minLength:
description: minimum pasword length required.
minimum: 5
description: minimum pasword length required, default is 5.
type: integer
recaptcha:
allOf:
@@ -517,6 +519,9 @@ definitions:
description: when false, the date is relative, when true, the date is an exact
timestamp
type: boolean
deleteWithoutConfirming:
description: delete files without confirmation
type: boolean
disableOfficePreviewExt:
description: comma separated list of file extensions to disable office preview
for
@@ -525,6 +530,12 @@ definitions:
description: comma separated list of file extensions to disable onlyoffice
preview for
type: string
disableQuickToggles:
description: disable the quick toggles in the sidebar
type: boolean
disableSearchOptions:
description: disable the search options in the search bar
type: boolean
disableSettings:
description: disable the user from viewing the settings page
type: boolean
@@ -534,6 +545,9 @@ definitions:
gallerySize:
description: 0-9 - the size of the gallery thumbnails
type: integer
hideSidebarFileActions:
description: hide the file actions in the sidebar
type: boolean
locale:
description: 'language to use: eg. de, en, or fr'
type: string
@@ -682,6 +696,9 @@ definitions:
description: when false, the date is relative, when true, the date is an exact
timestamp
type: boolean
deleteWithoutConfirming:
description: delete files without confirmation
type: boolean
disableOfficePreviewExt:
description: comma separated list of file extensions to disable office preview
for
@@ -690,6 +707,12 @@ definitions:
description: comma separated list of file extensions to disable onlyoffice
preview for
type: string
disableQuickToggles:
description: disable the quick toggles in the sidebar
type: boolean
disableSearchOptions:
description: disable the search options in the search bar
type: boolean
disableSettings:
type: boolean
disableUpdateNotifications:
@@ -698,6 +721,9 @@ definitions:
gallerySize:
description: 0-9 - the size of the gallery thumbnails
type: integer
hideSidebarFileActions:
description: hide the file actions in the sidebar
type: boolean
id:
type: integer
locale:
@@ -1215,6 +1241,7 @@ paths:
- description: Source name for the desired source, default is used if not provided
in: query
name: source
required: true
type: string
produces:
- application/json
@@ -1256,6 +1283,7 @@ paths:
- description: Source name for the desired source, default is used if not provided
in: query
name: source
required: true
type: string
- description: Include file content if true
in: query
@@ -1363,6 +1391,7 @@ paths:
is used if not provided
in: query
name: source
required: true
type: string
- description: Override existing file if true
in: query
@@ -1414,6 +1443,7 @@ paths:
- description: Source name for the desired source, default is used if not provided
in: query
name: source
required: true
type: string
produces:
- application/json
@@ -1459,6 +1489,11 @@ paths:
name: query
required: true
type: string
- description: Source name for the desired source
in: query
name: source
required: true
type: string
- description: path within user scope to search, for example '/first/second'
to search within the second directory only
in: query
@@ -1788,10 +1823,13 @@ paths:
- application/json
description: Updates the details of a user identified by ID.
parameters:
- description: User ID
- description: user ID to update
in: query
name: id
type: string
- description: usename to update
in: query
name: id
required: true
type: string
- description: User data to update
in: body
+8 -3
View File
@@ -1,5 +1,5 @@
server:
numImageProcessors: 8 # number of concurrent image processing jobs used to create previews, default is number of cpu cores available.
numImageProcessors: 16 # 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
tlsCert: "" # path to TLS cert
@@ -38,7 +38,7 @@ server:
rootFolders: []
rootFiles: []
defaultUserScope: / # default "/" should match folders under path
defaultEnabled: true # should be added as a default source for new users?
defaultEnabled: false # should be added as a default source for new users?
createUserDir: false # create a user directory for each user
externalUrl: "" # used by share links if set
internalUrl: "" # used by integrations if set, this is the url that an integration service will use to communicate with filebrowser
@@ -55,7 +55,7 @@ auth:
noauth: false # if set to true, overrides all other auth methods and disables authentication
password: # validate:omitempty
enabled: true
minLength: 5 # minimum pasword length required. validate:omitempty,min=5
minLength: 5 # minimum pasword length required, default is 5. validate:omitempty
signup: false # allow signups on login page if enabled -- not secure. validate:omitempty
recaptcha: # recaptcha config, only used if signup is enabled validate:omitempty
host: "" # validate:required
@@ -88,7 +88,11 @@ frontend:
- 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
disableNavButtons: false # disable the nav buttons in the sidebar
userDefaults:
hideSidebarFileActions: false # hide the file actions in the sidebar
disableQuickToggles: false # disable the quick toggles in the sidebar
disableSearchOptions: false # disable the search options in the search bar
stickySidebar: true # keep sidebar open when navigating
darkMode: true # should dark mode be enabled
locale: en # language to use: eg. de, en, or fr
@@ -120,6 +124,7 @@ userDefaults:
realtime: false
loginMethod: password # login method to use: eg. password, proxy, oidc
disableUpdateNotifications: false # disable update notifications banner for admin users
deleteWithoutConfirming: false # delete files without confirmation
integrations:
office: # validate:omitempty
url: "" # The URL to the OnlyOffice Document Server, needs to be accessible to the user. validate:required
+17 -1
View File
@@ -22,8 +22,22 @@ if (!DEEPL_API_KEY) {
const translator = new deepl.Translator(DEEPL_API_KEY);
const deeplLangMap = {
'zh-cn': 'ZH',
'pt': 'PT-PT', // or 'PT-BR' if you want Brazilian Portuguese
'pt-br': 'PT-BR',
'en': 'EN',
'en-us': 'EN-US',
'en-gb': 'EN-GB',
'sv-se': 'SV',
'ua': 'UK',
'nl-be': 'NL',
// Add more as needed
};
// --- Translation Function using DeepL ---
async function translateText(text, targetLanguage, keyPath = '') {
if (!text || typeof text !== 'string' || text.trim() === '') {
console.warn(`Skipping translation for empty or non-string text: "${text}"`);
return text;
@@ -41,7 +55,9 @@ async function translateText(text, targetLanguage, keyPath = '') {
try {
console.log(`Translating "${text}" from '${masterLanguageCode}' to '${targetLanguage}'...`);
const result = await translator.translateText(text, masterLanguageCode, targetLanguage.toUpperCase());
let deeplTargetLang = deeplLangMap[targetLanguage.toLowerCase()] || targetLanguage.toUpperCase();
const result = await translator.translateText(text, masterLanguageCode, deeplTargetLang);
// Delay to avoid rate-limiting
await new Promise(resolve => setTimeout(resolve, 100));
+2 -2
View File
@@ -1,6 +1,6 @@
import { fetchURL, adjustedData } from './utils'
import { getApiPath, extractSourceFromPath,removePrefix } from '@/utils/url.js'
import { state } from '@/store'
import { state, mutations } from '@/store'
import { notify } from '@/notify'
import { externalUrl,baseURL,serverHasMultipleSources } from '@/utils/constants'
@@ -184,6 +184,7 @@ export async function moveCopy(
notify.showSuccess(
action === 'copy' ? 'Resources copied successfully' : 'Resources moved successfully'
)
mutations.setReload(true);
} catch (err) {
notify.showError(err.message || 'Error moving/copying resources')
throw err // Re-throw the error to propagate it back to the caller
@@ -215,7 +216,6 @@ export function getDownloadURL(source, path, inline, useExternal) {
}
const apiPath = getApiPath('api/raw', params)
if (externalUrl && useExternal) {
return externalUrl + removePrefix(apiPath, baseURL)
}
return window.origin + apiPath
+1 -1
View File
@@ -171,7 +171,7 @@ export async function create (user) {
}
}
export async function update (user, which = ['all']) {
export async function update(user, which = ['all']) {
// List of keys to exclude from the "which" array
const excludeKeys = ['id', 'name']
// Filter out the keys from "which"
+11 -2
View File
@@ -49,7 +49,7 @@
show="info"
/>
<action
v-if="!isMultiple && !isSearchActive"
v-if="!isMultiple && !isSearchActive && isMobileDevice"
icon="check_circle"
:label="$t('buttons.selectMultiple')"
@action="toggleMultipleSelection"
@@ -132,7 +132,16 @@ export default {
showCreate: false,
};
},
props: {
showCentered: {
type: Boolean,
default: false,
},
},
computed: {
isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || getters.isMobile();
},
noItems() {
return !this.showEdit && !this.showSave && !this.showDelete;
},
@@ -190,7 +199,7 @@ export default {
return state.user;
},
centered() {
return getters.isMobile() || !this.posX || !this.posY;
return this.showCentered || !this.posX || !this.posY;
},
top() {
// Ensure the context menu stays within the viewport
+4 -1
View File
@@ -54,7 +54,7 @@
</div>
<div id="result-list">
<div>
<div v-if="!disableSearchOptions">
<div v-if="active">
<div v-if="isMobile">
<ButtonGroup
@@ -264,6 +264,9 @@ export default {
}
},
computed: {
disableSearchOptions() {
return state.user.disableSearchOptions;
},
showOptions() {
return !this.hiddenOptions || !this.isMobile;
},
@@ -295,9 +295,6 @@ export default {
let action = async (overwrite, rename) => {
await filesApi.moveCopy(items, "move", overwrite, rename);
setTimeout(() => {
mutations.setReload(true);
}, 50);
};
let conflict = upload.checkConflict(items, response.items);
@@ -84,6 +84,7 @@ export default {
usersApi.deleteApiKey({ name: this.name });
notify.showSuccess("API key deleted!");
window.location.reload();
mutations.closeHovers();
} catch (error) {
notify.showError(error);
}
@@ -113,6 +113,7 @@ export default {
await usersApi.createApiKey(params);
notify.showSuccess($t("api.createKeySuccess"));
window.location.reload();
mutations.closeHovers();
} catch (error) {
notify.showError($t("api.createKeyFailed"));
}
+19 -20
View File
@@ -12,20 +12,12 @@
</div>
</div>
<div class="card-action">
<button
@click="closeHovers"
class="button button--flat button--grey"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')"
>
<button @click="closeHovers" class="button button--flat button--grey" :aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">
{{ $t("buttons.cancel") }}
</button>
<button
@click="submit"
class="button button--flat button--red"
aria-label="Confirm-Delete"
:title="$t('buttons.delete')"
>
<button @click="submit" class="button button--flat button--red" aria-label="Confirm-Delete"
:title="$t('buttons.delete')">
{{ $t("buttons.delete") }}
</button>
</div>
@@ -37,10 +29,18 @@ import { filesApi } from "@/api";
import buttons from "@/utils/buttons";
import { state, getters, mutations } from "@/store";
import { notify } from "@/notify";
import router from "@/router";
export default {
name: "delete",
mounted() {
const selectedCount = getters.selectedCount();
if (selectedCount > 0) {
if (state.user.deleteWithoutConfirming && getters.isSingleFileSelected()) {
this.submit();
}
}
},
computed: {
isListing() {
return getters.isListing();
@@ -62,22 +62,21 @@ export default {
return paths;
},
},
methods: {
closeHovers() {
mutations.closeHovers();
},
async submit() {
buttons.loading("delete");
try {
if (state.isSearchActive || getters.currentView() == "preview") {
const currentView = getters.currentView()
if (state.isSearchActive || currentView == "preview") {
await filesApi.remove(state.selected[0].url);
buttons.success("delete");
notify.showSuccess("Deleted item successfully");
mutations.closeHovers();
if (getters.currentView() == "preview") {
router.go(-1);
}
mutations.setDeletedItem(true);
return;
}
if (!this.isListing) {
@@ -100,10 +99,10 @@ export default {
for (let index of state.selected) {
promises.push(filesApi.remove(state.req.items[index].url));
}
mutations.resetSelected();
await Promise.all(promises);
buttons.success("delete");
notify.showSuccess("Deleted item successfully! reloading...");
notify.showSuccess(this.$t('prompts.deleted'));
mutations.setReload(true); // Handle reload as neededs
} catch (e) {
buttons.done("delete");
@@ -38,7 +38,6 @@
</template>
<script>
import { filesApi } from "@/api";
import url from "@/utils/url.js";
import { getters, mutations, state } from "@/store"; // Import your custom store
import { notify } from "@/notify";
@@ -39,7 +39,6 @@
<script>
import { state } from "@/store";
import { filesApi } from "@/api";
import url from "@/utils/url.js";
import { getters, mutations } from "@/store"; // Import your custom store
import { notify } from "@/notify";
@@ -103,8 +103,6 @@ export default {
this.$router.push({ path: newLink });
return;
}
mutations.setReload(true);
} catch (error) {
notify.showError(error);
}
+141 -53
View File
@@ -87,7 +87,7 @@
</div>
</div>
<div class="card-wrapper" @mouseleave="resetHoverTextToDefault">
<div v-if="!disableQuickToggles" class="card-wrapper" @mouseleave="resetHoverTextToDefault">
<div class="quick-toggles">
<div
:class="{ active: user?.singleClick }"
@@ -116,57 +116,81 @@
</div>
</div>
</div>
</div>
<!-- Section for logged-in users -->
<div v-if="loginCheck" class="sidebar-scroll-list">
<div class="sources card">
<span> {{ $t("sidebar.sources") }}</span>
<div class="inner-card">
<!-- My Files button -->
<button
v-for="(info, name) in sourceInfo"
:key="name"
class="action source-button"
:class="{ active: activeSource == name }"
@click="navigateTo('/files/' + info.pathPrefix)"
@mouseenter="updateSourceTooltip($event, info)"
@mouseleave="resetSourceTooltip"
:aria-label="$t('sidebar.myFiles')"
>
<div class="source-container">
<svg
class="realtime-pulse"
:class="{
active: realtimeActive,
danger: info.status != 'indexing' && info.status != 'ready',
warning: info.status == 'indexing',
ready: info.status == 'ready',
}"
>
<circle class="center" cx="50%" cy="50%" r="7px"></circle>
<circle class="pulse" cx="50%" cy="50%" r="10px"></circle>
</svg>
<span>{{ name }}</span>
</div>
<div v-if="hasSourceInfo" class="usage-info">
<ProgressBar
:val="info.usedPercentage"
text-position="inside"
:text="info.usedPercentage + '%'"
size="large"
text-fg-color="white"
></ProgressBar>
<div class="usage-info">
<span>
{{ getHumanReadableFilesize(info.used) }} {{ $t("index.of") }}
{{ getHumanReadableFilesize(info.total) }} {{ $t("index.used") }}
</span>
</div>
</div>
<!-- Sidebar file actions -->
<transition
name="expand"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<div v-if="!hideSidebarFileActions && isListingView" class="card-wrapper">
<button @click="openContextMenu" aria-label="File-Actions" class="action file-actions">
<i class="material-icons">add</i>
{{ $t("sidebar.fileActions") }}
</button>
</div>
</div>
</transition>
</div>
<!-- Section for logged-in users -->
<transition
name="expand"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<div v-if="loginCheck" class="sidebar-scroll-list">
<div class="sources card">
<span> {{ $t("sidebar.sources") }}</span>
<transition-group name="expand" tag="div" class="inner-card">
<button
v-for="(info, name) in sourceInfo"
:key="name"
class="action source-button"
:class="{ active: activeSource == name }"
@click="navigateTo('/files/' + info.pathPrefix)"
:aria-label="$t('sidebar.myFiles')"
>
<div class="source-container">
<svg
class="realtime-pulse"
:class="{
active: realtimeActive,
danger: info.status != 'indexing' && info.status != 'ready',
warning: info.status == 'indexing',
ready: info.status == 'ready',
}"
>
<circle class="center" cx="50%" cy="50%" r="7px"></circle>
<circle class="pulse" cx="50%" cy="50%" r="10px"></circle>
</svg>
<span>{{ name }}</span>
<i class="no-select material-symbols-outlined source-info-icon"
@mouseenter="updateSourceTooltip($event, info)"
@mouseleave="resetSourceTooltip">
info <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
</i>
</div>
<div v-if="hasSourceInfo" class="usage-info">
<ProgressBar
:val="info.usedPercentage"
text-position="inside"
:text="info.usedPercentage + '%'"
size="large"
text-fg-color="white"
></ProgressBar>
<div class="usage-info">
<span>
{{ getHumanReadableFilesize(info.used) }} {{ $t("index.of") }}
{{ getHumanReadableFilesize(info.total) }} {{ $t("index.used") }}
</span>
</div>
</div>
</button>
</transition-group>
</div>
</div>
</transition>
</template>
<script>
@@ -190,14 +214,14 @@ export default {
};
},
computed: {
hasSourceInfo() {
return state.sources.hasSourceInfo;
},
disableQuickToggles: () => state.user.disableQuickToggles,
hasSourceInfo: () => state.sources.hasSourceInfo,
hideSidebarFileActions: () => state.user.hideSidebarFileActions,
settingsAllowed: () => !state.user.disableSettings,
isSettings: () => getters.isSettings(),
isStickySidebar: () => getters.isStickySidebar(),
isMobile: () => getters.isMobile(),
isFiles: () => getters.isFiles(),
isListingView: () => getters.currentView() == "listingView",
user: () => (getters.isLoggedIn() ? state.user : {}),
isDarkMode: () => getters.isDarkMode(),
loginCheck: () => getters.isLoggedIn() && !getters.routePath().startsWith("/share"),
@@ -243,6 +267,15 @@ export default {
},
},
methods: {
openContextMenu() {
mutations.resetSelected();
mutations.showHover({
name: "ContextMenu",
props: {
showCentered: true,
},
});
},
getHumanReadableFilesize(size) {
return getHumanReadableFilesize(size);
},
@@ -295,6 +328,32 @@ export default {
// Logout the user
logout: auth.logout,
beforeEnter(el) {
el.style.height = '0';
el.style.opacity = '0';
},
enter(el, done) {
el.style.transition = '';
el.style.height = '0';
el.style.opacity = '0';
// Force reflow
void el.offsetHeight;
el.style.transition = 'height 0.3s, opacity 0.3s';
el.style.height = el.scrollHeight + 'px';
el.style.opacity = '1';
setTimeout(() => {
el.style.height = 'auto';
done();
}, 300);
},
leave(el, done) {
el.style.transition = 'height 0.3s, opacity 0.3s';
el.style.height = el.scrollHeight + 'px';
void el.offsetHeight;
el.style.height = '0';
el.style.opacity = '0';
setTimeout(done, 300);
},
},
};
</script>
@@ -570,4 +629,33 @@ button.action {
max-width: 13em;
padding-right: 1em !important;
}
.file-actions {
padding: 0.25em !important;
margin-top: 0.5em !important;
display: flex !important;
align-items: center;
justify-content: center;
}
.file-actions i {
padding: 0em !important;
}
.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;
}
.source-info-icon {
font-size: 1em !important;
padding: 0.1em !important;
padding-left: 0.5em !important;
}
</style>
+14 -5
View File
@@ -163,7 +163,8 @@
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "كلمة مرور اختيارية",
"typeName": "النوع:",
"selected": "مختارة"
"selected": "مختارة",
"deleted": "تم الحذف بنجاح!"
},
"search": {
"images": "الصور",
@@ -239,7 +240,6 @@
"rulesHelp": "يمكنك هنا تحديد مجموعة من شروط السماح والمنع لهذا المستخدم. الملفات الممنوعة لن تظهر ضمن القائمة لهذا المستخدم ولن يستطيع الوصول لها. هنا ندعم الـ regex والـ relative path لنطاق المستخدمين.\n",
"scope": "نطاق",
"settingsUpdated": "تم تعديل الإعدادات",
"shareDuration": "مدة المشاركة",
"shareManagement": "إدارة المشاركة",
"singleClick": "استخدام نقرات واحدة لفتح الملفات والدلائل",
"themes": {
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "قائمة مفصولة بمسافات لامتدادات الملفات لتعطيل عارض OnlyOffice لها. (على سبيل المثال، \".txt .html\").",
"disableOfficeEditor": "تعطيل عارض المكتب لامتدادات ملفات معينة",
"disableOfficePreviews": "تعطيل معاينات المكتب لامتدادات ملفات معينة",
"disableOfficePreviewsDescription": "قائمة مفصولة بمسافة من امتدادات الملفات لتعطيل المعاينات المكتبية لها. (على سبيل المثال، \".txt .html\")."
"disableOfficePreviewsDescription": "قائمة مفصولة بمسافة من امتدادات الملفات لتعطيل المعاينات المكتبية لها. (على سبيل المثال، \".txt .html\").",
"adminOptions": "خيارات المستخدم المسؤول",
"shareDuration": "تنتهي صلاحيتها",
"searchOptions": "خيارات البحث"
},
"sidebar": {
"help": "مساعدة",
@@ -302,7 +305,8 @@
"signup": "Signup",
"siteSettings": "إعدادات الموقع",
"sources": "المصادر",
"updateIsAvailable": "يتوفر تحديث!"
"updateIsAvailable": "يتوفر تحديث!",
"fileActions": "إجراءات الملف"
},
"success": {
"linkCopied": "تم نسخ الملف"
@@ -374,7 +378,12 @@
"editOffice": "تحرير/عرض ملفات المكتب",
"disableUpdateNotifications": "تعطيل إظهار شعار تحديث الإشعارات في الشريط الجانبي.",
"disableHideSidebar": "تعطيل إخفاء الشريط الجانبي تلقائيًا عند معاينة الملفات أو تحريرها.",
"autoplayMedia": "التشغيل التلقائي لملفات الوسائط في المعاينة"
"autoplayMedia": "التشغيل التلقائي لملفات الوسائط في المعاينة",
"deleteWithoutConfirming": "حذف الملفات بدون مطالبة التأكيد",
"sidebarOptions": "خيارات الشريط الجانبي",
"disableQuickToggles": "تعطيل التبديل السريع",
"disableSearchOptions": "تعطيل خيارات البحث",
"hideSidebarFileActions": "إخفاء إجراءات الملف في الشريط الجانبي"
},
"otp": {
"name": "المصادقة الثنائية",
+14 -5
View File
@@ -163,7 +163,8 @@
"deleteUserMessage": "Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?",
"filesSelected": "ausgewählte Dateien",
"typeName": "Art:",
"selected": "ausgewählt"
"selected": "ausgewählt",
"deleted": "Erfolgreich gelöscht!"
},
"search": {
"images": "Bilder",
@@ -244,7 +245,6 @@
"scope": "Scope",
"setDateFormat": "Exaktes Datumsformat setzen",
"settingsUpdated": "Einstellungen aktualisiert!",
"shareDuration": "Dauer",
"shareManagement": "Freigaben verwalten",
"shareDeleted": "Freigabe gelöscht!",
"singleClick": "Einfacher Klick zum Öffnen von Dateien und Ordnern",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Eine durch Leerzeichen getrennte Liste von Dateierweiterungen, für die der OnlyOffice-Viewer deaktiviert werden soll. (z. B. '.txt .html').",
"disableOfficeEditor": "Office Viewer für bestimmte Dateierweiterungen deaktivieren",
"disableOfficePreviews": "Office-Vorschauen für bestimmte Dateierweiterungen deaktivieren",
"disableOfficePreviewsDescription": "Eine durch Leerzeichen getrennte Liste von Dateierweiterungen, für die die Office-Vorschau deaktiviert werden soll. (z. B. '.txt .html')."
"disableOfficePreviewsDescription": "Eine durch Leerzeichen getrennte Liste von Dateierweiterungen, für die die Office-Vorschau deaktiviert werden soll. (z. B. '.txt .html').",
"adminOptions": "Optionen für Verwaltungsbenutzer",
"shareDuration": "Abgelaufen",
"searchOptions": "Suchoptionen"
},
"sidebar": {
"help": "Hilfe",
@@ -302,7 +305,8 @@
"signup": "Registrieren",
"siteSettings": "Seiteneinstellungen",
"sources": "Quellen",
"updateIsAvailable": "Ein Update ist verfügbar!"
"updateIsAvailable": "Ein Update ist verfügbar!",
"fileActions": "Datei-Aktionen"
},
"success": {
"linkCopied": "Link wurde kopiert!"
@@ -374,7 +378,12 @@
"editOffice": "Office-Dateien bearbeiten/anzeigen",
"disableUpdateNotifications": "Deaktivieren Sie die Anzeige des Banners für Benachrichtigungsaktualisierungen in der Seitenleiste.",
"disableHideSidebar": "Deaktivieren Sie das automatische Ausblenden der Seitenleiste bei der Vorschau oder Bearbeitung von Dateien.",
"autoplayMedia": "Autoplay von Mediendateien in der Vorschau"
"autoplayMedia": "Autoplay von Mediendateien in der Vorschau",
"deleteWithoutConfirming": "Dateien ohne Sicherheitsabfrage löschen",
"sidebarOptions": "Optionen für die Seitenleiste",
"disableQuickToggles": "Quick Toggles deaktivieren",
"disableSearchOptions": "Suchoptionen deaktivieren",
"hideSidebarFileActions": "Datei-Aktionen in der Seitenleiste ausblenden"
},
"otp": {
"name": "Zwei-Faktoren-Authentifizierung",
+14 -5
View File
@@ -167,7 +167,8 @@
"deleteUserMessage": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτόν τον χρήστη;",
"filesSelected": "επιλεγμένα αρχεία",
"typeName": "Τύπος:",
"selected": "επιλεγμένο"
"selected": "επιλεγμένο",
"deleted": "Διαγράφηκε με επιτυχία!"
},
"search": {
"images": "Εικόνες",
@@ -251,7 +252,6 @@
"scope": "Εμβέλεια",
"setDateFormat": "Ορισμός ακριβούς μορφής ημερομηνίας",
"settingsUpdated": "Οι ρυθμίσεις ενημερώθηκαν!",
"shareDuration": "Διάρκεια κοινοποίησης",
"shareManagement": "Διαχείριση κοινοποίησης",
"shareDeleted": "Η κοινοποίηση διαγράφηκε!",
"singleClick": "Χρήση μονού κλικ για να ανοίξετε αρχεία και φακέλους",
@@ -291,7 +291,10 @@
"disableOfficeEditorDescription": "Ένας κατάλογος επεκτάσεων αρχείων με διαχωρισμό διαστήματος για την απενεργοποίηση της προβολής OnlyOffice. (π.χ. '.txt .html').",
"disableOfficeEditor": "Απενεργοποίηση της προβολής γραφείου για ορισμένες επεκτάσεις αρχείων",
"disableOfficePreviews": "Απενεργοποίηση προεπισκοπήσεων γραφείου για ορισμένες επεκτάσεις αρχείων",
"disableOfficePreviewsDescription": "Μια λίστα επεκτάσεων αρχείων με διαχωρισμό διαστήματος για την απενεργοποίηση των προεπισκοπήσεων του Office. (π.χ., '.txt .html')."
"disableOfficePreviewsDescription": "Μια λίστα επεκτάσεων αρχείων με διαχωρισμό διαστήματος για την απενεργοποίηση των προεπισκοπήσεων του Office. (π.χ., '.txt .html').",
"adminOptions": "Επιλογές χρήστη διαχειριστή",
"shareDuration": "Λήγει",
"searchOptions": "Επιλογές αναζήτησης"
},
"sidebar": {
"help": "Βοήθεια",
@@ -306,7 +309,8 @@
"signup": "Εγγραφή",
"siteSettings": "Ρυθμίσεις ιστότοπου",
"sources": "Πηγές",
"updateIsAvailable": "Μια ενημέρωση είναι διαθέσιμη!"
"updateIsAvailable": "Μια ενημέρωση είναι διαθέσιμη!",
"fileActions": "Ενέργειες αρχείων"
},
"success": {
"linkCopied": "Ο σύνδεσμος αντιγράφηκε!"
@@ -375,7 +379,12 @@
"editOffice": "Επεξεργασία/προβολή αρχείων γραφείου",
"disableUpdateNotifications": "Απενεργοποιήστε την εμφάνιση του banner ενημέρωσης ειδοποιήσεων στην πλαϊνή γραμμή.",
"disableHideSidebar": "Απενεργοποίηση της αυτόματης απόκρυψης της πλευρικής γραμμής κατά την προεπισκόπηση ή την επεξεργασία αρχείων.",
"autoplayMedia": "Αυτόματη αναπαραγωγή αρχείων πολυμέσων στην προεπισκόπηση"
"autoplayMedia": "Αυτόματη αναπαραγωγή αρχείων πολυμέσων στην προεπισκόπηση",
"deleteWithoutConfirming": "Διαγραφή αρχείων χωρίς προτροπή επιβεβαίωσης",
"sidebarOptions": "Επιλογές πλευρικής γραμμής",
"disableQuickToggles": "Απενεργοποίηση γρήγορων εναλλαγών",
"disableSearchOptions": "Απενεργοποίηση επιλογών αναζήτησης",
"hideSidebarFileActions": "Απόκρυψη ενεργειών αρχείων στην πλευρική γραμμή"
},
"otp": {
"name": "Αυθεντικοποίηση δύο παραγόντων",
+14 -5
View File
@@ -170,7 +170,8 @@
"uploadFiles": "Uploading {files} files...",
"uploadMessage": "Select an option to upload.",
"optionalPassword": "Optional password",
"typeName": "Type:"
"typeName": "Type:",
"deleted": "Deleted successfully!"
},
"search": {
"images": "Images",
@@ -200,7 +201,12 @@
"filePreviewOptions": "File preview options",
"disableUpdateNotifications": "Disable showing notification update banner in sidebar.",
"disableHideSidebar": "Disable automatically hiding sidebar when previewing or editing files.",
"autoplayMedia": "Autoplay media files in preview"
"autoplayMedia": "Autoplay media files in preview",
"deleteWithoutConfirming": "Delete files without confirmation prompt",
"sidebarOptions": "Sidebar options",
"disableQuickToggles": "Disable quick toggles",
"disableSearchOptions": "Disable search options",
"hideSidebarFileActions": "Hide file actions in sidebar"
},
"settings": {
"enterPassword": "Enter password",
@@ -216,6 +222,7 @@
"disableOfficePreviews": "Disable office previews for certain file extensions",
"disableOfficePreviewsDescription": "A space-separated list of file extensions to disable office previews for. (e.g., '.txt .html').",
"listingOptions": "Listing options",
"searchOptions": "Search options",
"emptyGlobal": "Currently there are no global config options, You can edit the configuration yaml to adjust system settings.",
"scopes": "Define allowed sources and the user scope within them",
"UserManagement": "User Management",
@@ -282,7 +289,7 @@
"rulesHelp": "Here you can define a set of allow and disallow rules for this specific user. The blocked files won't show up in the listings and they wont be accessible to the user. We support regex and paths relative to the users scope.\n",
"scope": "Scope",
"settingsUpdated": "Settings updated!",
"shareDuration": "Share Duration",
"shareDuration": "Expires",
"shareManagement": "Share Management",
"shareDeleted": "Share deleted!",
"singleClick": "Use single clicks to open files and directories",
@@ -316,7 +323,8 @@
"sources": "Sources",
"signup": "Signup",
"siteSettings": "Site Settings",
"updateIsAvailable": "An update is available!"
"updateIsAvailable": "An update is available!",
"fileActions": "File actions"
},
"success": {
"linkCopied": "Link copied!"
@@ -366,7 +374,8 @@
"toggleDark": "Toggle Dark Mode",
"toggleSticky": "Toggle Sticky Mode",
"of": "of",
"used": "used"
"used": "used",
"noSources": "No sources available for this user. Either add a source for this user or configure a source with defaultEnabled: true"
},
"otp": {
"name": "Two-Factor Authentication",
+14 -5
View File
@@ -163,7 +163,8 @@
"deleteUserMessage": "¿Está seguro de que desea eliminar este usuario?",
"filesSelected": "archivos seleccionados",
"typeName": "Tipo:",
"selected": "seleccionado"
"selected": "seleccionado",
"deleted": "¡Borrado con éxito!"
},
"search": {
"images": "Imágenes",
@@ -244,7 +245,6 @@
"scope": "Raíz",
"setDateFormat": "Establecer el formato exacto de la fecha",
"settingsUpdated": "¡Ajustes actualizados!",
"shareDuration": "Compartir Duración",
"shareManagement": "Gestión Compartida",
"shareDeleted": "¡Recurso compartido eliminado!",
"singleClick": "Utilice un solo clic para abrir archivos y directorios",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Una lista separada por espacios de las extensiones de archivo para las que desea desactivar el visor de OnlyOffice. (por ejemplo, '.txt .html').",
"disableOfficeEditor": "Desactivar el visor de office para determinadas extensiones de archivo",
"disableOfficePreviews": "Desactivar la vista previa de office para determinadas extensiones de archivo",
"disableOfficePreviewsDescription": "Una lista separada por espacios de extensiones de archivo para las que deshabilitar las vistas previas de office. (por ejemplo, '.txt .html')."
"disableOfficePreviewsDescription": "Una lista separada por espacios de extensiones de archivo para las que deshabilitar las vistas previas de office. (por ejemplo, '.txt .html').",
"adminOptions": "Opciones del usuario administrador",
"shareDuration": "Expira en",
"searchOptions": "Opciones de búsqueda"
},
"sidebar": {
"help": "Ayuda",
@@ -302,7 +305,8 @@
"signup": "Registrate",
"siteSettings": "Ajustes del sitio",
"sources": "Fuentes",
"updateIsAvailable": "Ya está disponible la actualización."
"updateIsAvailable": "Ya está disponible la actualización.",
"fileActions": "Acciones de archivo"
},
"success": {
"linkCopied": "¡Link copiado!"
@@ -374,7 +378,12 @@
"editOffice": "Editar/ver archivos ofimáticos",
"disableUpdateNotifications": "Desactivar la visualización del banner de actualización de notificaciones en la barra lateral.",
"disableHideSidebar": "Desactivar la ocultación automática de la barra lateral al previsualizar o editar archivos.",
"autoplayMedia": "Reproducción automática de archivos multimedia en vista previa"
"autoplayMedia": "Reproducción automática de archivos multimedia en vista previa",
"deleteWithoutConfirming": "Eliminar archivos sin mensaje de confirmación",
"sidebarOptions": "Opciones de la barra lateral",
"disableQuickToggles": "Desactivar alternancias rápidas",
"disableSearchOptions": "Desactivar las opciones de búsqueda",
"hideSidebarFileActions": "Ocultar las acciones de archivo en la barra lateral"
},
"otp": {
"name": "Autenticación de dos factores",
+14 -5
View File
@@ -163,7 +163,8 @@
"deleteUserMessage": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
"filesSelected": "fichiers sélectionnés",
"typeName": "Type :",
"selected": "sélectionné"
"selected": "sélectionné",
"deleted": "Supprimé avec succès !"
},
"search": {
"images": "Images",
@@ -241,7 +242,6 @@
"scope": "Portée du dossier utilisateur",
"setDateFormat": "Définir le format de la date",
"settingsUpdated": "Les paramètres ont été mis à jour !",
"shareDuration": "Durée du partage",
"shareManagement": "Gestion des partages",
"shareDeleted": "Partage supprimé !",
"singleClick": "Utiliser un simple clic pour ouvrir les fichiers et les dossiers",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Une liste d'extensions de fichiers séparées par des espaces pour lesquelles le visualisateur OnlyOffice doit être désactivé. (par exemple, '.txt .html').",
"disableOfficeEditor": "Désactiver la visionneuse d'office pour certaines extensions de fichiers",
"disableOfficePreviews": "Désactiver les aperçus Office pour certaines extensions de fichiers",
"disableOfficePreviewsDescription": "Une liste d'extensions de fichiers séparées par des espaces pour lesquelles les aperçus Office doivent être désactivés. (par exemple, '.txt .html')."
"disableOfficePreviewsDescription": "Une liste d'extensions de fichiers séparées par des espaces pour lesquelles les aperçus Office doivent être désactivés. (par exemple, '.txt .html').",
"adminOptions": "Options de l'utilisateur administrateur",
"shareDuration": "Expiration",
"searchOptions": "Options de recherche"
},
"sidebar": {
"help": "Aide",
@@ -302,7 +305,8 @@
"signup": "S'inscrire",
"siteSettings": "Paramètres du site",
"sources": "Sources d'information",
"updateIsAvailable": "Une mise à jour est disponible !"
"updateIsAvailable": "Une mise à jour est disponible !",
"fileActions": "Actions sur les dossiers"
},
"success": {
"linkCopied": "Lien copié !"
@@ -374,7 +378,12 @@
"filePreviewOptions": "Options de prévisualisation des fichiers",
"disableUpdateNotifications": "Désactiver l'affichage de la bannière de mise à jour des notifications dans la barre latérale.",
"disableHideSidebar": "Désactiver le masquage automatique de la barre latérale lors de la prévisualisation ou de l'édition de fichiers.",
"autoplayMedia": "Lecture automatique des fichiers multimédias dans l'aperçu"
"autoplayMedia": "Lecture automatique des fichiers multimédias dans l'aperçu",
"deleteWithoutConfirming": "Supprimer des fichiers sans demande de confirmation",
"sidebarOptions": "Options de la barre latérale",
"disableQuickToggles": "Désactiver les bascules rapides",
"disableSearchOptions": "Désactiver les options de recherche",
"hideSidebarFileActions": "Cacher les actions sur les fichiers dans la barre latérale"
},
"otp": {
"name": "Authentification à deux facteurs",
+14 -5
View File
@@ -163,7 +163,8 @@
"deleteUserMessage": "האם אתה בטוח שברצונך למחוק משתמש זה?",
"selected": "נבחר",
"filesSelected": "קבצים שנבחרו",
"typeName": "סוג:"
"typeName": "סוג:",
"deleted": "נמחק בהצלחה!"
},
"search": {
"images": "תמונות",
@@ -242,7 +243,6 @@
"scope": "תחום",
"setDateFormat": "הגדר פורמט תאריך",
"settingsUpdated": "ההגדרות עודכנו!",
"shareDuration": "משך השיתוף",
"shareManagement": "ניהול שיתוף",
"shareDeleted": "שיתוף נמחק!",
"singleClick": "השתמש בלחיצות בודדות כדי לפתוח קבצים ותקיות",
@@ -284,7 +284,10 @@
"tusUploadsRetryCount": "מספר הניסיונות שיש לבצע אם העלאת קטע נכשלה.",
"disableUsedDiskPercentage": "השבת גרף אחוזי השימוש בדיסק",
"shareSettings": "הגדרות שיתוף",
"api": "מפתחות API"
"api": "מפתחות API",
"adminOptions": "אפשרויות משתמש מנהל",
"shareDuration": "תוקף",
"searchOptions": "אפשרויות חיפוש"
},
"sidebar": {
"help": "עזרה",
@@ -299,7 +302,8 @@
"signup": "הרשמה",
"siteSettings": "הגדרות",
"sources": "מקורות",
"updateIsAvailable": "עדכון זמין!"
"updateIsAvailable": "עדכון זמין!",
"fileActions": "פעולות קובץ"
},
"success": {
"linkCopied": "הקישור הועתק!"
@@ -370,7 +374,12 @@
"filePreviewOptions": "אפשרויות תצוגה מקדימה של קבצים",
"disableUpdateNotifications": "השבת הצגת כרטיסיית עדכון הודעות בסרגל הצד.",
"disableHideSidebar": "השבת הסתרה אוטומטית של סרגל הצד בעת תצוגה מקדימה או עריכת קבצים.",
"autoplayMedia": "הפעל קבצי מדיה באופן אוטומטי בתצוגה מקדימה"
"autoplayMedia": "הפעל קבצי מדיה באופן אוטומטי בתצוגה מקדימה",
"deleteWithoutConfirming": "מחיקת קבצים ללא בקשת אישור",
"sidebarOptions": "אפשרויות סרגל צד",
"disableQuickToggles": "השבת מתגים מהירים",
"disableSearchOptions": "השבת אפשרויות חיפוש",
"hideSidebarFileActions": "הסתר פעולות קובץ בסרגל הצד"
},
"otp": {
"name": "אימות דו-גורמי",
+14 -5
View File
@@ -163,7 +163,8 @@
"deleteUserMessage": "Biztos, hogy törölni szeretné ezt a felhasználót?",
"filesSelected": "kiválasztott fájlok",
"typeName": "Típus:",
"selected": "kiválasztott"
"selected": "kiválasztott",
"deleted": "Sikeresen törölve!"
},
"search": {
"images": "Képek",
@@ -242,7 +243,6 @@
"scope": "Hatókör",
"setDateFormat": "Pontos dátumformátum beállítása",
"settingsUpdated": "Beállítások frissítve!",
"shareDuration": "Megosztás időtartama",
"shareManagement": "Megosztáskezelés",
"shareDeleted": "Megosztás törölve!",
"singleClick": "Fájlok és könyvtárak megnyitása egyetlen kattintással",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Azon fájlkiterjesztések szóközzel elválasztott listája, amelyek esetében az OnlyOffice megjelenítőjét le kell tiltani. (pl. '.txt .html').",
"disableOfficeEditor": "Bizonyos fájlkiterjesztéseknél letilthatja az Office Viewer-t",
"disableOfficePreviews": "Office előnézet letiltása bizonyos fájlkiterjesztésekhez",
"disableOfficePreviewsDescription": "Az irodai előnézetek letiltásának szóközzel elválasztott fájlkiterjesztések listája. (pl. '.txt .html')."
"disableOfficePreviewsDescription": "Az irodai előnézetek letiltásának szóközzel elválasztott fájlkiterjesztések listája. (pl. '.txt .html').",
"adminOptions": "Admin felhasználói beállítások",
"shareDuration": "Lejárat:",
"searchOptions": "Keresési lehetőségek"
},
"sidebar": {
"help": "Súgó",
@@ -302,7 +305,8 @@
"signup": "Regisztráció",
"siteSettings": "Oldalbeállítások",
"sources": "Források",
"updateIsAvailable": "Elérhető a frissítés!"
"updateIsAvailable": "Elérhető a frissítés!",
"fileActions": "Fájl műveletek"
},
"success": {
"linkCopied": "Link másolva!"
@@ -374,7 +378,12 @@
"editOffice": "Office fájlok szerkesztése/megjelenítése",
"disableUpdateNotifications": "Értesítési frissítés banner megjelenítésének kikapcsolása az oldalsávban.",
"disableHideSidebar": "Az oldalsáv automatikus elrejtésének kikapcsolása fájlok előnézeténél vagy szerkesztésénél.",
"autoplayMedia": "Médiafájlok automatikus lejátszása előnézetben"
"autoplayMedia": "Médiafájlok automatikus lejátszása előnézetben",
"deleteWithoutConfirming": "Fájlok törlése megerősítés nélkül",
"sidebarOptions": "Oldalsáv beállítások",
"disableQuickToggles": "Gyorskapcsolók letiltása",
"disableSearchOptions": "Keresési lehetőségek letiltása",
"hideSidebarFileActions": "Fájlműveletek elrejtése az oldalsávban"
},
"otp": {
"name": "Kétfaktoros hitelesítés",
-1
View File
@@ -213,7 +213,6 @@
"rulesHelp": "Hér getur þú skilgreint hvaða reglur gilda um notandann. Skjölin sem hann hefur ekki aðgang að eru óaðgengileg og hann sér þau ekki. Stuðst er við reglulegar segðir og slóðir sem miðast við sýn notandans. ",
"scope": "Sýn notandans",
"settingsUpdated": "Stillingar vistaðar!",
"shareDuration": "",
"shareManagement": "",
"singleClick": "",
"themes": {
+14 -5
View File
@@ -163,7 +163,8 @@
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Password opzionale",
"typeName": "Tipo:",
"selected": "selezionato"
"selected": "selezionato",
"deleted": "Eliminato con successo!"
},
"search": {
"images": "Immagini",
@@ -239,7 +240,6 @@
"rulesHelp": "Qui è possibile definire una serie di regole e permessi per questo specifico utente. I file bloccati non appariranno negli elenchi e non saranno accessibili dagli utenti. all'utente. Sia regex che i percorsi relativi all'ambito di applicazione degli utenti sono supportati.\n",
"scope": "Scope",
"settingsUpdated": "Impostazioni aggiornate!",
"shareDuration": "Durata della condivisione",
"shareManagement": "Gestione delle condivisioni",
"singleClick": "Usa un singolo click per aprire file e cartelle",
"themes": {
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Un elenco separato da spazi di estensioni di file per cui disabilitare il visualizzatore di OnlyOffice. (ad esempio, '.txt .html').",
"disableOfficeEditor": "Disattivare il visualizzatore di Office per alcune estensioni di file",
"disableOfficePreviews": "Disattivare le anteprime di Office per alcune estensioni di file",
"disableOfficePreviewsDescription": "Un elenco separato da spazi di estensioni di file per cui disabilitare le anteprime di Office. (ad esempio, '.txt .html')."
"disableOfficePreviewsDescription": "Un elenco separato da spazi di estensioni di file per cui disabilitare le anteprime di Office. (ad esempio, '.txt .html').",
"adminOptions": "Opzioni dell'utente amministratore",
"shareDuration": "Scadenza",
"searchOptions": "Opzioni di ricerca"
},
"sidebar": {
"help": "Aiuto",
@@ -302,7 +305,8 @@
"signup": "Registrati",
"siteSettings": "Impostazioni del sito",
"sources": "Fonti",
"updateIsAvailable": "È disponibile un aggiornamento!"
"updateIsAvailable": "È disponibile un aggiornamento!",
"fileActions": "Azioni sui file"
},
"success": {
"linkCopied": "Link copiato!"
@@ -374,7 +378,12 @@
"editOffice": "Modificare/visualizzare i file dell'ufficio",
"disableUpdateNotifications": "Disattivare la visualizzazione del banner di aggiornamento delle notifiche nella barra laterale.",
"disableHideSidebar": "Disabilita la possibilità di nascondere automaticamente la barra laterale durante l'anteprima o la modifica dei file.",
"autoplayMedia": "Riproduzione automatica dei file multimediali nell'anteprima"
"autoplayMedia": "Riproduzione automatica dei file multimediali nell'anteprima",
"deleteWithoutConfirming": "Eliminazione di file senza richiesta di conferma",
"sidebarOptions": "Opzioni della barra laterale",
"disableQuickToggles": "Disattivare i toggle rapidi",
"disableSearchOptions": "Disattivare le opzioni di ricerca",
"hideSidebarFileActions": "Nascondere le azioni dei file nella barra laterale"
},
"otp": {
"name": "Autenticazione a due fattori",
+14 -5
View File
@@ -163,7 +163,8 @@
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "任意のパスワード",
"typeName": "タイプ",
"selected": "選ばれた"
"selected": "選ばれた",
"deleted": "正常に削除されました!"
},
"search": {
"images": "画像",
@@ -239,7 +240,6 @@
"rulesHelp": "ここに、あなたはこのユーザーの許可または拒否規則を設定できます。ブロックされたファイルはリストに表示されません、それではアクセスも制限されます。正規表現(regex)のサポートと範囲に相対のパスが提供されています。",
"scope": "範囲",
"settingsUpdated": "設定は更新されました!",
"shareDuration": "シェア期間",
"shareManagement": "シェア・マネジメント",
"singleClick": "シングルクリックでファイルやディレクトリを開く",
"themes": {
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "OnlyOffice ビューアを無効にするファイル拡張子をスペース区切りで指定します。(例: '.txt .html')。",
"disableOfficeEditor": "特定のファイル拡張子のオフィスビューアを無効にする",
"disableOfficePreviews": "特定のファイル拡張子でオフィスのプレビューを無効にする",
"disableOfficePreviewsDescription": "オフィスプレビューを無効にするファイル拡張子をスペースで区切ったリスト。(例: '.txt .html')。"
"disableOfficePreviewsDescription": "オフィスプレビューを無効にするファイル拡張子をスペースで区切ったリスト。(例: '.txt .html')。",
"adminOptions": "管理者ユーザーオプション",
"shareDuration": "期限切れ",
"searchOptions": "検索オプション"
},
"sidebar": {
"help": "ヘルプ",
@@ -302,7 +305,8 @@
"signup": "Signup",
"siteSettings": "サイト設定",
"sources": "情報源",
"updateIsAvailable": "アップデートがある!"
"updateIsAvailable": "アップデートがある!",
"fileActions": "ファイル操作"
},
"success": {
"linkCopied": "リンクがコピーされました!"
@@ -374,7 +378,12 @@
"editOffice": "オフィスファイルの編集/閲覧",
"disableUpdateNotifications": "サイドバーに通知更新バナーを表示しないようにしました。",
"disableHideSidebar": "ファイルのプレビューや編集時にサイドバーを自動的に非表示にします。",
"autoplayMedia": "プレビューでメディアファイルを自動再生"
"autoplayMedia": "プレビューでメディアファイルを自動再生",
"deleteWithoutConfirming": "確認プロンプトなしでファイルを削除する",
"sidebarOptions": "サイドバーオプション",
"disableQuickToggles": "クイックトグルを無効にする",
"disableSearchOptions": "検索オプションを無効にする",
"hideSidebarFileActions": "サイドバーにファイルアクションを隠す"
},
"otp": {
"name": "二要素認証",
+14 -5
View File
@@ -163,7 +163,8 @@
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "선택적 비밀번호",
"typeName": "유형:",
"selected": "선택"
"selected": "선택",
"deleted": "성공적으로 삭제되었습니다!"
},
"search": {
"images": "이미지",
@@ -239,7 +240,6 @@
"rulesHelp": "사용자별로 규칙을 허용/방지를 지정할 수 있습니다. 방지된 파일은 보이지 않고 사용자들은 접근할 수 없습니다. 사용자의 접근 허용 범위와 관련해 정규표현식(regex)과 경로를 지원합니다.\n",
"scope": "범위",
"settingsUpdated": "설정 수정됨!",
"shareDuration": "공유 기간",
"shareManagement": "공유 관리",
"singleClick": "클릭 한 번으로 파일과 디렉터리 열기",
"themes": {
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "OnlyOffice 뷰어를 비활성화할 파일 확장명을 공백으로 구분한 목록입니다. (예: '.txt .html').",
"disableOfficeEditor": "특정 파일 확장자에 대해 Office 뷰어 사용 안 함",
"disableOfficePreviews": "특정 파일 확장명에 대한 Office 미리 보기 사용 안 함",
"disableOfficePreviewsDescription": "Office 미리 보기를 사용하지 않도록 설정할 파일 확장명을 공백으로 구분한 목록입니다. (예: '.txt .html')."
"disableOfficePreviewsDescription": "Office 미리 보기를 사용하지 않도록 설정할 파일 확장명을 공백으로 구분한 목록입니다. (예: '.txt .html').",
"adminOptions": "관리자 사용자 옵션",
"shareDuration": "만료",
"searchOptions": "검색 옵션"
},
"sidebar": {
"help": "도움말",
@@ -302,7 +305,8 @@
"signup": "가입하기",
"siteSettings": "사이트 설정",
"sources": "출처",
"updateIsAvailable": "업데이트를 사용할 수 있습니다!"
"updateIsAvailable": "업데이트를 사용할 수 있습니다!",
"fileActions": "파일 작업"
},
"success": {
"linkCopied": "링크가 복사되었습니다!"
@@ -374,7 +378,12 @@
"editOffice": "오피스 파일 편집/보기",
"disableUpdateNotifications": "사이드바에 알림 업데이트 배너 표시를 비활성화합니다.",
"disableHideSidebar": "파일을 미리 보거나 편집할 때 사이드바 자동 숨기기를 비활성화합니다.",
"autoplayMedia": "미리보기에서 미디어 파일 자동 재생"
"autoplayMedia": "미리보기에서 미디어 파일 자동 재생",
"deleteWithoutConfirming": "확인 메시지 없이 파일 삭제",
"sidebarOptions": "사이드바 옵션",
"disableQuickToggles": "빠른 토글 비활성화",
"disableSearchOptions": "검색 옵션 비활성화",
"hideSidebarFileActions": "사이드바에서 파일 작업 숨기기"
},
"otp": {
"name": "2단계 인증",
+155 -23
View File
@@ -31,17 +31,27 @@
"switchView": "Beeld wisselen",
"toggleSidebar": "Zijbalk tonen",
"update": "Updaten",
"upload": "Uploaden"
"upload": "Uploaden",
"generateNewOtp": "Reset en genereer nieuwe twee-factorcode",
"verify": "Controleer",
"copyDownloadLinkToClipboard": "Downloadlink naar klembord kopiëren",
"file": "Bestand",
"folder": "Map",
"submit": "Stuur in",
"openFile": "Bestand openen",
"continue": "Ga verder",
"edit": "Bewerk"
},
"download": {
"downloadFile": "Bestand downloaden",
"downloadFolder": "Map downloaden",
"downloadSelected": ""
"downloadSelected": "Geselecteerd downloaden"
},
"errors": {
"forbidden": "U hebt geen rechten om hier toegang toe te krijgen.",
"internal": "Er ging iets mis.",
"notFound": "Deze locatie kan niet worden bereikt."
"notFound": "Deze locatie kan niet worden bereikt.",
"connection": "De server kan niet worden bereikt."
},
"files": {
"body": "Body",
@@ -59,7 +69,8 @@
"size": "Grootte",
"sortByLastModified": "Sorteren op laatst bewerkt",
"sortByName": "Sorteren op naam",
"sortBySize": "Sorteren op grootte"
"sortBySize": "Sorteren op grootte",
"noPreview": "Preview is niet beschikbaar voor dit bestand."
},
"help": {
"click": "selecteer bestand of map",
@@ -110,7 +121,8 @@
"submit": "Log in",
"username": "Gebruikersnaam",
"usernameTaken": "Gebruikersnaam reeds in gebruik",
"wrongCredentials": "Verkeerde inloggegevens"
"wrongCredentials": "Verkeerde inloggegevens",
"or": "OF"
},
"permanent": "Permanent",
"prompts": {
@@ -143,9 +155,16 @@
"scheduleMessage": "Kies een datum en tijd om de publicatie van dit bericht in te plannen.",
"show": "Tonen",
"size": "Grootte",
"upload": "",
"uploadMessage": "",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?"
"upload": "Uploaden",
"uploadMessage": "Selecteer een optie om te 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",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Optioneel wachtwoord",
"typeName": "Type:",
"deleted": "Met succes verwijderd!"
},
"search": {
"images": "Afbeeldingen",
@@ -155,7 +174,12 @@
"search": "Zoeken...",
"typeToSearch": "Typ om te zoeken...",
"types": "Types",
"video": "Video"
"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."
},
"settings": {
"admin": "Admin",
@@ -194,7 +218,7 @@
"newUser": "Nieuwe gebruiker",
"password": "Wachtwoord",
"passwordUpdated": "Wachtwoord bijgewerkt!",
"path": "",
"path": "Pad",
"permissions": {
"create": "Bestanden en mappen aanmaken",
"delete": "Bestanden en mappen verwijderen",
@@ -202,7 +226,10 @@
"execute": "Commando's uitvoeren",
"modify": "Bestanden aanpassen",
"rename": "Bestanden of mappen hernoemen of verplaatsen",
"share": "Bestanden delen"
"share": "Bestanden delen",
"admin": "Beheerder",
"api": "Langlevende API-sleutels maken en beheren",
"realtime": "Real-time verbindingen en updates mogelijk maken"
},
"permissions-name": "Permissies",
"permissionsHelp": "U kunt de gebruiker instellen als beheerder of de machtigingen afzonderlijk kiezen. Als u \"Beheerder\" selecteert, worden alle andere opties automatisch gecontroleerd. Het beheer van gebruikers blijft een privilege van een beheerder.\n",
@@ -213,13 +240,12 @@
"rulesHelp": "Hier kunt u een reeks regels voor toestaan en niet toestaan voor deze specifieke gebruiker definiëren. De geblokkeerde bestanden verschijnen niet in de lijsten en zijn niet toegankelijk voor de gebruiker. We ondersteunen regex en paden relatief ten opzichte van het bereik van gebruikers. \n",
"scope": "Scope",
"settingsUpdated": "Instellingen bijgewerkt!",
"shareDuration": "",
"shareManagement": "",
"singleClick": "",
"shareManagement": "Beheer van aandelen",
"singleClick": "Gebruik één klik om bestanden en mappen te openen",
"themes": {
"dark": "",
"light": "",
"title": ""
"dark": "Donker",
"light": "Licht",
"title": "Thema"
},
"user": "Gebruiker",
"userCommands": "Commando's",
@@ -230,7 +256,37 @@
"userManagement": "Gebruikersbeheer",
"userUpdated": "Gebruiker bijgewerkt!",
"username": "Gebruikersnaam",
"users": "Gebruikers"
"users": "Gebruikers",
"enterPassword": "Wachtwoord invoeren",
"enterPasswordAgain": "Voer het wachtwoord opnieuw in",
"passwordsDoNotMatch": "Wachtwoorden komen niet overeen",
"loginMethod": "Aanmeldingsmethode",
"loginMethodDescription": "toegestane aanmeldingsmethode voor de gebruiker.",
"modifyOtherUser": "Gebruiker wijzigen:",
"modifyCurrentUser": "Huidige gebruiker wijzigen",
"themeColor": "Thema Kleur",
"disableOfficeEditorDescription": "Een door spaties gescheiden lijst met bestandsextensies waarvoor de OnlyOffice viewer uitgeschakeld moet worden. (bijv. '.txt .html').",
"disableOfficeEditor": "Office Viewer uitschakelen voor bepaalde bestandsextensies",
"disableOfficePreviews": "Office-previews uitschakelen voor bepaalde bestandsextensies",
"disableOfficePreviewsDescription": "Een door spaties gescheiden lijst met bestandsextensies waarvoor Office-previews moeten worden uitgeschakeld. (bijv. '.txt .html').",
"listingOptions": "Advertentie-opties",
"emptyGlobal": "Op dit moment zijn er geen globale configuratieopties. U kunt de configuratie yaml bewerken om systeeminstellingen aan te passen.",
"scopes": "Toegestane bronnen en het gebruikersbereik daarbinnen definiëren",
"UserManagement": "Gebruikersbeheer",
"userNotAdmin": "Gebruiker is geen beheerder",
"tusUploads": "Verzamelde uploads",
"tusUploadsHelp": "File Browser ondersteunt chunked bestandsuploads, waardoor efficiënte, betrouwbare, hervattbare en chunked bestandsuploads mogelijk zijn, zelfs op onbetrouwbare netwerken.",
"tusUploadsChunkSize": "Geeft de maximale grootte van een verzoek aan (directe uploads worden gebruikt voor kleinere uploads). Je kunt een gewoon geheel getal invoeren dat staat voor een bytes-invoer of een tekenreeks zoals 10MB, 1GB enz.",
"tusUploadsRetryCount": "Aantal pogingen dat moet worden uitgevoerd als het uploaden van een chunk mislukt.",
"userHomeBasePath": "Basispad voor homedirectory's van gebruikers",
"userScopeGenerationPlaceholder": "De scope wordt automatisch gegenereerd",
"createUserHomeDirectory": "Thuismap gebruiker aanmaken",
"shareSettings": "Instellingen delen",
"api": "API-sleutels",
"shareDeleted": "Deel verwijderd!",
"adminOptions": "Opties voor beheerders",
"shareDuration": "Verloopt op",
"searchOptions": "Zoekopties"
},
"sidebar": {
"help": "Help",
@@ -243,7 +299,10 @@
"preview": "Voorbeeld",
"settings": "Instellingen",
"signup": "Registeren",
"siteSettings": "Site-instellingen"
"siteSettings": "Site-instellingen",
"sources": "Bronnen",
"updateIsAvailable": "Er is een update beschikbaar!",
"fileActions": "Bestandsacties"
},
"success": {
"linkCopied": "Link gekopieerd!"
@@ -255,8 +314,81 @@
"seconds": "Seconden",
"unit": "Tijdseenheid"
},
"upload": {},
"share": {},
"api": {},
"index": {}
"upload": {
"abortUpload": "Weet je zeker dat je wilt aborteren?"
},
"share": {
"notice": "Opmerking: iedereen die toegang heeft tot de link (en optioneel wachtwoord) kan de gedeelde bestanden openen. Je hoeft niet ingelogd te zijn."
},
"api": {
"title": "API-sleutels",
"keyName": "API-sleutelnaam:",
"createdAt": "Gemaakt op:",
"expiresAt": "Verloopt op:",
"permissions": "Toestemmingen:",
"createTitle": "API-sleutel aanmaken",
"keyNamePlaceholder": "Voer een unieke naam voor de API-sleutel in:",
"tokenDuration": "Duur token:",
"durationNumberPlaceholder": "Aantal",
"days": "Dagen",
"months": "Maanden",
"permissionNote": "Kies minstens één permissie voor de sleutel. Je gebruiker moet deze rechten ook hebben.",
"createKeyFailed": "API-sleutel is niet aangemaakt.",
"createKeySuccess": "API-sleutel succesvol aangemaakt.",
"description": "API-sleutels zijn gebaseerd op de gebruiker die ze aanmaakt. Zie",
"swaggerLinkText": "swagger pagina",
"name": "Naam",
"created": "Gemaakt",
"expires": "Verloopt op",
"actions": "Acties",
"enabled": "Ingeschakeld",
"disabled": "Uitgeschakeld"
},
"index": {
"status": "Status",
"assessment": "Beoordeling",
"files": "Bestanden",
"folders": "Mappen",
"lastScanned": "Laatst gescand",
"quickScan": "Snelle scan Seconden",
"fullScan": "Seconden voor volledig scannen",
"settingsHover": "Instellingen voor gebruiker",
"logout": "Afmelden",
"toggleClick": "Schakelen Enkele klik",
"toggleDark": "Donkere modus inschakelen",
"toggleSticky": "Kleefmodus schakelen",
"of": "van",
"used": "tweedehands"
},
"profileSettings": {
"setDateFormat": "Exact datumformaat instellen",
"showHiddenFiles": "Verborgen bestanden weergeven",
"showQuickDownload": "Downloadpictogram weergeven voor snelle toegang",
"previewImages": "Voorbeeldafbeeldingen",
"previewVideos": "Video's bekijken",
"previewMotionVideos": "Bewegingsvoorbeeld voor video's",
"highQualityPreview": "Hoge kwaliteit preview voor galerijafbeeldingen",
"editOffice": "Office-bestanden bewerken/bekijken",
"popupPreview": "Pop-up voorbeeld inschakelen",
"filePreviewOptions": "Opties voor bestandsvoorbeeld",
"disableUpdateNotifications": "Schakel het tonen van de melding update banner in de zijbalk uit.",
"disableHideSidebar": "Het automatisch verbergen van de zijbalk bij het bekijken of bewerken van bestanden uitschakelen.",
"autoplayMedia": "Mediabestanden automatisch afspelen in voorvertoning",
"deleteWithoutConfirming": "Bestanden verwijderen zonder bevestigingsprompt",
"sidebarOptions": "Zijbalk opties",
"disableQuickToggles": "Snel wisselen uitschakelen",
"disableSearchOptions": "Zoekopties uitschakelen",
"hideSidebarFileActions": "Verberg bestandsacties in de zijbalk"
},
"otp": {
"name": "Authenticatie met twee factoren",
"verifyInstructions": "Voer de code van uw authenticator-app in",
"generate": "Scan de QR-code met uw authenticator-app",
"codeInputPlaceholder": "6-cijferige code",
"invalidCodeType": "De verificatiecode moet uit 6 engelse cijfers bestaan",
"enabledSuccessfully": "OTP succesvol ingeschakeld",
"verificationFailed": "Verificatie mislukt",
"verificationSucceed": "Verificatie slagen",
"generationFailed": "Generatie mislukt"
}
}
+14 -5
View File
@@ -163,7 +163,8 @@
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Hasło opcjonalne",
"typeName": "Typ:",
"selected": "wybrany"
"selected": "wybrany",
"deleted": "Usunięto pomyślnie!"
},
"search": {
"images": "Zdjęcia",
@@ -239,7 +240,6 @@
"rulesHelp": "Tu możesz zdefiniować zestawienie reguł zezwalających i zabraniających dla tego konkretnego użytkownika. Zablokowane pliki nie będą widoczne na listach i nie będą dostępne dla użytkownika. Wspierane są wyrażenia regularne i ścieżki względne wobec zakresu użytkownika.\n",
"scope": "Zakres",
"settingsUpdated": "Uprawnienia Zapisane!",
"shareDuration": "Okres udostępniania",
"shareManagement": "Zarządzanie udostępnianiem",
"singleClick": "Pojedyncze kliknięcie",
"themes": {
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Rozdzielana spacjami lista rozszerzeń plików, dla których przeglądarka OnlyOffice ma zostać wyłączona. (np. \".txt .html\").",
"disableOfficeEditor": "Wyłącz przeglądarkę Office dla niektórych rozszerzeń plików",
"disableOfficePreviews": "Wyłącz podglądy Office dla niektórych rozszerzeń plików",
"disableOfficePreviewsDescription": "Rozdzielana spacjami lista rozszerzeń plików do wyłączenia podglądu plików biurowych. (np. \".txt .html\")."
"disableOfficePreviewsDescription": "Rozdzielana spacjami lista rozszerzeń plików do wyłączenia podglądu plików biurowych. (np. \".txt .html\").",
"adminOptions": "Opcje użytkownika administratora",
"shareDuration": "Wygasa",
"searchOptions": "Opcje wyszukiwania"
},
"sidebar": {
"help": "Pomoc",
@@ -302,7 +305,8 @@
"signup": "Rejestracja",
"siteSettings": "Ustawienia Strony",
"sources": "Źródła",
"updateIsAvailable": "Aktualizacja jest już dostępna!"
"updateIsAvailable": "Aktualizacja jest już dostępna!",
"fileActions": "Działania na plikach"
},
"success": {
"linkCopied": "Link Skopiowany!"
@@ -374,7 +378,12 @@
"editOffice": "Edycja/przeglądanie plików biurowych",
"disableUpdateNotifications": "Wyłącz wyświetlanie banera aktualizacji powiadomień na pasku bocznym.",
"disableHideSidebar": "Wyłącz automatyczne ukrywanie paska bocznego podczas podglądu lub edycji plików.",
"autoplayMedia": "Automatyczne odtwarzanie plików multimedialnych w podglądzie"
"autoplayMedia": "Automatyczne odtwarzanie plików multimedialnych w podglądzie",
"deleteWithoutConfirming": "Usuwanie plików bez monitu o potwierdzenie",
"sidebarOptions": "Opcje paska bocznego",
"disableQuickToggles": "Wyłączanie szybkich przełączników",
"disableSearchOptions": "Wyłącz opcje wyszukiwania",
"hideSidebarFileActions": "Ukryj akcje związane z plikami na pasku bocznym"
},
"otp": {
"name": "Uwierzytelnianie dwuskładnikowe",
+14 -5
View File
@@ -163,7 +163,8 @@
"deleteUserMessage": "Tem certeza de que deseja excluir esse usuário?",
"filesSelected": "arquivos selecionados",
"typeName": "Tipo:",
"selected": "selecionado"
"selected": "selecionado",
"deleted": "Excluído com sucesso!"
},
"search": {
"images": "Imagens",
@@ -243,7 +244,6 @@
"scope": "Escopo",
"setDateFormat": "Definir formato exato de data",
"settingsUpdated": "Configurações atualizadas!",
"shareDuration": "Duração do compartilhamento",
"shareManagement": "Gerenciamento do compartilhamento",
"shareDeleted": "Compartilhamento apagado!",
"singleClick": "Usar clique único para abrir arquivos e diretórios",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Uma lista de extensões de arquivo separada por espaços para desativar o visualizador do OnlyOffice. (por exemplo, '.txt .html').",
"disableOfficeEditor": "Desativar o visualizador do Office para determinadas extensões de arquivo",
"disableOfficePreviews": "Desativar as visualizações do Office para determinadas extensões de arquivo",
"disableOfficePreviewsDescription": "Uma lista de extensões de arquivo separada por espaços para desativar as visualizações do Office. (por exemplo, '.txt .html')."
"disableOfficePreviewsDescription": "Uma lista de extensões de arquivo separada por espaços para desativar as visualizações do Office. (por exemplo, '.txt .html').",
"adminOptions": "Opções do usuário administrador",
"shareDuration": "Expirações",
"searchOptions": "Opções de pesquisa"
},
"sidebar": {
"help": "Ajuda",
@@ -302,7 +305,8 @@
"signup": "Cadastrar",
"siteSettings": "Configurações do site",
"sources": "Fontes",
"updateIsAvailable": "Uma atualização está disponível!"
"updateIsAvailable": "Uma atualização está disponível!",
"fileActions": "Ações de arquivo"
},
"success": {
"linkCopied": "Link copiado!"
@@ -374,7 +378,12 @@
"editOffice": "Editar/visualizar arquivos de escritório",
"disableUpdateNotifications": "Desativar a exibição do banner de atualização de notificação na barra lateral.",
"disableHideSidebar": "Desative a ocultação automática da barra lateral ao visualizar ou editar arquivos.",
"autoplayMedia": "Reprodução automática de arquivos de mídia na visualização"
"autoplayMedia": "Reprodução automática de arquivos de mídia na visualização",
"deleteWithoutConfirming": "Excluir arquivos sem prompt de confirmação",
"sidebarOptions": "Opções da barra lateral",
"disableQuickToggles": "Desativar as alternâncias rápidas",
"disableSearchOptions": "Desativar opções de pesquisa",
"hideSidebarFileActions": "Ocultar ações de arquivo na barra lateral"
},
"otp": {
"name": "Autenticação de dois fatores",
+155 -23
View File
@@ -31,17 +31,27 @@
"switchView": "Alterar vista",
"toggleSidebar": "Alternar barra lateral",
"update": "Atualizar",
"upload": "Enviar"
"upload": "Enviar",
"generateNewOtp": "Repor e gerar um novo código de dois factores",
"verify": "Verificar",
"copyDownloadLinkToClipboard": "Copiar a ligação de transferência para a área de transferência",
"file": "Ficheiro",
"folder": "Pasta",
"submit": "Enviar",
"openFile": "Abrir ficheiro",
"continue": "Continuar",
"edit": "Editar"
},
"download": {
"downloadFile": "Descarregar ficheiro",
"downloadFolder": "Descarregar pasta",
"downloadSelected": ""
"downloadSelected": "Descarregar selecionado"
},
"errors": {
"forbidden": "Não tem permissões para aceder a isto",
"internal": "Algo correu bastante mal.",
"notFound": "Esta localização não é alcançável."
"notFound": "Esta localização não é alcançável.",
"connection": "O servidor não pode ser contactado."
},
"files": {
"body": "Corpo",
@@ -59,7 +69,8 @@
"size": "Tamanho",
"sortByLastModified": "Ordenar pela última alteração",
"sortByName": "Ordenar pelo nome",
"sortBySize": "Ordenar pelo tamanho"
"sortBySize": "Ordenar pelo tamanho",
"noPreview": "A pré-visualização não está disponível para este ficheiro."
},
"help": {
"click": "selecionar pasta ou ficheiro",
@@ -110,7 +121,8 @@
"submit": "Entrar na conta",
"username": "Nome de utilizador",
"usernameTaken": "O nome de utilizador já está registado",
"wrongCredentials": "Dados errados"
"wrongCredentials": "Dados errados",
"or": "OU"
},
"permanent": "Permanente",
"prompts": {
@@ -143,9 +155,16 @@
"scheduleMessage": "Escolha uma data para publicar este post.",
"show": "Mostrar",
"size": "Tamanho",
"upload": "",
"uploadMessage": "",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?"
"upload": "Carregar",
"uploadMessage": "Selecione uma opção para 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",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Palavra-passe opcional",
"typeName": "Tipo:",
"deleted": "Eliminado com êxito!"
},
"search": {
"images": "Imagens",
@@ -155,7 +174,12 @@
"search": "Pesquisar...",
"typeToSearch": "Escrever para pesquisar...",
"types": "Tipos",
"video": "Vídeos"
"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."
},
"settings": {
"admin": "Admin",
@@ -194,7 +218,7 @@
"newUser": "Novo utilizador",
"password": "Palavra-passe",
"passwordUpdated": "Palavra-passe atualizada!",
"path": "",
"path": "Caminho",
"permissions": {
"create": "Criar ficheiros e pastas",
"delete": "Eliminar ficheiros e pastas",
@@ -202,7 +226,10 @@
"execute": "Executar comandos",
"modify": "Editar ficheiros",
"rename": "Alterar o nome ou mover ficheiros e pastas",
"share": "Partilhar ficheiros"
"share": "Partilhar ficheiros",
"admin": "Administrador",
"api": "Criar e gerir chaves de API de longa duração",
"realtime": "Permitir ligações e actualizações em tempo real"
},
"permissions-name": "Permissões",
"permissionsHelp": "Pode definir o utilizador como administrador ou escolher as permissões manualmente. Se selecionar a opção \"Administrador\", todas as outras opções serão automaticamente selecionadas. A gestão dos utilizadores é um privilégio restringido aos administradores.\n",
@@ -213,13 +240,12 @@
"rulesHelp": "Aqui pode definir um conjunto de regras para permitir ou bloquear o acesso do utilizador a determinados ficheiros ou pastas. Os ficheiros bloqueados não irão aparecer durante a navegação. Suportamos expressões regulares e os caminhos dos ficheiros devem ser relativos à base do utilizador.\n",
"scope": "Base",
"settingsUpdated": "Configurações atualizadas!",
"shareDuration": "",
"shareManagement": "",
"singleClick": "",
"shareManagement": "Gestão de acções",
"singleClick": "Utilizar cliques únicos para abrir ficheiros e diretórios",
"themes": {
"dark": "",
"light": "",
"title": ""
"dark": "Escuro",
"light": "Luz",
"title": "Tema"
},
"user": "Utilizador",
"userCommands": "Comandos",
@@ -230,7 +256,37 @@
"userManagement": "Gestão de utilizadores",
"userUpdated": "Utilizador atualizado!",
"username": "Nome de utilizador",
"users": "Utilizadores"
"users": "Utilizadores",
"enterPassword": "Introduzir a palavra-passe",
"enterPasswordAgain": "Introduzir novamente a palavra-passe",
"passwordsDoNotMatch": "As palavras-passe não coincidem",
"loginMethod": "Método de início de sessão",
"loginMethodDescription": "método de início de sessão permitido para o utilizador.",
"modifyOtherUser": "Modificar utilizador:",
"modifyCurrentUser": "Modificar o utilizador atual",
"themeColor": "Cor do tema",
"disableOfficeEditorDescription": "Uma lista de extensões de ficheiros separada por espaços para desativar o visualizador do OnlyOffice. (por exemplo, '.txt .html').",
"disableOfficeEditor": "Desativar o visualizador do Office para determinadas extensões de ficheiros",
"disableOfficePreviews": "Desativar as pré-visualizações do Office para determinadas extensões de ficheiros",
"disableOfficePreviewsDescription": "Uma lista de extensões de ficheiros, separada por espaços, para desativar as pré-visualizações do Office. (por exemplo, '.txt .html').",
"listingOptions": "Opções de listagem",
"emptyGlobal": "Atualmente, não existem opções de configuração global. É possível editar o yaml de configuração para ajustar as definições do sistema.",
"scopes": "Definir fontes permitidas e o âmbito do utilizador dentro delas",
"UserManagement": "Gestão de utilizadores",
"userNotAdmin": "O utilizador não é um administrador",
"tusUploads": "Carregamentos em pedaços",
"tusUploadsHelp": "O File Browser suporta carregamentos de ficheiros em pedaços, permitindo a criação de carregamentos de ficheiros eficientes, fiáveis, reponíveis e em pedaços, mesmo em redes não fiáveis.",
"tusUploadsChunkSize": "Indica o tamanho máximo de um pedido (os carregamentos diretos serão utilizados para carregamentos mais pequenos). Pode introduzir um número inteiro simples que indique uma entrada em bytes ou uma cadeia de caracteres como 10 MB, 1 GB, etc.",
"tusUploadsRetryCount": "Número de tentativas a efetuar se um pedaço não for carregado.",
"userHomeBasePath": "Caminho de base para as diretorias pessoais dos utilizadores",
"userScopeGenerationPlaceholder": "O âmbito será gerado automaticamente",
"createUserHomeDirectory": "Criar o diretório pessoal do utilizador",
"shareSettings": "Partilhar definições",
"api": "Chaves da API",
"shareDeleted": "Partilhar eliminado!",
"adminOptions": "Opções do utilizador administrador",
"shareDuration": "Expirações",
"searchOptions": "Opções de pesquisa"
},
"sidebar": {
"help": "Ajuda",
@@ -243,7 +299,10 @@
"preview": "Pré-visualizar",
"settings": "Configurações",
"signup": "Registar",
"siteSettings": "Configurações do site"
"siteSettings": "Configurações do site",
"sources": "Fontes",
"updateIsAvailable": "Está disponível uma atualização!",
"fileActions": "Acções do ficheiro"
},
"success": {
"linkCopied": "Link copiado!"
@@ -255,8 +314,81 @@
"seconds": "Segundos",
"unit": "Unidades de tempo"
},
"upload": {},
"share": {},
"api": {},
"index": {}
"upload": {
"abortUpload": "Tens a certeza que queres abortar?"
},
"share": {
"notice": "Nota: qualquer pessoa que tenha acesso à ligação (e à palavra-passe opcional) pode aceder aos ficheiros partilhados. Não é necessário ter sessão iniciada."
},
"api": {
"title": "Chaves da API",
"keyName": "Nome da chave da API:",
"createdAt": "Criado em:",
"expiresAt": "Expira em:",
"permissions": "Permissões:",
"createTitle": "Criar chave API",
"keyNamePlaceholder": "Introduza um nome de chave API único:",
"tokenDuration": "Duração da ficha:",
"durationNumberPlaceholder": "Número",
"days": "Dias",
"months": "Meses",
"permissionNote": "Escolha pelo menos uma permissão para a chave. O utilizador também tem de ter a permissão.",
"createKeyFailed": "Falha ao criar a chave da API.",
"createKeySuccess": "Chave API criada com sucesso.",
"description": "As chaves da API são baseadas no utilizador que as cria. Veja",
"swaggerLinkText": "página do swagger",
"name": "Nome",
"created": "Criado",
"expires": "Expirações",
"actions": "Acções",
"enabled": "Ativado",
"disabled": "Desativado"
},
"index": {
"status": "Estado",
"assessment": "Avaliação",
"files": "Ficheiros",
"folders": "Pastas",
"lastScanned": "Última digitalização",
"quickScan": "Segundos de leitura rápida",
"fullScan": "Segundos de varrimento completo",
"settingsHover": "Definições para o utilizador",
"logout": "Terminar sessão",
"toggleClick": "Alternar clique único",
"toggleDark": "Alternar o modo escuro",
"toggleSticky": "Alternar o modo de aderência",
"of": "de",
"used": "utilizado"
},
"profileSettings": {
"setDateFormat": "Definir o formato exato da data",
"showHiddenFiles": "Mostrar ficheiros ocultos",
"showQuickDownload": "Mostrar o ícone de transferência para acesso rápido",
"previewImages": "Pré-visualizar imagens",
"previewVideos": "Pré-visualizar vídeos",
"previewMotionVideos": "Pré-visualização de movimento para vídeos",
"highQualityPreview": "Pré-visualização de alta qualidade para imagens da galeria",
"editOffice": "Editar/visualizar ficheiros de escritório",
"popupPreview": "Ativar pré-visualização pop-up",
"filePreviewOptions": "Opções de pré-visualização de ficheiros",
"disableUpdateNotifications": "Desativar a apresentação da faixa de atualização de notificações na barra lateral.",
"disableHideSidebar": "Desativar a ocultação automática da barra lateral ao pré-visualizar ou editar ficheiros.",
"autoplayMedia": "Reprodução automática de ficheiros multimédia na pré-visualização",
"deleteWithoutConfirming": "Eliminar ficheiros sem pedido de confirmação",
"sidebarOptions": "Opções da barra lateral",
"disableQuickToggles": "Desativar as alternâncias rápidas",
"disableSearchOptions": "Desativar opções de pesquisa",
"hideSidebarFileActions": "Ocultar acções de ficheiros na barra lateral"
},
"otp": {
"name": "Autenticação de dois factores",
"verifyInstructions": "Introduza o código da sua aplicação de autenticação",
"generate": "Digitalize o código QR com a sua aplicação de autenticação",
"codeInputPlaceholder": "Código de 6 dígitos",
"invalidCodeType": "O código de verificação deve ter 6 dígitos em inglês",
"enabledSuccessfully": "OTP ativado com sucesso",
"verificationFailed": "Falha na verificação",
"verificationSucceed": "Verificação de sucesso",
"generationFailed": "Geração falhada"
}
}
+14 -5
View File
@@ -163,7 +163,8 @@
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Parolă opțională",
"typeName": "Tip:",
"selected": "selectat"
"selected": "selectat",
"deleted": "Șterse cu succes!"
},
"search": {
"images": "Imagini",
@@ -239,7 +240,6 @@
"rulesHelp": "Aici poți defini un set de reguli pentru acest utilizator. Fișierele blocate nu vor apărea in lista și nici nu vor putea fi accesate de utilizator. Expresiile regulate si căile relative la domeniul utilizatorului sunt permise.\n",
"scope": "Domeniu",
"settingsUpdated": "Setări actualizate!",
"shareDuration": "Acțiune Durată",
"shareManagement": "Gestionarea acțiunilor",
"singleClick": "Utilizați un singur clic pentru a deschide fișiere și directoare",
"themes": {
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "O listă de extensii de fișiere separate prin spațiu pentru care se dezactivează vizualizatorul OnlyOffice. (de exemplu, \".txt .html\").",
"disableOfficeEditor": "Dezactivați vizualizatorul office pentru anumite extensii de fișiere",
"disableOfficePreviews": "Dezactivați previzualizările office pentru anumite extensii de fișiere",
"disableOfficePreviewsDescription": "O listă de extensii de fișiere separate prin spațiu pentru care se dezactivează previzualizările office. (de exemplu, \".txt .html\")."
"disableOfficePreviewsDescription": "O listă de extensii de fișiere separate prin spațiu pentru care se dezactivează previzualizările office. (de exemplu, \".txt .html\").",
"adminOptions": "Opțiuni utilizator administrator",
"shareDuration": "Expiră",
"searchOptions": "Opțiuni de căutare"
},
"sidebar": {
"help": "Ajutor",
@@ -302,7 +305,8 @@
"signup": "Înregistrare",
"siteSettings": "Setări site",
"sources": "Surse",
"updateIsAvailable": "O actualizare este disponibilă!"
"updateIsAvailable": "O actualizare este disponibilă!",
"fileActions": "Acțiuni de fișier"
},
"success": {
"linkCopied": "Legătură copiată!"
@@ -374,7 +378,12 @@
"editOffice": "Editarea/vizualizarea fișierelor office",
"disableUpdateNotifications": "Dezactivați afișarea bannerului de actualizare a notificărilor în bara laterală.",
"disableHideSidebar": "Dezactivați ascunderea automată a barei laterale la previzualizarea sau editarea fișierelor.",
"autoplayMedia": "Redarea automată a fișierelor media în previzualizare"
"autoplayMedia": "Redarea automată a fișierelor media în previzualizare",
"deleteWithoutConfirming": "Ștergeți fișiere fără solicitare de confirmare",
"sidebarOptions": "Opțiuni pentru bara laterală",
"disableQuickToggles": "Dezactivați comutatoarele rapide",
"disableSearchOptions": "Dezactivați opțiunile de căutare",
"hideSidebarFileActions": "Ascundeți acțiunile fișierelor în bara laterală"
},
"otp": {
"name": "Autentificare cu doi factori",
+14 -5
View File
@@ -163,7 +163,8 @@
"filesSelected": "выбранные файлы",
"uploadFiles": "Uploading {files} files...",
"typeName": "Тип:",
"selected": "выбранные"
"selected": "выбранные",
"deleted": "Удалено успешно!"
},
"search": {
"images": "Изображения",
@@ -240,7 +241,6 @@
"scope": "Корень",
"setDateFormat": "Установить точный формат даты",
"settingsUpdated": "Настройки применены!",
"shareDuration": "Время расшаренной ссылки",
"shareManagement": "Управление расшаренными ссылками",
"shareDeleted": "Расшаренная ссылка удалена!",
"singleClick": "Открытие файлов и каталогов одним кликом",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Список расширений файлов, для которых необходимо отключить программу просмотра OnlyOffice, разделенный пробелами. (например, '.txt .html').",
"disableOfficeEditor": "Отключение просмотра офисных файлов для определенных расширений файлов",
"disableOfficePreviews": "Отключение предварительного просмотра в офисе для определенных расширений файлов",
"disableOfficePreviewsDescription": "Список расширений файлов, для которых необходимо отключить предварительный просмотр в офисе, разделенный пробелами. (например, '.txt .html')."
"disableOfficePreviewsDescription": "Список расширений файлов, для которых необходимо отключить предварительный просмотр в офисе, разделенный пробелами. (например, '.txt .html').",
"adminOptions": "Параметры пользователя администратора",
"shareDuration": "Срок действия:",
"searchOptions": "Параметры поиска"
},
"sidebar": {
"help": "Помощь",
@@ -302,7 +305,8 @@
"signup": "Зарегистрироваться",
"siteSettings": "Настройки сайта",
"sources": "Источники",
"updateIsAvailable": "Обновление доступно!"
"updateIsAvailable": "Обновление доступно!",
"fileActions": "Действия с файлами"
},
"success": {
"linkCopied": "Ссылка скопирована!"
@@ -374,7 +378,12 @@
"editOffice": "Редактирование/просмотр офисных файлов",
"disableUpdateNotifications": "Отключите показ баннера обновлений в боковой панели.",
"disableHideSidebar": "Отключите автоматическое скрытие боковой панели при предварительном просмотре или редактировании файлов.",
"autoplayMedia": "Автовоспроизведение медиафайлов в режиме предварительного просмотра"
"autoplayMedia": "Автовоспроизведение медиафайлов в режиме предварительного просмотра",
"deleteWithoutConfirming": "Удаление файлов без запроса подтверждения",
"sidebarOptions": "Параметры боковой панели",
"disableQuickToggles": "Отключение быстрых переключений",
"disableSearchOptions": "Отключить параметры поиска",
"hideSidebarFileActions": "Скрыть действия с файлами в боковой панели"
},
"otp": {
"name": "Двухфакторная аутентификация",
+14 -5
View File
@@ -163,7 +163,8 @@
"filesSelected": "vybrané súbory",
"uploadFiles": "Uploading {files} files...",
"typeName": "Typ:",
"selected": "vybrané"
"selected": "vybrané",
"deleted": "Úspešne vymazané!"
},
"search": {
"images": "Obrázky",
@@ -239,7 +240,6 @@
"rulesHelp": "Tu môžete definovať pravidlá pre konkrétneho používateľa. Blokované súbory používateľ nebude vidieť a ani nebude k nim mať prístup. Podporujeme regex a cesty relatívne k používateľovi.\n",
"scope": "Scope",
"settingsUpdated": "Nastavenia upravené!",
"shareDuration": "Trvanie zdieľania",
"shareManagement": "Správa zdieľania",
"shareDeleted": "Zdieľanie odstránené!",
"singleClick": "Používať jeden klik na otváranie súborov a priečinkov",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "Medzerou oddelený zoznam prípon súborov, pre ktoré sa má prehliadač OnlyOffice vypnúť. (napr. \".txt .html\").",
"disableOfficeEditor": "Zakázanie prehliadača Office pre určité prípony súborov",
"disableOfficePreviews": "Zakázanie náhľadov kancelárskych súborov pre určité prípony súborov",
"disableOfficePreviewsDescription": "Medzerou oddelený zoznam prípon súborov, pre ktoré chcete vypnúť náhľady kancelárskych súborov. (napr. \".txt .html\")."
"disableOfficePreviewsDescription": "Medzerou oddelený zoznam prípon súborov, pre ktoré chcete vypnúť náhľady kancelárskych súborov. (napr. \".txt .html\").",
"adminOptions": "Možnosti používateľa správcu",
"shareDuration": "Platnosť končí na",
"searchOptions": "Možnosti vyhľadávania"
},
"sidebar": {
"help": "Pomoc",
@@ -302,7 +305,8 @@
"signup": "Registrovať",
"siteSettings": "Nastavenia stránky",
"sources": "Zdroje",
"updateIsAvailable": "K dispozícii je aktualizácia!"
"updateIsAvailable": "K dispozícii je aktualizácia!",
"fileActions": "Akcie so súbormi"
},
"success": {
"linkCopied": "Odkaz skopírovaný!"
@@ -374,7 +378,12 @@
"editOffice": "Úprava/zobrazenie kancelárskych súborov",
"disableUpdateNotifications": "Zakázanie zobrazovania bannera s oznámením o aktualizácii v bočnom paneli.",
"disableHideSidebar": "Zakázanie automatického skrývania bočného panela pri náhľade alebo úprave súborov.",
"autoplayMedia": "Automatické prehrávanie mediálnych súborov v náhľade"
"autoplayMedia": "Automatické prehrávanie mediálnych súborov v náhľade",
"deleteWithoutConfirming": "Odstránenie súborov bez výzvy na potvrdenie",
"sidebarOptions": "Možnosti bočného panela",
"disableQuickToggles": "Zakázanie rýchlych prepínačov",
"disableSearchOptions": "Zakázanie možností vyhľadávania",
"hideSidebarFileActions": "Skrytie akcií súborov v bočnom paneli"
},
"otp": {
"name": "Dvojfaktorové overovanie",
+155 -23
View File
@@ -31,17 +31,27 @@
"switchView": "Byt vy",
"toggleSidebar": "Växla sidofält",
"update": "Uppdatera",
"upload": "Ladda upp"
"upload": "Ladda upp",
"generateNewOtp": "Återställ och skapa ny tvåfaktorkod",
"verify": "Verifiera",
"copyDownloadLinkToClipboard": "Kopiera nedladdningslänken till urklipp",
"file": "Fil",
"folder": "Mapp",
"submit": "Skicka in",
"openFile": "Öppna fil",
"continue": "Fortsätt",
"edit": "Redigera"
},
"download": {
"downloadFile": "Ladda ner fil",
"downloadFolder": "Ladda ner mapp",
"downloadSelected": ""
"downloadSelected": "Ladda ner Valda"
},
"errors": {
"forbidden": "Du saknar rättigheter till detta",
"internal": "Något gick fel",
"notFound": "Det går inte att nå den här platsen."
"notFound": "Det går inte att nå den här platsen.",
"connection": "Det går inte att nå servern."
},
"files": {
"body": "Huvud",
@@ -59,7 +69,8 @@
"size": "Storlek",
"sortByLastModified": "Sortera på senast ändrad",
"sortByName": "Sortera på namn",
"sortBySize": "Sortera på storlek"
"sortBySize": "Sortera på storlek",
"noPreview": "Förhandsgranskning är inte tillgänglig för den här filen."
},
"help": {
"click": "välj fil eller mapp",
@@ -110,7 +121,8 @@
"submit": "Logga in",
"username": "Användarnamn",
"usernameTaken": "Användarnamn upptaget",
"wrongCredentials": "Fel inloggning"
"wrongCredentials": "Fel inloggning",
"or": "ELLER"
},
"permanent": "Permanent",
"prompts": {
@@ -143,9 +155,16 @@
"scheduleMessage": "Pick a date and time to schedule the publication of this post.",
"show": "Visa",
"size": "Storlek",
"upload": "",
"uploadMessage": "",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?"
"upload": "Ladda upp",
"uploadMessage": "Välj ett alternativ att 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",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Valfritt lösenord",
"typeName": "Typ:",
"deleted": "Raderad framgångsrikt!"
},
"search": {
"images": "Bilder",
@@ -155,7 +174,12 @@
"search": "Sök...",
"typeToSearch": "Skriv för att söka....",
"types": "Typ",
"video": "Video"
"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."
},
"settings": {
"admin": "Admin",
@@ -194,7 +218,7 @@
"newUser": "Ny användare",
"password": "Lösenord",
"passwordUpdated": "Lösenord uppdaterat",
"path": "",
"path": "Väg",
"permissions": {
"create": "Skapa filer och mappar",
"delete": "Ta bort filer och mappar",
@@ -202,7 +226,10 @@
"execute": "Exekvera kommandon",
"modify": "Ändra fil",
"rename": "Byta namn på eller flytta filer och kataloger",
"share": "Dela filer"
"share": "Dela filer",
"admin": "Administratör",
"api": "Skapa och hantera API-nycklar med lång livslängd",
"realtime": "Möjliggör anslutningar och uppdateringar i realtid"
},
"permissions-name": "Rättigheter",
"permissionsHelp": "Du kan ange att användaren ska vara administratör eller välja behörigheterna individuellt. Om du väljer \"administratör \" kommer alla andra alternativ att kontrolleras automatiskt. Hanteringen av användare är fortfarande ett privilegium för en administratör.\n",
@@ -213,13 +240,12 @@
"rulesHelp": "Här kan du definiera en uppsättning regler för godkänna och neka för den här specifika användaren. Den blockerade filen kommer inte upp i listningarna och kommer inte att vara tillgänglig till användaren. Vi stöder regex och sökvägar i förhållande till användarnas omfång.\n",
"scope": "Omfattning",
"settingsUpdated": "Inställning uppdaterad!",
"shareDuration": "",
"shareManagement": "",
"singleClick": "",
"shareManagement": "Aktieförvaltning",
"singleClick": "Öppna filer och kataloger med ett enda klick",
"themes": {
"dark": "",
"light": "",
"title": ""
"dark": "Mörk",
"light": "Ljus",
"title": "Tema"
},
"user": "Användare",
"userCommands": "Kommandon",
@@ -230,7 +256,37 @@
"userManagement": "Användarehantering",
"userUpdated": "Användare uppdaterad!",
"username": "Användarnamn",
"users": "Användare"
"users": "Användare",
"enterPassword": "Ange lösenord",
"enterPasswordAgain": "Ange lösenordet igen",
"passwordsDoNotMatch": "Lösenorden stämmer inte överens",
"loginMethod": "Inloggningsmetod",
"loginMethodDescription": "tillåten inloggningsmetod för användaren.",
"modifyOtherUser": "Ändra användare:",
"modifyCurrentUser": "Ändra nuvarande användare",
"themeColor": "Tema Färg",
"disableOfficeEditorDescription": "En mellanslagsseparerad lista över filändelser som OnlyOffice-visaren ska inaktiveras för. (t.ex. '.txt .html').",
"disableOfficeEditor": "Avaktivera Office Viewer för vissa filtillägg",
"disableOfficePreviews": "Inaktivera Office-förhandsgranskningar för vissa filtillägg",
"disableOfficePreviewsDescription": "En mellanslagsseparerad lista över filändelser för vilka Office-förhandsgranskningar ska inaktiveras. (t.ex. '.txt .html').",
"listingOptions": "Listningsalternativ",
"emptyGlobal": "För närvarande finns det inga globala konfigurationsalternativ, men du kan redigera konfigurationen i yaml för att justera systeminställningarna.",
"scopes": "Definiera tillåtna källor och användarens omfattning inom dem",
"UserManagement": "Användarhantering",
"userNotAdmin": "Användaren är inte administratör",
"tusUploads": "Uppladdningar i bitar",
"tusUploadsHelp": "File Browser har stöd för chunked file uploads, vilket gör det möjligt att skapa effektiva, tillförlitliga, återupptagbara och chunked file uploads även i opålitliga nätverk.",
"tusUploadsChunkSize": "Anger maximal storlek på en begäran (direkta uppladdningar kommer att användas för mindre uppladdningar). Du kan ange ett vanligt heltal som anger en bytesinmatning eller en sträng som 10MB, 1GB etc.",
"tusUploadsRetryCount": "Antal försök som ska göras om en chunk inte kan laddas upp.",
"userHomeBasePath": "Basväg för användares hemkataloger",
"userScopeGenerationPlaceholder": "Omfattningen kommer att genereras automatiskt",
"createUserHomeDirectory": "Skapa användarens hemkatalog",
"shareSettings": "Dela inställningar",
"api": "API-nycklar",
"shareDeleted": "Dela borttaget!",
"adminOptions": "Alternativ för administratörsanvändare",
"shareDuration": "Upphör att gälla",
"searchOptions": "Sökalternativ"
},
"sidebar": {
"help": "Hjälp",
@@ -243,7 +299,10 @@
"preview": "Visa",
"settings": "Inställningar",
"signup": "Registrera",
"siteSettings": "System inställningar"
"siteSettings": "System inställningar",
"sources": "Källor",
"updateIsAvailable": "En uppdatering är tillgänglig!",
"fileActions": "Filåtgärder"
},
"success": {
"linkCopied": "Länk kopierad"
@@ -255,8 +314,81 @@
"seconds": "Sekunder",
"unit": "Tidsenhet"
},
"upload": {},
"share": {},
"api": {},
"index": {}
"upload": {
"abortUpload": "Är du säker på att du vill göra abort?"
},
"share": {
"notice": "Observera: alla som har tillgång till länken (och valfritt lösenord) kan komma åt de delade filerna. Det finns inget krav på att vara inloggad."
},
"api": {
"title": "API-nycklar",
"keyName": "Namn på API-nyckel:",
"createdAt": "Skapad på:",
"expiresAt": "Upphör att gälla kl:",
"permissions": "Behörighet:",
"createTitle": "Skapa API-nyckel",
"keyNamePlaceholder": "Ange ett unikt namn på API-nyckeln:",
"tokenDuration": "Token Varaktighet:",
"durationNumberPlaceholder": "Antal",
"days": "Dagar",
"months": "Månader",
"permissionNote": "Välj minst en behörighet för nyckeln. Din användare måste också ha behörigheten.",
"createKeyFailed": "Misslyckades med att skapa API-nyckel.",
"createKeySuccess": "API-nyckel skapad framgångsrikt.",
"description": "API-nycklar baseras på den användare som skapar dem. Se även",
"swaggerLinkText": "swagger-sida",
"name": "Namn",
"created": "Skapad",
"expires": "Upphör att gälla",
"actions": "Åtgärder",
"enabled": "Aktiverad",
"disabled": "Inaktiverad"
},
"index": {
"status": "Status",
"assessment": "Bedömning",
"files": "Filer",
"folders": "Mappar",
"lastScanned": "Senast skannad",
"quickScan": "Snabbsökning Sekunder",
"fullScan": "Fullständig skanning Sekunder",
"settingsHover": "Inställningar för användare",
"logout": "Logga ut",
"toggleClick": "Toggle Enkelklick",
"toggleDark": "Växla till mörkt läge",
"toggleSticky": "Växla klisterläge",
"of": "av",
"used": "används"
},
"profileSettings": {
"setDateFormat": "Ange exakt datumformat",
"showHiddenFiles": "Visa dolda filer",
"showQuickDownload": "Visa nedladdningsikon för snabb åtkomst",
"previewImages": "Förhandsgranska bilder",
"previewVideos": "Förhandsgranska videor",
"previewMotionVideos": "Förhandsgranskning av rörelse för videor",
"highQualityPreview": "Förhandsgranskning av galleribilder i hög kvalitet",
"editOffice": "Redigera/visa kontorsfiler",
"popupPreview": "Aktivera popup-förhandsgranskning",
"filePreviewOptions": "Alternativ för förhandsgranskning av filer",
"disableUpdateNotifications": "Inaktivera visning av banner för uppdatering av meddelanden i sidofältet.",
"disableHideSidebar": "Inaktivera automatisk döljning av sidofältet när du förhandsgranskar eller redigerar filer.",
"autoplayMedia": "Autoplay av mediefiler i förhandsgranskningen",
"deleteWithoutConfirming": "Radera filer utan bekräftelseprompt",
"sidebarOptions": "Alternativ för sidofält",
"disableQuickToggles": "Inaktivera snabba växlingar",
"disableSearchOptions": "Inaktivera sökalternativ",
"hideSidebarFileActions": "Dölj filåtgärder i sidofältet"
},
"otp": {
"name": "Tvåfaktorsautentisering",
"verifyInstructions": "Ange koden från din autentiseringsapp",
"generate": "Skanna QR-koden med din autentiseringsapp",
"codeInputPlaceholder": "6-siffrig kod",
"invalidCodeType": "Verifieringskoden ska vara 6 engelska siffror",
"enabledSuccessfully": "OTP aktiverat framgångsrikt",
"verificationFailed": "Verifiering misslyckades",
"verificationSucceed": "Verifiering lyckas",
"generationFailed": "Generation misslyckad"
}
}
+14 -5
View File
@@ -163,7 +163,8 @@
"filesSelected": "seçilen dosyalar",
"uploadFiles": "Uploading {files} files...",
"typeName": "Tip:",
"selected": "seçilmiş"
"selected": "seçilmiş",
"deleted": "Başarıyla silindi!"
},
"search": {
"images": "Görseller",
@@ -239,7 +240,6 @@
"rulesHelp": "Burada, bu belirli kullanıcı için bir dizi izin verme ve izin vermeme kuralı tanımlayabilirsiniz. Engellenen dosyalar listelerde görünmeyecek ve kullanıcı bunlara erişemeyecek. Kullanıcı erişimine göre regex ifadeleri destekliyoruz.\n",
"scope": "Kapsam",
"settingsUpdated": "Ayarlar güncellendi!",
"shareDuration": "Paylaşım süresi",
"shareManagement": "Paylaşım yönetimi",
"shareDeleted": "Paylaşım silindi!",
"singleClick": "Dosyaları ve dizinleri açmak için tek tıklamayı kullanın",
@@ -287,7 +287,10 @@
"disableOfficeEditorDescription": "OnlyOffice görüntüleyicisinin devre dışı bırakılacağı dosya uzantılarının boşluk bırakılarak ayrılmış bir listesi. (örneğin, '.txt .html').",
"disableOfficeEditor": "Belirli dosya uzantıları için ofis görüntüleyiciyi devre dışı bırakma",
"disableOfficePreviews": "Belirli dosya uzantıları için ofis önizlemelerini devre dışı bırakma",
"disableOfficePreviewsDescription": "Ofis önizlemelerinin devre dışı bırakılacağı dosya uzantılarının boşluk bırakılarak ayrılmış bir listesi. (örneğin, '.txt .html')."
"disableOfficePreviewsDescription": "Ofis önizlemelerinin devre dışı bırakılacağı dosya uzantılarının boşluk bırakılarak ayrılmış bir listesi. (örneğin, '.txt .html').",
"adminOptions": "Yönetici kullanıcı seçenekleri",
"shareDuration": "Sona erdi",
"searchOptions": "Arama seçenekleri"
},
"sidebar": {
"help": "Yardım",
@@ -302,7 +305,8 @@
"signup": "Kayıt",
"siteSettings": "Site ayarları!",
"sources": "Kaynaklar",
"updateIsAvailable": "Bir güncelleme mevcut!"
"updateIsAvailable": "Bir güncelleme mevcut!",
"fileActions": "Dosya işlemleri"
},
"success": {
"linkCopied": "Link kopyalandı!"
@@ -374,7 +378,12 @@
"editOffice": "Ofis dosyalarını düzenleme/görüntüleme",
"disableUpdateNotifications": "Kenar çubuğunda bildirim güncelleme banner'ını göstermeyi devre dışı bırakın.",
"disableHideSidebar": "Dosyaları önizlerken veya düzenlerken kenar çubuğunu otomatik olarak gizlemeyi devre dışı bırakın.",
"autoplayMedia": "Medya dosyalarını önizlemede otomatik oynatma"
"autoplayMedia": "Medya dosyalarını önizlemede otomatik oynatma",
"deleteWithoutConfirming": "Dosyaları onay istemi olmadan silme",
"sidebarOptions": "Kenar çubuğu seçenekleri",
"disableQuickToggles": "Hızlı geçişleri devre dışı bırakma",
"disableSearchOptions": "Arama seçeneklerini devre dışı bırak",
"hideSidebarFileActions": "Kenar çubuğundaki dosya eylemlerini gizle"
},
"otp": {
"name": "İki Faktörlü Kimlik Doğrulama",
+136 -12
View File
@@ -35,7 +35,12 @@
"toggleSidebar": "Бічна панель",
"update": "Оновити",
"upload": "Завантажити",
"openFile": "Відкрити файл"
"openFile": "Відкрити файл",
"generateNewOtp": "Скинути та згенерувати новий двофакторний код",
"verify": "Підтвердити",
"copyDownloadLinkToClipboard": "Скопіюйте посилання для завантаження в буфер обміну",
"continue": "Продовжуйте",
"edit": "Редагувати"
},
"download": {
"downloadFile": "Завантажити файл",
@@ -116,7 +121,8 @@
"submit": "Увійти",
"username": "Ім'я користувача",
"usernameTaken": "Ім'я користувача вже використовується",
"wrongCredentials": "Невірне ім'я користувача або пароль"
"wrongCredentials": "Невірне ім'я користувача або пароль",
"or": "АБО"
},
"permanent": "Постійний",
"prompts": {
@@ -152,7 +158,13 @@
"size": "Розмір",
"upload": "Завантажити",
"uploadMessage": "Виберіть варіант для завантаження.",
"optionalPassword": "Необов'язковий пароль"
"optionalPassword": "Необов'язковий пароль",
"deleteUserMessage": "Ви впевнені, що хочете видалити цього користувача?",
"selected": "вибрано",
"filesSelected": "вибрані файли",
"uploadFiles": "Uploading {files} files...",
"typeName": "Друкуй:",
"deleted": "Видалено успішно!"
},
"search": {
"images": "Зображення",
@@ -162,7 +174,12 @@
"search": "Пошук...",
"typeToSearch": "Введіть ім'я файлу...",
"types": "Типи",
"video": "Відео"
"video": "Відео",
"path": "Шлях:",
"smallerThan": "Менше, ніж:",
"largerThan": "Більше, ніж:",
"helpText1": "Пошук відбувається за кожним введеним символом (мінімум 3 символи для пошукових термінів).",
"helpText2": "Індекс: Пошук використовує індекс, який автоматично оновлюється з заданим інтервалом (за замовчуванням: 5 хвилин). Пошук, коли програма щойно запущена, може призвести до неповних результатів."
},
"settings": {
"admin": "Адмін",
@@ -209,7 +226,10 @@
"execute": "Виконувати команди",
"modify": "Редагувати файли",
"rename": "Перейменовувати або переміщувати файли та каталоги",
"share": "Ділітися файлами"
"share": "Ділітися файлами",
"admin": "Адміністратор",
"api": "Створюйте та керуйте довготривалими ключами API",
"realtime": "Увімкніть з'єднання та оновлення в режимі реального часу"
},
"permissions-name": "Дозволи",
"permissionsHelp": "Можна настроїти користувача як адміністратора або вибрати індивідуальні дозволи. При виборі \"Адміністратор\" всі інші параметри будуть автоматично вибрані. Керування користувачами - привілей адміністратора.\n",
@@ -221,7 +241,6 @@
"scope": "Корінь",
"setDateFormat": "Встановити точний формат дати",
"settingsUpdated": "Налаштування застосовані!",
"shareDuration": "Тривалість спільного посилання",
"shareManagement": "Управління спільними посиланнями",
"shareDeleted": "Спільне посилання видалено!",
"singleClick": "Відкриття файлів та каталогів одним кліком",
@@ -239,7 +258,36 @@
"userManagement": "Керування користувачами",
"userUpdated": "Користувач змінений!",
"username": "Ім'я користувача",
"users": "Користувачі"
"users": "Користувачі",
"enterPassword": "Введіть пароль",
"enterPasswordAgain": "Введіть пароль ще раз",
"passwordsDoNotMatch": "Паролі не збігаються",
"loginMethod": "Спосіб входу",
"loginMethodDescription": "дозволений метод входу для користувача.",
"modifyOtherUser": "Змінити користувача:",
"modifyCurrentUser": "Змінити поточного користувача",
"themeColor": "Колір теми",
"disableOfficeEditorDescription": "Список розширень файлів через пробіл, для яких слід вимкнути переглядач OnlyOffice. (наприклад, '.txt .html').",
"disableOfficeEditor": "Вимкнути офісний переглядач для певних розширень файлів",
"disableOfficePreviews": "Вимкнути попередній перегляд для певних розширень файлів",
"disableOfficePreviewsDescription": "Список розширень файлів через пробіл, для яких слід вимкнути попередній перегляд. (наприклад, '.txt .html').",
"listingOptions": "Параметри лістингу",
"emptyGlobal": "Наразі немає глобальних параметрів конфігурації, ви можете редагувати конфігураційний файл yaml, щоб налаштувати параметри системи.",
"scopes": "Визначте дозволені джерела та коло користувачів у них",
"UserManagement": "Керування користувачами",
"userNotAdmin": "Користувач не є адміністратором",
"tusUploads": "Часткові завантаження",
"tusUploadsHelp": "File Browser підтримує завантаження файлів частинами, що дозволяє створювати ефективні, надійні, відновлювані та завантажувати файли частинами навіть у ненадійних мережах.",
"tusUploadsChunkSize": "Вказує на максимальний розмір запиту (для менших файлів буде використано пряме завантаження). Ви можете ввести звичайне ціле число, що позначає байти, або рядок, наприклад, 10 МБ, 1 ГБ тощо.",
"tusUploadsRetryCount": "Кількість повторних спроб, якщо фрагмент не вдалося завантажити.",
"userHomeBasePath": "Базовий шлях для домашніх каталогів користувачів",
"userScopeGenerationPlaceholder": "Область дії буде згенеровано автоматично",
"createUserHomeDirectory": "Створення домашнього каталогу користувача",
"shareSettings": "Налаштування спільного доступу",
"api": "Ключі API",
"adminOptions": "Параметри користувача адміністрування",
"shareDuration": "Закінчується",
"searchOptions": "Параметри пошуку"
},
"sidebar": {
"help": "Допомога",
@@ -252,7 +300,10 @@
"preview": "Перегляд",
"settings": "Налаштування",
"signup": "Зареєструватися",
"siteSettings": "Налаштування сайту"
"siteSettings": "Налаштування сайту",
"sources": "Джерела",
"updateIsAvailable": "Доступне оновлення!",
"fileActions": "Дії з файлами"
},
"success": {
"linkCopied": "Посилання скопійоване!"
@@ -264,8 +315,81 @@
"seconds": "Секунди",
"unit": "Одиниця часу"
},
"upload": {},
"share": {},
"api": {},
"index": {}
"upload": {
"abortUpload": "Ви впевнені, що хочете зробити аборт?"
},
"share": {
"notice": "Примітка: будь-хто, хто має доступ за посиланням (і необов'язковий пароль), може отримати доступ до спільних файлів. Для цього не потрібно входити в систему."
},
"api": {
"title": "Ключі API",
"keyName": "Ім'я ключа API:",
"createdAt": "Створено в Ат:",
"expiresAt": "Термін дії закінчується о:",
"permissions": "Дозвольте:",
"createTitle": "Створення ключа API",
"keyNamePlaceholder": "Введіть унікальне ім'я ключа API:",
"tokenDuration": "Тривалість жетона:",
"durationNumberPlaceholder": "Номер",
"days": "Дні",
"months": "Місяці",
"permissionNote": "Виберіть принаймні один дозвіл для ключа. Ваш користувач також повинен мати ці права.",
"createKeyFailed": "Не вдалося створити ключ API.",
"createKeySuccess": "Ключ API створено успішно.",
"description": "Ключі API залежать від користувача, який їх створює. Дивіться",
"swaggerLinkText": "сторінка swagger",
"name": "Ім'я",
"created": "Створено",
"expires": "Закінчується",
"actions": "Дії",
"enabled": "Увімкнено",
"disabled": "Інваліди"
},
"index": {
"status": "Статус",
"assessment": "Оцінка",
"files": "Файли",
"folders": "Папки",
"lastScanned": "Останнє сканування",
"quickScan": "Секунди швидкого сканування",
"fullScan": "Секунди повного сканування",
"settingsHover": "Налаштування для користувача",
"logout": "Вийти з системи",
"toggleClick": "Перемикання одним клацанням миші",
"toggleDark": "Увімкнути темний режим",
"toggleSticky": "Увімкнути липкий режим",
"of": "з",
"used": "використаний"
},
"profileSettings": {
"setDateFormat": "Встановіть точний формат дати",
"showHiddenFiles": "Показати приховані файли",
"showQuickDownload": "Показати іконку завантаження для швидкого доступу",
"previewImages": "Попередній перегляд зображень",
"previewVideos": "Попередній перегляд відео",
"previewMotionVideos": "Попередній перегляд руху для відео",
"highQualityPreview": "Високоякісний попередній перегляд зображень галереї",
"editOffice": "Редагування/перегляд офісних файлів",
"popupPreview": "Увімкнути спливаючий попередній перегляд",
"filePreviewOptions": "Параметри попереднього перегляду файлів",
"disableUpdateNotifications": "Вимкнути показ банера оновлення сповіщень у бічній панелі.",
"disableHideSidebar": "Вимкнути автоматичне приховування бічної панелі під час попереднього перегляду або редагування файлів.",
"autoplayMedia": "Автоматичне відтворення медіафайлів у попередньому перегляді",
"deleteWithoutConfirming": "Видалення файлів без запиту на підтвердження",
"sidebarOptions": "Параметри бічної панелі",
"disableQuickToggles": "Вимкнути швидкі перемикання",
"disableSearchOptions": "Вимкнути параметри пошуку",
"hideSidebarFileActions": "Приховати дії з файлами на бічній панелі"
},
"otp": {
"name": "Двофакторна автентифікація",
"verifyInstructions": "Введіть код з програми-автентифікатора",
"generate": "Відскануйте QR-код за допомогою програми-автентифікатора",
"codeInputPlaceholder": "6-значний код",
"invalidCodeType": "Код верифікації повинен складатися з 6 англійських цифр",
"enabledSuccessfully": "OTP успішно ввімкнено",
"verificationFailed": "Перевірка не вдалася",
"verificationSucceed": "Перевірка пройшла успішно",
"generationFailed": "Покоління зазнало невдачі"
}
}
+135 -12
View File
@@ -35,7 +35,12 @@
"toggleSidebar": "切换侧边栏",
"update": "更新",
"upload": "上传",
"openFile": "打开文件"
"openFile": "打开文件",
"generateNewOtp": "重置并生成新的双因素代码",
"verify": "验证",
"copyDownloadLinkToClipboard": "将下载链接复制到剪贴板",
"continue": "继续",
"edit": "编辑"
},
"download": {
"downloadFile": "下载文件",
@@ -116,7 +121,8 @@
"submit": "登录",
"username": "用户名",
"usernameTaken": "用户名已经被使用",
"wrongCredentials": "用户名或密码错误"
"wrongCredentials": "用户名或密码错误",
"or": "或"
},
"permanent": "永久",
"prompts": {
@@ -152,7 +158,13 @@
"size": "大小",
"upload": "上传",
"uploadMessage": "选择上传选项。",
"optionalPassword": "密码(选填,不填即无密码)"
"optionalPassword": "密码(选填,不填即无密码)",
"deleteUserMessage": "您确定要删除该用户吗?",
"selected": "选定的",
"filesSelected": "所选文件",
"uploadFiles": "Uploading {files} files...",
"typeName": "类型",
"deleted": "已成功删除!"
},
"search": {
"images": "图像",
@@ -162,7 +174,12 @@
"search": "搜索...",
"typeToSearch": "输入以搜索...",
"types": "类型",
"video": "视频"
"video": "视频",
"path": "路径",
"smallerThan": "小于",
"largerThan": "大于:",
"helpText1": "您键入的每个字符都会进行搜索(搜索词最少 3 个字符)。",
"helpText2": "索引:利用索引进行搜索,索引会根据配置的时间间隔(默认为 5 分钟)自动更新。程序刚启动时进行搜索可能会导致搜索结果不完整。"
},
"settings": {
"scopes": "定义允许的来源及其中的用户范围",
@@ -210,7 +227,10 @@
"execute": "执行命令",
"modify": "编辑",
"rename": "重命名或移动文件和文件夹",
"share": "分享文件"
"share": "分享文件",
"admin": "管理员",
"api": "创建和管理长效 API 密钥",
"realtime": "实现实时连接和更新"
},
"permissions-name": "权限",
"permissionsHelp": "您可以将该用户设置为管理员或单独选择各项权限。如果您选择了“管理员”,则其他的选项会被自动选中,同时该用户可以管理其他用户。\n",
@@ -222,7 +242,6 @@
"scope": "目录范围",
"setDateFormat": "显示精确的日期格式",
"settingsUpdated": "设置已更新!",
"shareDuration": "分享期限",
"shareManagement": "分享管理",
"shareDeleted": "分享已删除!",
"singleClick": "使用单击来打开文件和目录",
@@ -240,7 +259,35 @@
"userManagement": "用户管理",
"userUpdated": "用户已更新!",
"username": "用户名",
"users": "用户"
"users": "用户",
"enterPassword": "输入密码",
"enterPasswordAgain": "再次输入密码",
"passwordsDoNotMatch": "密码不匹配",
"loginMethod": "登录方法",
"loginMethodDescription": "用户允许的登录方式。",
"modifyOtherUser": "修改用户:",
"modifyCurrentUser": "修改当前用户",
"themeColor": "主题颜色",
"disableOfficeEditorDescription": "用空格分隔的文件扩展名列表,用于禁用 OnlyOffice 浏览器。(例如,\".txt .html\")。",
"disableOfficeEditor": "禁用某些文件扩展名的 Office 查看器",
"disableOfficePreviews": "禁用某些文件扩展名的 Office 预览功能",
"disableOfficePreviewsDescription": "用空格分隔的文件扩展名列表,用于禁用办公预览。(例如,\".txt .html\")。",
"listingOptions": "列表选项",
"emptyGlobal": "目前没有全局配置选项,您可以编辑配置 yaml 来调整系统设置。",
"UserManagement": "用户管理",
"userNotAdmin": "用户不是管理员",
"tusUploads": "分块上传",
"tusUploadsHelp": "文件浏览器支持分块文件上传,即使在不可靠的网络上也能创建高效、可靠、可恢复和分块的文件上传。",
"tusUploadsChunkSize": "表示请求的最大大小(直接上传将用于较小的上传)。您可以输入表示字节输入的纯整数或字符串,如 10MB、1GB 等。",
"tusUploadsRetryCount": "块上传失败时的重试次数。",
"userHomeBasePath": "用户主目录的基本路径",
"userScopeGenerationPlaceholder": "范围将自动生成",
"createUserHomeDirectory": "创建用户主目录",
"shareSettings": "共享设置",
"api": "API 密钥",
"adminOptions": "管理员用户选项",
"shareDuration": "到期",
"searchOptions": "搜索选项"
},
"sidebar": {
"help": "帮助",
@@ -253,7 +300,10 @@
"preview": "预览",
"settings": "设置",
"signup": "注册",
"siteSettings": "网站设置"
"siteSettings": "网站设置",
"sources": "资料来源",
"updateIsAvailable": "有了最新进展!",
"fileActions": "文件操作"
},
"success": {
"linkCopied": "链接已复制!"
@@ -265,8 +315,81 @@
"seconds": "秒",
"unit": "时间单位"
},
"upload": {},
"share": {},
"api": {},
"index": {}
"upload": {
"abortUpload": "你确定要流产吗?"
},
"share": {
"notice": "注意:任何可以访问链接(和可选密码)的人都可以访问共享文件。无需登录。"
},
"api": {
"title": "应用程序接口密钥",
"keyName": "API 密钥名称:",
"createdAt": "创建于",
"expiresAt": "有效期至",
"permissions": "权限",
"createTitle": "创建 API 密钥",
"keyNamePlaceholder": "输入唯一的 API 密钥名称:",
"tokenDuration": "令牌持续时间:",
"durationNumberPlaceholder": "数量",
"days": "天数",
"months": "月数",
"permissionNote": "为密钥选择至少一个权限。您的用户也必须拥有该权限。",
"createKeyFailed": "创建 API 密钥失败。",
"createKeySuccess": "API 密钥创建成功。",
"description": "API 密钥基于创建密钥的用户。请参见",
"swaggerLinkText": "跩頁",
"name": "名称",
"created": "创建",
"expires": "到期",
"actions": "行动",
"enabled": "已启用",
"disabled": "残疾"
},
"index": {
"status": "现状",
"assessment": "评估",
"files": "文件",
"folders": "文件夹",
"lastScanned": "上次扫描",
"quickScan": "快速扫描秒数",
"fullScan": "完整扫描秒数",
"settingsHover": "用户设置",
"logout": "注销",
"toggleClick": "单击切换",
"toggleDark": "切换黑暗模式",
"toggleSticky": "切换粘贴模式",
"of": "的",
"used": "中古"
},
"profileSettings": {
"setDateFormat": "设置准确的日期格式",
"showHiddenFiles": "显示隐藏文件",
"showQuickDownload": "显示下载图标以便快速访问",
"previewImages": "预览图片",
"previewVideos": "预览视频",
"previewMotionVideos": "视频动态预览",
"highQualityPreview": "高质量预览图库图像",
"editOffice": "编辑/查看办公文件",
"popupPreview": "启用弹出式预览",
"filePreviewOptions": "文件预览选项",
"disableUpdateNotifications": "禁止在侧边栏显示通知更新横幅。",
"disableHideSidebar": "预览或编辑文件时禁用自动隐藏侧边栏。",
"autoplayMedia": "在预览中自动播放媒体文件",
"deleteWithoutConfirming": "无需确认提示即可删除文件",
"sidebarOptions": "侧边栏选项",
"disableQuickToggles": "禁用快速切换",
"disableSearchOptions": "禁用搜索选项",
"hideSidebarFileActions": "隐藏侧边栏中的文件操作"
},
"otp": {
"name": "双因素验证",
"verifyInstructions": "输入验证程序中的代码",
"generate": "使用认证程序扫描 QR 码",
"codeInputPlaceholder": "6 位数代码",
"invalidCodeType": "验证码应为 6 个英文数字",
"enabledSuccessfully": "成功启用 OTP",
"verificationFailed": "验证失败",
"verificationSucceed": "验证成功",
"generationFailed": "一代失败"
}
}
-1
View File
@@ -213,7 +213,6 @@
"rulesHelp": "您可以為該使用者製定一組黑名單或白名單式的規則,被屏蔽的檔案將不會顯示在清單中,使用者也無權限存取,支持相對於目錄範圍的路徑。",
"scope": "目錄範圍",
"settingsUpdated": "設定已更新!",
"shareDuration": "",
"shareManagement": "",
"singleClick": "",
"themes": {
+1 -1
View File
@@ -75,7 +75,7 @@ export const getters = {
isListing: () => getters.isFiles() && state.req.type === 'directory',
selectedCount: () =>
Array.isArray(state.selected) ? state.selected.length : 0,
getFirstSelected: () => state.req.items[state.selected[0]],
getFirstSelected: () => typeof(state.selected[0]) == 'number' ? state.req.items[state.selected[0]] : state.selected[0],
isSingleFileSelected: () =>
getters.selectedCount() === 1 &&
getters.getFirstSelected()?.type != 'directory',
+15 -1
View File
@@ -8,6 +8,10 @@ import { sortedItems } from "@/utils/sort.js";
import { serverHasMultipleSources } from "@/utils/constants.js";
export const mutations = {
setDeletedItem: (value) => {
state.deletedItem = value;
emitStateChanged();
},
setSeenUpdate: (value) => {
state.seenUpdate = value
localStorage.setItem("seenUpdate", value);
@@ -224,6 +228,9 @@ export const mutations = {
i18n.setLocale(value.locale);
}
state.user = value;
state.user.sorting.by = "name";
state.user.sorting.asc = true;
} catch (error) {
console.log(error);
}
@@ -326,6 +333,14 @@ export const mutations = {
if (!state.user.showHidden) {
value.items = value.items.filter((item) => !item.hidden);
}
let sortby = "name"
let asc = true
if (state.user.username && state.user?.username != "publicUser") {
sortby = state.user.sorting.by;
asc = state.user.sorting.asc;
}
// map must be last to ensure the index is set correctly
value.items = sortedItems(value.items, sortby, asc)
value.items.map((item, index) => {
item.index = index;
return item;
@@ -343,7 +358,6 @@ export const mutations = {
emitStateChanged();
},
updateListingItems: () => {
state.req.items = sortedItems(state.req.items, state.user.sorting.by)
mutations.replaceRequest(state.req);
emitStateChanged();
},
+1
View File
@@ -2,6 +2,7 @@ import { reactive } from 'vue';
import { detectLocale } from "@/i18n";
export const state = reactive({
deletedItem: false,
previousHash: "",
multiButtonState: "menu",
multiButtonLastState: "menu",
+23 -10
View File
@@ -2,7 +2,7 @@ import { mutations, getters,state } from "@/store";
import router from "@/router";
import { usersApi } from "@/api";
import { getApiPath } from "@/utils/url.js";
import { recaptcha, loginPage } from "@/utils/constants";
import { recaptcha, loginPage, baseURL } from "@/utils/constants";
export async function setNewToken(token) {
document.cookie = `auth=${token}; path=/`;
@@ -71,16 +71,19 @@ export async function logout() {
// Check if the request was successful and handle the JSON response
if (res.ok) {
const data = await res.json(); // Parse the JSON body
const logoutUrl = data.logoutUrl;
if (logoutUrl) {
// Clean up local state *before* navigating away
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
mutations.setCurrentUser(null);
// Redirect the browser window
window.location.href = logoutUrl;
return; // Stop execution
let logoutUrl = data.logoutUrl;
// Clean up local state *before* navigating away
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
mutations.setCurrentUser(null);
mutations.setJWT("");
if (!logoutUrl) {
logoutUrl = baseURL+"login";
}
// Add a small delay to ensure cookie deletion completes before redirect
setTimeout(() => {
window.location.href = logoutUrl;
}, 100);
return; // Stop execution
} else {
// Handle potential errors from the API, e.g., res.status 401, 500
console.error("Logout API call failed:", res.status, res.statusText);
@@ -92,8 +95,18 @@ export async function logout() {
}
// Fallback for non-oidc/proxy users or if the above logic fails
// More aggressive cookie clearing to handle different browser behaviors
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/; domain=" + window.location.hostname;
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/; domain=." + window.location.hostname;
mutations.setCurrentUser(null);
mutations.setJWT("");
// Clear any session/local storage that might persist
sessionStorage.removeItem('auth');
localStorage.removeItem('auth');
router.push({ path: "/login" });
}
+2
View File
@@ -21,6 +21,7 @@ const passwordAvailable = window.FileBrowser.PasswordAvailable;
const mediaAvailable = window.FileBrowser.MediaAvailable;
const muPdfAvailable = window.FileBrowser.MuPdfAvailable;
const updateAvailable = window.FileBrowser.UpdateAvailable;
const disableNavButtons = window.FileBrowser.DisableNavButtons;
const origin = window.location.origin;
const settings = [
@@ -32,6 +33,7 @@ const settings = [
];
export {
disableNavButtons,
updateAvailable,
muPdfAvailable,
mediaAvailable,
+7 -3
View File
@@ -12,11 +12,15 @@ export function fromNow(date, locale) {
{ label: 'second', seconds: 1 },
];
const formatter = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
// Use absolute value for calculations
const absDiffInSeconds = Math.abs(diffInSeconds);
for (let interval of intervals) {
const count = Math.floor(diffInSeconds / interval.seconds);
const count = Math.floor(absDiffInSeconds / interval.seconds);
if (count > 0) {
return formatter.format(-count, interval.label);
// For past dates (diffInSeconds > 0), we want negative values
// For future dates (diffInSeconds < 0), we want positive values
const formattedCount = diffInSeconds > 0 ? -count : count;
return formatter.format(formattedCount, interval.label);
}
}
return 'just now';
+5 -7
View File
@@ -1,7 +1,5 @@
import { state } from "@/store";
export function sortedItems(items = [], sortby="name") {
export function sortedItems(items = [], sortby="name", asc=true) {
return items.sort((a, b) => {
let valueA = a[sortby];
let valueB = b[sortby];
@@ -15,18 +13,18 @@ export function sortedItems(items = [], sortby="name") {
if (isNumericA && isNumericB) {
// Compare numeric strings as numbers
return state.user.sorting.asc
? parseFloat(valueA) - parseFloat(valueB)
return asc
? parseFloat(valueA) - parseFloat(valueB)
: parseFloat(valueB) - parseFloat(valueA);
}
// Compare non-numeric values as strings
return state.user.sorting.asc
return asc
? valueA.localeCompare(valueB)
: valueB.localeCompare(valueA);
}
// Default sorting for other fields
if (state.user.sorting.asc) {
if (asc) {
return valueA > valueB ? 1 : -1;
} else {
return valueA < valueB ? 1 : -1;
+8 -1
View File
@@ -9,13 +9,21 @@ describe('testSort', () => {
{ name: "1" },
{ name: "10" },
{ name: "Apple" },
{ name: "bee 20" },
{ name: "cave 10.txt" },
{ name: "cave 1.txt" },
{ name: "2" },
{ name: "bee 2" },
]
const expected = [
{ name: "1" },
{ name: "2" },
{ name: "10" },
{ name: "Apple" },
{ name: "bee 2" },
{ name: "bee 20" },
{ name: "cave 1.txt" },
{ name: "cave 10.txt" },
{ name: "zebra" }
]
expect(sortedItems(input, "name")).toEqual(expected);
@@ -73,5 +81,4 @@ describe('testSort', () => {
expect(sortedItems(input, "date")).toEqual(expected);
});
});
+16 -4
View File
@@ -1,6 +1,7 @@
import { state } from '@/store'
import { state, mutations } from '@/store'
import { filesApi } from '@/api'
import { notify } from '@/notify'
import { serverHasMultipleSources } from './constants'
export function checkConflict (files, items) {
if (typeof items === 'undefined' || items === null) {
@@ -100,7 +101,9 @@ export function scanFiles (dt) {
})
}
export async function handleFiles (files, base, overwrite = false) {
export async function handleFiles(files, base, overwrite = false) {
console.log('handleFiles called with base:', base, 'and overwrite:', overwrite)
console.log('Files to upload:', files)
let blockUpdates = false
let c = 0
const count = files.length
@@ -113,6 +116,12 @@ export async function handleFiles (files, base, overwrite = false) {
let basePath = base
let relativePath = file.fullPath || file.webkitRelativePath || file.name
// Do not allow paths to go backwards
if (relativePath.includes('../')) {
console.error('Path traversal detected, upload blocked for file:', relativePath);
return; // or throw an error
}
if (basePath.endsWith('/')) basePath = basePath.slice(0, -1)
if (relativePath.startsWith('/')) relativePath = relativePath.slice(1)
@@ -134,7 +143,10 @@ export async function handleFiles (files, base, overwrite = false) {
`(${c} of ${count}) Uploading ${relativePath}`,
false
)
if (serverHasMultipleSources) {
item.path = `/files/${file.source}${item.path}`
}
console.log(`(${c} of ${count}) Uploading ${relativePath} to ${item.path}`)
await filesApi
.post(item.path, item.file, item.overwrite, percentComplete => {
if (blockUpdates) return
@@ -149,7 +161,6 @@ export async function handleFiles (files, base, overwrite = false) {
const spinner = document.querySelector('.notification-spinner')
if (spinner) spinner.classList.add('hidden')
console.log('Upload successful!', response)
notify.showSuccess('Upload successful!')
})
.catch(error => {
const spinner = document.querySelector('.notification-spinner')
@@ -158,4 +169,5 @@ export async function handleFiles (files, base, overwrite = false) {
throw error
})
}
mutations.setReload(true);
}
-2
View File
@@ -8,8 +8,6 @@
</template>
<script>
import { state } from "@/store";
import { router } from "@/router";
const errors = {
0: {
icon: "cloud_off",
+4
View File
@@ -127,6 +127,10 @@ export default {
router.push(`${routePath}/${urlEncodedSource}`);
return;
}
if (state.sources.current === "") {
notify.showError($t("index.noSources"));
return;
}
this.lastHash = "";
// Set loading to true and reset the error.
mutations.setLoading("files", true);
+1
View File
@@ -136,6 +136,7 @@ export default {
},
async updateSourceInfo() {
if (getters.isLoggedIn()) {
console.log("Updating source info");
const sourceinfo = await filesApi.sources();
mutations.updateSourceInfo(sourceinfo);
if (state.user.permissions.realtime) {
+3
View File
@@ -104,6 +104,9 @@ export default {
};
},
mounted() {
mutations.setCurrentUser(null);
mutations.setJWT("");
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
let redirect = state.route.query.redirect;
if (redirect === "" || redirect === undefined || redirect === null) {
redirect = baseURL + "files/";
+7 -3
View File
@@ -1,7 +1,7 @@
<template>
<header v-if="!isOnlyOffice" :class="['flexbar', { 'dark-mode-header': isDarkMode }]">
<action
v-if="!isShare"
v-if="!isShare && !(disableNavButtons && isListingView)"
icon="close_back"
:label="$t('buttons.close')"
:disabled="isSearchActive"
@@ -11,7 +11,7 @@
<title v-else-if="isSettings" class="topTitle">{{ $t("sidebar.settings") }}</title>
<title v-else class="topTitle">{{ req.name }}</title>
<action
v-if="isListingView"
v-if="isListingView && !disableNavButtons"
class="menu-button"
:icon="viewIcon"
:label="$t('buttons.switchView')"
@@ -19,7 +19,7 @@
:disabled="isSearchActive"
/>
<action
v-else-if="!isShare"
v-else-if="!isShare && !isListingView"
:icon="iconName"
:disabled="noItems"
@click="toggleOverflow"
@@ -32,6 +32,7 @@ import router from "@/router";
import { getters, state, mutations } from "@/store";
import Action from "@/components/Action.vue";
import Search from "@/components/Search.vue";
import { disableNavButtons } from "@/utils/constants";
export default {
name: "UnifiedHeader",
@@ -45,6 +46,9 @@ export default {
};
},
computed: {
disableNavButtons() {
return disableNavButtons && !state.user.permissions.admin;
},
isOnlyOffice() {
return getters.currentView() === "onlyOfficeEditor";
},
+31 -18
View File
@@ -172,7 +172,6 @@ export default {
},
data() {
return {
sortField: "name",
columnWidth: 250 + state.user.gallerySize * 50,
dragCounter: 0,
width: window.innerWidth,
@@ -214,6 +213,9 @@ export default {
const fileTop = fileSection?.getBoundingClientRect().top ?? 0;
category = fileTop <= 0 ? "files" : "folders";
}
if (this.numDirs == 0) {
category = "files"; // If no directories, only files
}
mutations.updateListing({
...state.listing,
@@ -575,7 +577,7 @@ export default {
}
},
keyEvent(event) {
if (state.isSearchActive || getters.currentView() !== "listingView") {
if (state.isSearchActive || getters.currentView() != "listingView" || getters.currentPromptName() != null) {
return;
}
const { key, ctrlKey, metaKey, which } = event;
@@ -831,7 +833,9 @@ export default {
let dt = event.dataTransfer;
let el = event.target;
if (dt.files.length <= 0) return;
if (dt.files.length <= 0) {
return;
}
for (let i = 0; i < 5; i++) {
if (el !== null && !el.classList.contains("item")) {
@@ -841,24 +845,29 @@ export default {
let rawFiles = await upload.scanFiles(dt);
let items = state.req.items;
let path = getters.routePath();
let path = state.req.path;
if (el !== null && el.classList.contains("item") && el.dataset.dir === "true") {
const response = await filesApi.fetchFiles(decodeURIComponent(el.__vue__.url));
path = el.__vue__.url;
const response = await filesApi.fetchFiles(el.__vue__.url);
path = decodeURIComponent(el.__vue__.url);
items = response.items;
}
const uploadFiles = rawFiles.map((file) => ({
file,
name: file.name,
size: file.size,
path: file.fullPath || file.webkitRelativePath || file.name,
fullPath: file.fullPath || file.webkitRelativePath || file.name,
source: state.req.source,
}));
const uploadFiles = rawFiles.map((file) => {
// Here we construct a path that handleFiles will use
const filePath = file.fullPath || file.webkitRelativePath || file.name;
return {
file,
name: file.name,
size: file.size,
path: filePath.replace("//", "/"), // Ensure no double slashes
fullPath: filePath.replace("//", "/"), // Ensure no double slashes
source: state.req.source,
};
});
const conflict = upload.checkConflict(uploadFiles, items);
try {
if (conflict) {
mutations.showHover({
@@ -873,8 +882,8 @@ export default {
await upload.handleFiles(uploadFiles, path);
}
mutations.setReload(true);
} catch {
console.log("failed to upload files");
} catch (e) {
console.error("failed to upload files", e);
}
},
@@ -898,7 +907,7 @@ export default {
});
}
const path = getters.routePath();
const path = state.req.path;
const conflict = upload.checkConflict(uploadFiles, state.req.items);
if (conflict) {
@@ -924,7 +933,6 @@ export default {
) {
asc = true;
}
// Commit the updateSort mutation
mutations.updateListingSortConfig({ field, asc });
mutations.updateListingItems();
@@ -958,6 +966,7 @@ export default {
mutations.showHover({
name: "ContextMenu",
props: {
showCentered: getters.isMobile(),
posX: event.clientX,
posY: event.clientY,
},
@@ -1001,4 +1010,8 @@ export default {
border-radius: 1em;
box-shadow: var(--primaryColor) 0 0 1em;
}
#listingView {
min-height: 90vh !important;
}
</style>
+46 -70
View File
@@ -3,40 +3,17 @@
<div class="preview">
<ExtendedImage v-if="previewType == 'image' || pdfConvertable" :src="raw">
</ExtendedImage>
<audio
v-else-if="previewType == 'audio'"
ref="player"
:src="raw"
controls
:autoplay="autoPlay"
@play="autoPlay = true"
></audio>
<video
v-else-if="previewType == 'video'"
ref="player"
:src="raw"
controls
:autoplay="autoPlay"
@play="autoPlay = true"
>
<track
kind="captions"
v-for="(sub, index) in subtitlesList"
:key="index"
:src="sub.src"
:label="'Subtitle ' + sub.name"
:default="index === 0"
/>
<audio v-else-if="previewType == 'audio'" ref="player" :src="raw" controls :autoplay="autoPlay"
@play="autoPlay = true"></audio>
<video v-else-if="previewType == 'video'" ref="player" :src="raw" controls :autoplay="autoPlay"
@play="autoPlay = true">
<track kind="captions" v-for="(sub, index) in subtitlesList" :key="index" :src="sub.src"
:label="'Subtitle ' + sub.name" :default="index === 0" />
</video>
<div v-else-if="previewType == 'pdf'" class="pdf-wrapper">
<iframe class="pdf" :src="raw"></iframe>
<a
v-if="isMobileSafari"
:href="raw"
target="_blank"
class="button button--flat floating-btn"
>
<a v-if="isMobileSafari" :href="raw" target="_blank" class="button button--flat floating-btn">
<div>
<i class="material-icons">open_in_new</i>{{ $t("buttons.openFile") }}
</div>
@@ -54,12 +31,7 @@
<i class="material-icons">file_download</i>{{ $t("buttons.download") }}
</div>
</a>
<a
target="_blank"
:href="raw"
class="button button--flat"
v-if="req.type != 'directory'"
>
<a target="_blank" :href="raw" class="button button--flat" v-if="req.type != 'directory'">
<div>
<i class="material-icons">open_in_new</i>{{ $t("buttons.openFile") }}
</div>
@@ -68,24 +40,13 @@
</div>
</div>
<button
@click="prev"
@mouseover="hoverNav = true"
@mouseleave="hoverNav = false"
:class="{ hidden: !hasPrevious || !showNav }"
:aria-label="$t('buttons.previous')"
:title="$t('buttons.previous')"
>
<button @click="prev" @mouseover="hoverNav = true" @mouseleave="hoverNav = false"
:class="{ hidden: !hasPrevious || !showNav }" :aria-label="$t('buttons.previous')"
:title="$t('buttons.previous')">
<i class="material-icons">chevron_left</i>
</button>
<button
@click="next"
@mouseover="hoverNav = true"
@mouseleave="hoverNav = false"
:class="{ hidden: !hasNext || !showNav }"
:aria-label="$t('buttons.next')"
:title="$t('buttons.next')"
>
<button @click="next" @mouseover="hoverNav = true" @mouseleave="hoverNav = false"
:class="{ hidden: !hasNext || !showNav }" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
<i class="material-icons">chevron_right</i>
</button>
<link rel="prefetch" :href="previousRaw" />
@@ -191,18 +152,44 @@ export default {
req() {
return state.req;
},
deletedItem() {
return state.deletedItem;
},
},
watch: {
deletedItem() {
if (!state.deletedItem) {
return;
}
if (this.hasNext) {
this.next();
} else if (!this.hasPrevious && !this.hasNext) {
this.close();
} else {
this.prev();
}
mutations.setDeletedItem(false);
},
req() {
if (!getters.isLoggedIn()) {
return;
}
this.updatePreview();
this.toggleNavigation();
mutations.resetSelected();
mutations.addSelected({
name: state.req.name,
path: state.req.path,
size: state.req.size,
type: state.req.type,
source: state.req.source,
url: state.req.url,
});
},
},
async mounted() {
window.addEventListener("keydown", this.key);
mutations.setDeletedItem(false);
window.addEventListener("keydown", this.keyEvent);
this.subtitlesList = await this.subtitles();
this.updatePreview();
mutations.resetSelected();
@@ -216,7 +203,7 @@ export default {
});
},
beforeUnmount() {
window.removeEventListener("keydown", this.key);
window.removeEventListener("keydown", this.keyEvent);
},
methods: {
async subtitles() {
@@ -241,21 +228,6 @@ export default {
}
return subs;
},
deleteFile() {
this.currentPrompt = {
name: "delete",
confirm: () => {
this.listing = this.listing.filter((item) => item.name !== this.name);
if (this.hasNext) {
this.next();
} else if (!this.hasPrevious && !this.hasNext) {
this.close();
} else {
this.prev();
}
},
};
},
prev() {
this.hoverNav = false;
this.$router.replace({ path: this.previousLink });
@@ -264,7 +236,7 @@ export default {
this.hoverNav = false;
this.$router.replace({ path: this.nextLink });
},
async key(event) {
async keyEvent(event) {
if (getters.currentPromptName() != null) {
return;
}
@@ -282,6 +254,9 @@ export default {
this.prev();
}
break;
case "Delete":
mutations.showHover("delete")
break;
case "Escape":
case "Backspace":
this.close();
@@ -401,6 +376,7 @@ export default {
background: rgba(0, 0, 0, 0.5);
color: white;
}
.pdf-wrapper .floating-btn:hover {
background: rgba(0, 0, 0, 0.7);
}
+44 -15
View File
@@ -5,9 +5,35 @@
</div>
<div class="card-content">
<form>
<div class="card-content">
<h3>{{ $t("profileSettings.sidebarOptions") }}</h3>
<div class="settings-items">
<ToggleSwitch
class="item"
v-model="localuser.disableQuickToggles"
:name="$t('profileSettings.disableQuickToggles')"
/>
<ToggleSwitch
class="item"
v-model="localuser.preview.disableHideSidebar"
:name="$t('profileSettings.disableHideSidebar')"
/>
<ToggleSwitch
class="item"
v-model="localuser.hideSidebarFileActions"
:name="$t('profileSettings.hideSidebarFileActions')"
/>
</div>
</div>
<div class="card-content">
<h3>{{ $t("settings.listingOptions") }}</h3>
<div class="settings-items">
<ToggleSwitch
class="item"
v-model="localuser.deleteWithoutConfirming"
:name="$t('profileSettings.deleteWithoutConfirming')"
/>
<ToggleSwitch
class="item"
v-model="localuser.dateFormat"
@@ -56,19 +82,28 @@
v-model="localuser.preview.popup"
:name="$t('profileSettings.popupPreview')"
/>
</div>
<h3>{{ $t("profileSettings.filePreviewOptions") }}</h3>
<div class="settings-items">
<ToggleSwitch
class="item"
v-model="localuser.preview.autoplayMedia"
:name="$t('profileSettings.autoplayMedia')"
/>
</div>
<h3>{{ $t("settings.searchOptions") }}</h3>
<div class="settings-items">
<ToggleSwitch
class="item"
v-model="localuser.preview.disableHideSidebar"
:name="$t('profileSettings.disableHideSidebar')"
v-model="localuser.disableSearchOptions"
:name="$t('profileSettings.disableSearchOptions')"
/>
</div>
<h3 v-if="user.permissions.admin">{{ $t("settings.adminOptions") }}</h3>
<div v-if="user.permissions.admin" class="settings-items">
<ToggleSwitch
v-if="localuser.permissions?.admin"
class="item"
v-model="localuser.disableUpdateNotifications"
:name="$t('profileSettings.disableUpdateNotifications')"
/>
</div>
<div v-if="hasOnlyOfficeEnabled">
<h3>{{ $t("settings.disableOfficePreview") }}</h3>
@@ -117,7 +152,6 @@
</button>
</div>
</div>
<h3>{{ $t("settings.themeColor") }}</h3>
<ButtonGroup
:buttons="colorChoices"
@@ -130,15 +164,6 @@
:locale="localuser.locale"
@update:locale="updateLocale"
></Languages>
<h3 v-if="user.permissions.admin">{{ $t("settings.adminOptions") }}</h3>
<div v-if="user.permissions.admin" class="settings-items">
<ToggleSwitch
v-if="localuser.permissions?.admin"
class="item"
v-model="localuser.disableUpdateNotifications"
:name="$t('profileSettings.disableUpdateNotifications')"
/>
</div>
</div>
</form>
</div>
@@ -266,7 +291,11 @@ export default {
"quickDownload",
"disableOnlyOfficeExt",
"disableOfficePreviewExt",
"deleteWithoutConfirming",
"preview",
"disableQuickToggles",
"disableSearchOptions",
"hideSidebarFileActions",
]);
notify.showSuccess(this.$t("settings.settingsUpdated"));
} catch (e) {
@@ -121,7 +121,7 @@ test("delete file", async({ page, checkForErrors, context }) => {
await page.locator('button[aria-label="Confirm-Delete"]').click();
const popup = page.locator('#popup-notification-content');
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Deleted item successfully! reloading...");
await expect(popup).toHaveText("Deleted successfully!");
checkForErrors();
})
@@ -0,0 +1,37 @@
import { test, expect } from "../test-setup";
test.use({viewport: { width: 750, height: 750 }}); // mobile viewport
test("share download multiple files", async ({ page, checkForErrors, context }) => {
await page.goto("/files/");
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
if (shareHash == "") {
throw new Error("Share hash not found in localStorage");
}
await page.goto("/share/" + shareHash + "/testdata/");
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('button[aria-label="Select multiple"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Select multiple"]').click();
const popup = page.locator('#popup-notification-content');
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Multiple Selection Enabled");
await page.locator('a[aria-label="20130612_142406.jpg"]').click();
await page.locator('a[aria-label="IMG_2578.JPG"]').click();
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
await expect(page.locator('.selected-count-header')).toHaveText('3 selected');
await page.locator('button[aria-label="Download"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Download"]').click();
const popup2 = page.locator('#popup-notification-content');
await popup2.waitFor({ state: 'visible' });
await expect(popup2).toHaveText("Downloading...");
checkForErrors();
});
@@ -1,4 +1,3 @@
import { test, expect } from "../test-setup";
test("breadcrumbs navigation checks", async ({ page, checkForErrors, context }) => {
@@ -46,11 +45,11 @@ test("share file works", async ({ page, checkForErrors, context }) => {
});
test("share download single file", async ({ page, checkForErrors, context }) => {
await page.goto("/files/");
await page.goto("/files/files/");
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
if (shareHash == "") {
if (!shareHash) {
throw new Error("Share hash not found in localStorage");
}
@@ -64,37 +63,3 @@ test("share download single file", async ({ page, checkForErrors, context }) =>
await expect(popup).toHaveText("Downloading...");
checkForErrors();
});
test("share download multiple files", async ({ page, checkForErrors, context }) => {
await page.goto("/files/");
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
if (shareHash == "") {
throw new Error("Share hash not found in localStorage");
}
await page.goto("/share/" + shareHash + "/testdata/");
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('button[aria-label="Select multiple"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Select multiple"]').click();
const popup = page.locator('#popup-notification-content');
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Multiple Selection Enabled");
await page.locator('a[aria-label="20130612_142406.jpg"]').click();
await page.locator('a[aria-label="IMG_2578.JPG"]').click();
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
await expect(page.locator('.selected-count-header')).toHaveText('3 selected');
await page.locator('button[aria-label="Download"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Download"]').click();
const popup2 = page.locator('#popup-notification-content');
await popup2.waitFor({ state: 'visible' });
await expect(popup2).toHaveText("Downloading...");
checkForErrors();
});
@@ -97,7 +97,7 @@ test("delete file", async({ page, checkForErrors, context }) => {
await page.locator('button[aria-label="Confirm-Delete"]').click();
const popup = page.locator('#popup-notification-content');
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Deleted item successfully! reloading...");
await expect(popup).toHaveText("Deleted successfully!");
checkForErrors();
})
@@ -0,0 +1,37 @@
import { test, expect } from "../test-setup";
test.use({viewport: { width: 750, height: 750 }}); // mobile viewport
test("share download multiple files", async ({ page, checkForErrors, context }) => {
await page.goto("/files/files/");
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
if (!shareHash) {
throw new Error("Share hash not found in localStorage");
}
await page.goto("/files/share/" + shareHash + "/testdata/");
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('button[aria-label="Select multiple"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Select multiple"]').click();
const popup = page.locator('#popup-notification-content');
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Multiple Selection Enabled");
await page.locator('a[aria-label="20130612_142406.jpg"]').click();
await page.locator('a[aria-label="IMG_2578.JPG"]').click();
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
await expect(page.locator('.selected-count-header')).toHaveText('3 selected');
await page.locator('button[aria-label="Download"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Download"]').click();
const popup2 = page.locator('#popup-notification-content');
await popup2.waitFor({ state: 'visible' });
await expect(popup2).toHaveText("Downloading...");
checkForErrors();
});
+1 -36
View File
@@ -27,7 +27,6 @@ test("root share path is valid", async ({ page, checkForErrors, openContextMenu,
await page.goto("/files/files/");
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
await openContextMenu();
await page.locator('button[aria-label="Share"]').click();
await expect(page.locator('div[aria-label="share-path"]')).toHaveText('Path: /');
checkForErrors();
@@ -51,7 +50,7 @@ test("share download single file", async ({ page, checkForErrors, context }) =>
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
if (shareHash == "") {
if (!shareHash) {
throw new Error("Share hash not found in localStorage");
}
@@ -64,38 +63,4 @@ test("share download single file", async ({ page, checkForErrors, context }) =>
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Downloading...");
checkForErrors();
});
test("share download multiple files", async ({ page, checkForErrors, context }) => {
await page.goto("/files/files/");
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
if (shareHash == "") {
throw new Error("Share hash not found in localStorage");
}
await page.goto("/files/share/" + shareHash + "/testdata/");
await expect(page).toHaveTitle("Graham's Filebrowser - Share - testdata");
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('button[aria-label="Select multiple"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Select multiple"]').click();
const popup = page.locator('#popup-notification-content');
await popup.waitFor({ state: 'visible' });
await expect(popup).toHaveText("Multiple Selection Enabled");
await page.locator('a[aria-label="20130612_142406.jpg"]').click();
await page.locator('a[aria-label="IMG_2578.JPG"]').click();
await page.locator('a[aria-label="gray-sample.jpg"]').click({ button: "right" });
await page.locator('.selected-count-header').waitFor({ state: 'visible' });
await expect(page.locator('.selected-count-header')).toHaveText('3 selected');
await page.locator('button[aria-label="Download"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="Download"]').click();
const popup2 = page.locator('#popup-notification-content');
await popup2.waitFor({ state: 'visible' });
await expect(popup2).toHaveText("Downloading...");
checkForErrors();
});
+2 -6
View File
@@ -10,12 +10,8 @@ export const test = base.extend<{
},
openContextMenu: async ({ page }, use) => {
await use(async () => {
const listingView = await page.locator('#listingView');
const box = await listingView.boundingBox();
if (!box) throw new Error("Could not find listingView bounding box");
const x = box.x + box.width / 2;
const y = box.y + box.height - 1;
await page.mouse.click(x, y, { button: "right" });
await page.locator('button[aria-label="File-Actions"]').waitFor({ state: 'visible' });
await page.locator('button[aria-label="File-Actions"]').click();
});
}
});