Compare commits

...
Author SHA1 Message Date
Graham Steffaniak 785c93c52a updated linting 2025-07-25 17:00:57 -05:00
Graham Steffaniak 51cd55fe8e updated 2025-07-25 16:46:39 -05:00
Graham Steffaniak 794e23762d updated 2025-07-25 16:28:26 -05:00
Graham Steffaniak 7d55dd90a1 updatd theme 2025-07-25 16:13:42 -05:00
Graham Steffaniak 1ab544cea3 updated theme 2025-07-25 16:13:30 -05:00
Graham Steffaniak 587db41837 add mobile swipe to change photo and theme changes (#1016) 2025-07-25 13:16:46 -05:00
Graham Steffaniak 72c983bd56 Merge branch 'dev/v0.7.17' of github.com:gtsteffaniak/filebrowser into dev/v0.7.17 2025-07-24 18:52:54 -05:00
Graham Steffaniakandmordillo 3efadf1809 Fix multi selection mobile (#1012)
Co-authored-by: mordillo <121123040+mordilloSan@users.noreply.github.com>
2025-07-24 18:52:39 -05:00
Graham Steffaniak 1ee18cc1ec Merge branch 'dev/v0.7.17' of github.com:gtsteffaniak/filebrowser into dev/v0.7.17 2025-07-24 17:06:01 -05:00
Graham Steffaniakandmordillo 6e9c77cab4 Add css example (#1011)
Co-authored-by: mordillo <121123040+mordilloSan@users.noreply.github.com>
2025-07-24 17:01:50 -05:00
mordilloandGraham Steffaniak 0996ecffb5 Add light and dark background settings to frontend configuration (#997)
Co-authored-by: Graham Steffaniak <42989099+gtsteffaniak@users.noreply.github.com>
2025-07-24 11:34:05 -05:00
Graham Steffaniak 8ba3570740 added more changes (#1009) 2025-07-24 11:34:05 -05:00
Graham Steffaniak 86e5fce385 update partition size (#1003) 2025-07-24 11:34:05 -05:00
Graham Steffaniak c72bd93cc6 updated tests 2025-07-24 11:34:05 -05:00
mordilloandGraham Steffaniak 89d3dfc901 Add light and dark background settings to frontend configuration (#997)
Co-authored-by: Graham Steffaniak <42989099+gtsteffaniak@users.noreply.github.com>
2025-07-24 11:32:58 -05:00
Graham Steffaniak b21810308f added more changes (#1009) 2025-07-23 17:53:08 -05:00
Graham Steffaniak d5edac2415 update partition size (#1003) 2025-07-22 18:52:37 -05:00
Graham Steffaniak 28be159067 updated tests 2025-07-22 16:48:52 -05:00
Graham Steffaniak f95fa49cf6 updated with fixes for main issues (#991) 2025-07-21 14:52:37 -05:00
81 changed files with 1390 additions and 767 deletions

No files matched your search

+1
View File
@@ -3,6 +3,7 @@ name: PR Request
on:
pull_request:
branches:
- "main"
- "dev/v*.*.*"
- "beta/v*.*.*"
- "stable/v*.*.*"
+1
View File
@@ -48,3 +48,4 @@ yarn-error.log*
bin/
build/
*__debug*
backend/custom.css
+35
View File
@@ -2,6 +2,41 @@
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.17-beta
See an example of custom css styling that uses the reduce-rounded-corners.css by default and allows users to choose other themes. You can add your own themes as well that users can choose from in profile settings:
```
frontend:
styling:
lightBackground: "#f0f0f0" # or names of css colors
darkBackground: "#121212"
customCSS: "custom.css" # custom css file always applies first, then user themes on top of that.
customThemes:
"default": # if "default" is specified as the name, it will be the default option
description: "Reduce rounded corners"
css: "reduce-rounded-corners.css" # path to css file to use
"original":
description: "Original rounded theme"
css: "" # you could default to no styling changes this way.
```
**New Features**:
- more custom styling options (thanks @mordilloSan for #997)
- background colors can be easily set in config
- provided an example `reduce-rounded-corners.css` available by default in docker. (#986, #837)
- added feature to specify multiple css themes that users can choose from in profile settings
- swipe between photos on mobile (#825)
**Notes**:
- changed partition calculations on linux for total disk size (#982)
- upload conflict detection for folders offers "replace all" if the folder already exists in target location.
**BugFixes**:
- TOTP prompt not showing generated code issue https://github.com/gtsteffaniak/filebrowser/issues/996
- select mulitple deselect on mobile (#1002)
- viewing svg images.
## v0.7.16-beta
**Notes**:
+2 -1
View File
@@ -32,7 +32,8 @@ WORKDIR /home/filebrowser
# copy config to two locations for compatibility
COPY --from=base --chown=filebrowser:1000 [ "/app/filebrowser", "/app/config.yaml", "./" ]
COPY --from=base --chown=filebrowser:1000 [ "/app/config.yaml", "./data/config.yaml" ]
COPY --from=nbuild --chown=filebrowser:1000 /app/dist/ ./http/dist/
COPY --from=base --chown=filebrowser:1000 [ "/app/reduce-rounded-corners.css", "./" ]
COPY --from=nbuild --chown=filebrowser:1000 [ "/app/dist/", "./http/dist/" ]
## sanity checks
RUN [ "filebrowser", "version" ]
RUN [ "ffmpeg", "-version" ]
+4 -3
View File
@@ -25,9 +25,10 @@ RUN apk --no-cache add ca-certificates mailcap tzdata
RUN adduser -D -s /bin/true -u 1000 filebrowser
USER filebrowser
WORKDIR /home/filebrowser
COPY --from=base --chown=filebrowser:1000 /app/filebrowser ./
COPY --from=base --chown=filebrowser:1000 /app/config.yaml ./data/config.yaml
COPY --from=nbuild --chown=filebrowser:1000 /app/dist/ ./http/dist/
COPY --from=base --chown=filebrowser:1000 [ "/app/filebrowser", "./" ]
COPY --from=base --chown=filebrowser:1000 [ "/app/config.yaml", "./data/config.yaml" ]
COPY --from=base --chown=filebrowser:1000 [ "/app/reduce-rounded-corners.css", "./" ]
COPY --from=nbuild --chown=filebrowser:1000 [ "/app/dist/", "./http/dist/" ]
## sanity checks
RUN [ "filebrowser", "version" ]
USER root
+39
View File
@@ -156,7 +156,41 @@ func RefreshIndex(source string, path string, isDir bool) error {
return idx.RefreshFileInfo(iteminfo.FileOptions{Path: path, IsDir: isDir})
}
// validateMoveDestination checks if a move/rename operation is valid
// It prevents moving a directory into itself or its subdirectories
func validateMoveDestination(src, dst string, isSrcDir bool) error {
// Clean and normalize paths
src = filepath.Clean(src)
dst = filepath.Clean(dst)
// If source is a directory, check if destination is within source
if isSrcDir {
// Get the parent directory of the destination
dstParent := filepath.Dir(dst)
// Check if destination parent is the source directory or a subdirectory of it
if strings.HasPrefix(dstParent+string(filepath.Separator), src+string(filepath.Separator)) || dstParent == src {
return fmt.Errorf("cannot move directory '%s' to a location within itself: '%s'", src, dst)
}
}
// Check if destination parent directory exists
dstParent := filepath.Dir(dst)
if dstParent != "." && dstParent != "/" {
if _, err := os.Stat(dstParent); os.IsNotExist(err) {
return fmt.Errorf("destination directory does not exist: '%s'", dstParent)
}
}
return nil
}
func MoveResource(isSrcDir, isDestDir bool, sourceIndex, destIndex, realsrc, realdst string) error {
// Validate the move operation before executing
if err := validateMoveDestination(realsrc, realdst, isSrcDir); err != nil {
return err
}
err := fileutils.MoveFile(realsrc, realdst)
if err != nil {
return err
@@ -167,6 +201,11 @@ func MoveResource(isSrcDir, isDestDir bool, sourceIndex, destIndex, realsrc, rea
}
func CopyResource(isSrcDir, isDestDir bool, sourceIndex, destIndex, realsrc, realdst string) error {
// Validate the copy operation before executing
if err := validateMoveDestination(realsrc, realdst, isSrcDir); err != nil {
return err
}
err := fileutils.CopyFile(realsrc, realdst)
if err != nil {
return err
+2 -2
View File
@@ -10,8 +10,8 @@ import (
"github.com/gtsteffaniak/go-logger/logger"
)
const PermFile = 0640
const PermDir = 0750
const PermFile = 0644
const PermDir = 0755
// MoveFile moves a file from src to dst.
// By default, the rename system call is used. If src and dst point to different volumes,
+3 -3
View File
@@ -8,12 +8,12 @@ import (
)
var (
revokedApiKeyList map[string]bool
revokedApiKeyList map[string]struct{}
revokeMu sync.Mutex
)
func init() {
revokedApiKeyList = make(map[string]bool)
revokedApiKeyList = make(map[string]struct{})
}
// Auther is the authentication interface.
@@ -32,5 +32,5 @@ func IsRevokedApiKey(key string) bool {
func RevokeAPIKey(key string) {
revokeMu.Lock()
defer revokeMu.Unlock()
revokedApiKeyList[key] = true
revokedApiKeyList[key] = struct{}{}
}
+3 -14
View File
@@ -37,9 +37,6 @@ func validateUserInfo() {
if updateLoginType(user) {
updateUser = true
}
if updateUserDefaults(user) {
updateUser = true
}
adminUser := settings.Config.Auth.AdminUsername
adminPass := settings.Config.Auth.AdminPassword
passwordEnabled := settings.Config.Auth.Methods.PasswordAuth.Enabled
@@ -66,17 +63,9 @@ func validateUserInfo() {
}
}
func updateUserDefaults(user *users.User) bool {
if user.DisableOfficePreviewExt != "" && user.DisablePreviewExt == "" {
user.DisablePreviewExt = user.DisableOfficePreviewExt
return true
}
return false
}
func updateUserScopes(user *users.User) bool {
newScopes := []users.SourceScope{}
seen := make(map[string]bool)
seen := make(map[string]struct{})
// Build map for existing scopes by Name
existing := make(map[string]users.SourceScope)
@@ -106,12 +95,12 @@ func updateUserScopes(user *users.User) bool {
Name: realsource.Path,
Scope: existingScope.Scope,
})
seen[realsource.Path] = true
seen[realsource.Path] = struct{}{}
}
// Preserve user-defined scopes not matching current sources, append to end
for _, s := range user.Scopes {
if !seen[s.Name] {
if _, ok := seen[s.Name]; !ok {
newScopes = append(newScopes, s)
}
}
+73
View File
@@ -0,0 +1,73 @@
package settings
import (
"regexp"
"strings"
"github.com/gtsteffaniak/go-logger/logger"
)
// Strict hex color regex: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
var hexColorRE = regexp.MustCompile(`^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$`)
// All valid CSS named colors (W3C spec, lower-case)
var cssNamedColors = map[string]struct{}{
"aliceblue": {}, "antiquewhite": {}, "aqua": {}, "aquamarine": {}, "azure": {},
"beige": {}, "bisque": {}, "black": {}, "blanchedalmond": {}, "blue": {}, "blueviolet": {},
"brown": {}, "burlywood": {}, "cadetblue": {}, "chartreuse": {}, "chocolate": {},
"coral": {}, "cornflowerblue": {}, "cornsilk": {}, "crimson": {}, "cyan": {},
"darkblue": {}, "darkcyan": {}, "darkgoldenrod": {}, "darkgray": {}, "darkgreen": {},
"darkgrey": {}, "darkkhaki": {}, "darkmagenta": {}, "darkolivegreen": {}, "darkorange": {},
"darkorchid": {}, "darkred": {}, "darksalmon": {}, "darkseagreen": {}, "darkslateblue": {},
"darkslategray": {}, "darkslategrey": {}, "darkturquoise": {}, "darkviolet": {}, "deeppink": {},
"deepskyblue": {}, "dimgray": {}, "dimgrey": {}, "dodgerblue": {}, "firebrick": {},
"floralwhite": {}, "forestgreen": {}, "fuchsia": {}, "gainsboro": {}, "ghostwhite": {},
"gold": {}, "goldenrod": {}, "gray": {}, "green": {}, "greenyellow": {}, "grey": {},
"honeydew": {}, "hotpink": {}, "indianred": {}, "indigo": {}, "ivory": {},
"khaki": {}, "lavender": {}, "lavenderblush": {}, "lawngreen": {}, "lemonchiffon": {},
"lightblue": {}, "lightcoral": {}, "lightcyan": {}, "lightgoldenrodyellow": {}, "lightgray": {},
"lightgreen": {}, "lightgrey": {}, "lightpink": {}, "lightsalmon": {}, "lightseagreen": {},
"lightskyblue": {}, "lightslategray": {}, "lightslategrey": {}, "lightsteelblue": {}, "lightyellow": {},
"lime": {}, "limegreen": {}, "linen": {}, "magenta": {}, "maroon": {}, "mediumaquamarine": {},
"mediumblue": {}, "mediumorchid": {}, "mediumpurple": {}, "mediumseagreen": {}, "mediumslateblue": {},
"mediumspringgreen": {}, "mediumturquoise": {}, "mediumvioletred": {}, "midnightblue": {}, "mintcream": {},
"mistyrose": {}, "moccasin": {}, "navajowhite": {}, "navy": {}, "oldlace": {},
"olive": {}, "olivedrab": {}, "orange": {}, "orangered": {}, "orchid": {},
"palegoldenrod": {}, "palegreen": {}, "paleturquoise": {}, "palevioletred": {}, "papayawhip": {},
"peachpuff": {}, "peru": {}, "pink": {}, "plum": {}, "powderblue": {},
"purple": {}, "rebeccapurple": {}, "red": {}, "rosybrown": {}, "royalblue": {},
"saddlebrown": {}, "salmon": {}, "sandybrown": {}, "seagreen": {}, "seashell": {},
"sienna": {}, "silver": {}, "skyblue": {}, "slateblue": {}, "slategray": {},
"slategrey": {}, "snow": {}, "springgreen": {}, "steelblue": {}, "tan": {},
"teal": {}, "thistle": {}, "tomato": {}, "turquoise": {}, "violet": {},
"wheat": {}, "white": {}, "whitesmoke": {}, "yellow": {}, "yellowgreen": {},
// And the special values:
"transparent": {}, "currentcolor": {},
}
func FallbackColor(val, defaultColor string) string {
val = strings.TrimSpace(val)
if val == "" {
return defaultColor
}
if hexColorRE.MatchString(val) {
return val
}
lc := strings.ToLower(val)
// CSS color functions
if strings.HasPrefix(lc, "rgb(") || strings.HasPrefix(lc, "rgba(") ||
strings.HasPrefix(lc, "hsl(") || strings.HasPrefix(lc, "hsla(") {
return val
}
// CSS variable
if strings.HasPrefix(lc, "var(") {
return val
}
// Named colors and special values
if _, ok := cssNamedColors[lc]; ok {
return val
}
// Log a warning if the color is invalid
logger.Warningf("Invalid CSS color value provided: '%s'. Falling back to default: '%s'", val, defaultColor)
return defaultColor
}
+52
View File
@@ -59,6 +59,37 @@ func setupFrontend() {
Url: "https://github.com/gtsteffaniak/filebrowser/wiki",
})
}
Config.Frontend.Styling.LightBackground = FallbackColor(Config.Frontend.Styling.LightBackground, "white")
Config.Frontend.Styling.DarkBackground = FallbackColor(Config.Frontend.Styling.DarkBackground, "#141D24")
Config.Frontend.Styling.CustomCSS = readCustomCSS(Config.Frontend.Styling.CustomCSS)
Config.Frontend.Styling.CustomThemeOptions = map[string]CustomTheme{}
for name, theme := range Config.Frontend.Styling.CustomThemes {
thisTheme := CustomTheme{
Description: theme.Description,
CSS: theme.CSS, // Copy the CSS file path from the original theme
}
Config.Frontend.Styling.CustomThemeOptions[name] = thisTheme
thisTheme.CSS = readCustomCSS(theme.CSS) // Now read the CSS file content and replace the path
Config.Frontend.Styling.CustomThemes[name] = thisTheme
}
noThemes := len(Config.Frontend.Styling.CustomThemes) == 0
_, ok := Config.Frontend.Styling.CustomThemes["default"]
if noThemes || !ok {
theme := CustomTheme{
Description: "The default theme",
CSS: "",
}
if noThemes {
// If there are no themes at all, initialize the map with just default
Config.Frontend.Styling.CustomThemes = map[string]CustomTheme{
"default": theme,
}
} else {
// If there are themes but no default, add default to existing themes
Config.Frontend.Styling.CustomThemes["default"] = theme
}
Config.Frontend.Styling.CustomThemeOptions["default"] = theme
}
}
func getRealPath(path string) string {
@@ -335,6 +366,7 @@ func setDefaults(generate bool) Settings {
Frontend: Frontend{
Name: "FileBrowser Quantum",
},
UserDefaults: UserDefaults{
StickySidebar: true,
LockPassword: false,
@@ -491,3 +523,23 @@ func modifyExcludeInclude(config *Source) {
normalize(config.Config.Include.RootFiles, true)
normalize(config.Config.NeverWatchPaths, true)
}
func readCustomCSS(path string) string {
if path == "" {
return ""
}
content, err := os.ReadFile(path)
if err != nil {
// Only log if the file exists but cannot be read (e.g., permission error)
if !os.IsNotExist(err) {
logger.Warningf("Could not read custom CSS file %s: %v", path, err)
return ""
}
}
if len(content) > 128*1024 {
logger.Warningf("Custom CSS file %s is too large (%d bytes), ignoring", path, len(content))
return ""
}
logger.Infof("Loaded custom CSS from: %s (%d bytes)", path, len(content))
return string(content)
}
+28 -20
View File
@@ -226,27 +226,35 @@ func formatLine(line string) string {
const targetCol = 42
const defaultPadding = 1
idx := strings.Index(line, "#")
// If there's no comment, do nothing.
if idx == -1 {
return line
// Find the first # character that is NOT inside double or single quotes.
inSingle, inDouble := false, false
for i := 0; i < len(line); i++ {
c := line[i]
switch c {
case '\'':
if !inDouble {
inSingle = !inSingle
}
case '"':
if !inSingle {
inDouble = !inDouble
}
case '#':
if !inSingle && !inDouble {
// found a comment
contentPart := line[:i]
commentPart := line[i:]
trimmedContent := strings.TrimRight(contentPart, " ")
if len(trimmedContent) >= targetCol {
return trimmedContent + strings.Repeat(" ", defaultPadding) + commentPart
}
paddingNeeded := targetCol - len(trimmedContent)
return trimmedContent + strings.Repeat(" ", paddingNeeded) + commentPart
}
}
}
// Separate the content (before #) from the comment (from # onwards).
contentPart := line[:idx]
commentPart := line[idx:]
// The YAML encoder adds a space; trim it so we measure only the content length.
trimmedContent := strings.TrimRight(contentPart, " ")
// If the content already extends past our target column, just add a single space.
if len(trimmedContent) >= targetCol {
return trimmedContent + strings.Repeat(" ", defaultPadding) + commentPart
}
// Otherwise, calculate the needed padding to reach the target column and add it.
paddingNeeded := targetCol - len(trimmedContent)
return trimmedContent + strings.Repeat(" ", paddingNeeded) + commentPart
// No comment found
return line
}
// alignComments formats each line of the YAML string independently.
+15
View File
@@ -110,6 +110,20 @@ type Frontend struct {
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
Styling StylingConfig `json:"styling"`
}
type StylingConfig struct {
CustomCSS string `json:"customCSS"` // if a valid path to a css file is provided, it will be applied for all users. (eg. "reduce-rounded-corners.css")
LightBackground string `json:"lightBackground"` // specify a valid CSS color property value to use as the background color in light mode
DarkBackground string `json:"darkBackground"` // Specify a valid CSS color property value to use as the background color in dark mode
CustomThemes map[string]CustomTheme `json:"customThemes"` // A list of custom css files that each user can select to override the default styling. if "default" is key name then it will be the default option.
CustomThemeOptions map[string]CustomTheme `json:"-"` // not exposed
}
type CustomTheme struct {
Description string `json:"description"` // The description of the theme to display in the UI.
CSS string `json:"css,omitempty"` // The css file path and filename to use for the theme.
}
type ExternalLink struct {
@@ -148,4 +162,5 @@ type UserDefaults struct {
FileLoading users.FileLoading `json:"fileLoading"` // upload and download settings
DisableOfficePreviewExt string `json:"disableOfficePreviewExt"` // deprecated: use disablePreviewExt instead
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // list of file extensions to disable onlyoffice editor for
CustomTheme string `json:"customTheme"` // Name of theme to use chosen from custom themes config.
}
+1
View File
@@ -101,6 +101,7 @@ type NonAdminEditable struct {
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // deprecated
DisablePreviewExt string `json:"disablePreviewExt"` // comma separated list of file extensions to disable preview for
DisableViewingExt string `json:"disableViewingExt"` // comma separated list of file extensions to disable viewing for
CustomTheme string `json:"customTheme"` // Name of theme to use chosen from custom themes config.
}
type FileLoading struct {
+8 -5
View File
@@ -133,14 +133,17 @@ func StartHttp(ctx context.Context, storage *storage.Storage, shutdownComplete c
// Static and index file handlers
router.HandleFunc(fmt.Sprintf("GET %vstatic/", config.Server.BaseURL), staticFilesHandler)
router.HandleFunc(config.Server.BaseURL, indexHandler)
// health
router.HandleFunc(config.Server.BaseURL, withOrWithoutUser(indexHandler))
router.HandleFunc(fmt.Sprintf("GET %vhealth", config.Server.BaseURL), healthHandler)
// Swagger
router.Handle(fmt.Sprintf("%vswagger/", config.Server.BaseURL), withUser(swaggerHandler))
// redirect to baseUrl if not root
if config.Server.BaseURL != "/" {
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, config.Server.BaseURL, http.StatusMovedPermanently)
})
}
var scheme string
port := ""
srv := &http.Server{
+24
View File
@@ -100,6 +100,26 @@ func withAdminHelper(fn handleFunc) handleFunc {
})
}
// if withUserHelper fails, try without user
func withOrWithoutUserHelper(fn handleFunc) handleFunc {
return func(w http.ResponseWriter, r *http.Request, data *requestContext) (int, error) {
// Try to authenticate user first
status, err := withUserHelper(fn)(w, r, data)
// If user authentication succeeded, return the result
if err == nil {
return status, nil
}
// If user authentication failed, call the handler without user context
// Clear any user data that might have been partially set
data.user = nil
data.token = ""
// Call the handler function without user context
return fn(w, r, data)
}
}
func withoutUserHelper(fn handleFunc) handleFunc {
return func(w http.ResponseWriter, r *http.Request, data *requestContext) (int, error) {
// This middleware is used when no user authentication is required
@@ -285,6 +305,10 @@ func withUser(fn handleFunc) http.HandlerFunc {
return wrapHandler(withUserHelper(fn))
}
func withOrWithoutUser(fn handleFunc) http.HandlerFunc {
return wrapHandler(withOrWithoutUserHelper(fn))
}
func withoutUser(fn handleFunc) http.HandlerFunc {
return wrapHandler(withoutUserHelper(fn))
}
+37
View File
@@ -25,6 +25,35 @@ import (
"github.com/gtsteffaniak/go-logger/logger"
)
// validateMoveOperation checks if a move/rename operation is valid at the HTTP level
// It prevents moving a directory into itself or its subdirectories
func validateMoveOperation(src, dst string, isSrcDir bool) error {
// Clean and normalize paths
src = filepath.Clean(src)
dst = filepath.Clean(dst)
// If source is a directory, check if destination is within source
if isSrcDir {
// Get the parent directory of the destination
dstParent := filepath.Dir(dst)
// Check if destination parent is the source directory or a subdirectory of it
if strings.HasPrefix(dstParent+string(filepath.Separator), src+string(filepath.Separator)) || dstParent == src {
return fmt.Errorf("cannot move directory '%s' to a location within itself: '%s'", src, dst)
}
}
// Check if destination parent directory exists
dstParent := filepath.Dir(dst)
if dstParent != "." && dstParent != "/" {
if _, err := os.Stat(dstParent); os.IsNotExist(err) {
return fmt.Errorf("destination directory does not exist: '%s'", dstParent)
}
}
return nil
}
// resourceGetHandler retrieves information about a resource.
// @Summary Get resource information
// @Description Returns metadata and optionally file contents for a specified resource path.
@@ -455,6 +484,14 @@ func resourcePatchHandler(w http.ResponseWriter, r *http.Request, d *requestCont
if overwrite && !d.user.Permissions.Modify {
return http.StatusForbidden, fmt.Errorf("forbidden: user does not have permission to overwrite file")
}
// Validate move/rename operation to prevent circular references
if action == "rename" || action == "move" {
if err = validateMoveOperation(realSrc, realDest, isSrcDir); err != nil {
return http.StatusBadRequest, err
}
}
err = patchAction(r.Context(), patchActionParams{
action: action,
srcIndex: srcIndex,
+46 -36
View File
@@ -30,52 +30,64 @@ func (t *TemplateRenderer) Render(w http.ResponseWriter, name string, data inter
return t.templates.ExecuteTemplate(w, name, data)
}
func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentType string) {
func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestContext, file, contentType string) (int, error) {
w.Header().Set("Content-Type", contentType)
auther, err := store.Auth.Get("password")
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
return http.StatusInternalServerError, err
}
userSelectedTheme := ""
if d.user != nil {
theme, ok := config.Frontend.Styling.CustomThemes[d.user.CustomTheme]
if ok {
userSelectedTheme = theme.CSS
}
}
data := map[string]interface{}{
"Name": config.Frontend.Name,
"DisableExternal": config.Frontend.DisableDefaultLinks,
"darkMode": settings.Config.UserDefaults.DarkMode,
"BaseURL": config.Server.BaseURL,
"Version": version.Version,
"CommitSHA": version.CommitSHA,
"CustomCSS": config.Frontend.Styling.CustomCSS,
"userSelectedTheme": userSelectedTheme,
"LightBackground": config.Frontend.Styling.LightBackground,
"DarkBackground": config.Frontend.Styling.DarkBackground,
"StaticURL": config.Server.BaseURL + "static",
"Signup": settings.Config.Auth.Methods.PasswordAuth.Signup,
"NoAuth": config.Auth.Methods.NoAuth,
"LoginPage": auther.LoginPage(),
"CSS": false,
"EnableThumbs": !config.Server.DisablePreviews,
"ExternalLinks": config.Frontend.ExternalLinks,
"ExternalUrl": strings.TrimSuffix(config.Server.ExternalUrl, "/"),
"OnlyOfficeUrl": settings.Config.Integrations.OnlyOffice.Url,
"SourceCount": len(config.Server.SourceMap),
"OidcAvailable": config.Auth.Methods.OidcAuth.Enabled,
"PasswordAvailable": config.Auth.Methods.PasswordAuth.Enabled,
"MediaAvailable": config.Integrations.Media.FfmpegPath != "",
"MuPdfAvailable": config.Server.MuPdfAvailable,
"UpdateAvailable": utils.GetUpdateAvailableUrl(),
"DisableNavButtons": settings.Config.Frontend.DisableNavButtons,
"BaseURL": config.Server.BaseURL,
}
b, err := json.Marshal(data)
// variables consumed by frontend as json
data["globalVars"] = map[string]interface{}{
"Name": config.Frontend.Name,
"DisableExternal": config.Frontend.DisableDefaultLinks,
"darkMode": settings.Config.UserDefaults.DarkMode,
"BaseURL": config.Server.BaseURL,
"Version": version.Version,
"CommitSHA": version.CommitSHA,
"Signup": settings.Config.Auth.Methods.PasswordAuth.Signup,
"NoAuth": config.Auth.Methods.NoAuth,
"LoginPage": auther.LoginPage(),
"EnableThumbs": !config.Server.DisablePreviews,
"ExternalLinks": config.Frontend.ExternalLinks,
"ExternalUrl": strings.TrimSuffix(config.Server.ExternalUrl, "/"),
"OnlyOfficeUrl": settings.Config.Integrations.OnlyOffice.Url,
"SourceCount": len(config.Server.SourceMap),
"OidcAvailable": config.Auth.Methods.OidcAuth.Enabled,
"PasswordAvailable": config.Auth.Methods.PasswordAuth.Enabled,
"MediaAvailable": config.Integrations.Media.FfmpegPath != "",
"MuPdfAvailable": config.Server.MuPdfAvailable,
"UpdateAvailable": utils.GetUpdateAvailableUrl(),
"DisableNavButtons": settings.Config.Frontend.DisableNavButtons,
"UserSelectableThemes": config.Frontend.Styling.CustomThemeOptions,
}
jsonVars, err := json.Marshal(data["globalVars"])
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
return http.StatusInternalServerError, err
}
data["globalVars"] = strings.ReplaceAll(string(b), `'`, `\'`)
data["globalVars"] = strings.ReplaceAll(string(jsonVars), `'`, `\'`)
// Render the template with global variables
if err := templateRenderer.Render(w, file, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return http.StatusInternalServerError, err
}
return http.StatusOK, nil
}
func staticFilesHandler(w http.ResponseWriter, r *http.Request) {
@@ -102,11 +114,9 @@ func staticFilesHandler(w http.ResponseWriter, r *http.Request) {
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
func indexHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
if r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
return http.StatusNotFound, nil
}
handleWithStaticData(w, r, "index.html", "text/html")
return handleWithStaticData(w, r, d, "index.html", "text/html")
}
+71 -1
View File
@@ -3,7 +3,15 @@
package indexing
import "syscall"
import (
"bufio"
"fmt"
"os"
"runtime"
"strconv"
"strings"
"syscall"
)
func CheckWindowsHidden(realpath string) bool {
// Non-Windows platforms don't support hidden attributes in the same way
@@ -11,6 +19,68 @@ func CheckWindowsHidden(realpath string) bool {
}
func getPartitionSize(path string) (uint64, error) {
if runtime.GOOS == "darwin" {
return getFilesystemSize(path)
}
// Get the device containing the path
var stat syscall.Stat_t
err := syscall.Stat(path, &stat)
if err != nil {
return 0, err
}
// Get the device number
dev := stat.Dev
major := (dev >> 8) & 0xff
minor := dev & 0xff
// Read /proc/partitions to find the partition size
file, err := os.Open("/proc/partitions")
if err != nil {
// Fallback to filesystem size if we can't read /proc/partitions
return getFilesystemSize(path)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "major") {
continue
}
fields := strings.Fields(line)
if len(fields) < 4 {
continue
}
// Parse major and minor device numbers
majorNum, err1 := strconv.ParseUint(fields[0], 10, 32)
minorNum, err2 := strconv.ParseUint(fields[1], 10, 32)
blocks, err3 := strconv.ParseUint(fields[2], 10, 64)
if err1 != nil || err2 != nil || err3 != nil {
continue
}
// Check if this matches our device
if uint32(majorNum) == uint32(major) && uint32(minorNum) == uint32(minor) {
// blocks are in 1024-byte units in /proc/partitions
return blocks * 1024, nil
}
}
if err := scanner.Err(); err != nil {
return 0, fmt.Errorf("error reading /proc/partitions: %v", err)
}
// Fallback to filesystem size if partition not found
return getFilesystemSize(path)
}
// getFilesystemSize returns the filesystem size (fallback method)
func getFilesystemSize(path string) (uint64, error) {
var stat syscall.Statfs_t
err := syscall.Statfs(path, &stat)
if err != nil {
+8 -8
View File
@@ -40,16 +40,16 @@ type Index struct {
CurrentSchedule int `json:"-"`
settings.Source `json:"-"`
Directories map[string]*iteminfo.FileInfo `json:"-"`
DirectoriesLedger map[string]bool `json:"-"`
DirectoriesLedger map[string]struct{} `json:"-"`
runningScannerCount int `json:"-"`
SmartModifier time.Duration `json:"-"`
FilesChangedDuringIndexing bool `json:"-"`
mock bool
mu sync.RWMutex
hasIndex bool
FoundHardLinks map[string]uint64 `json:"-"` // hardlink path -> size
processedInodes map[uint64]bool `json:"-"`
totalSize uint64 `json:"-"`
FoundHardLinks map[string]uint64 `json:"-"` // hardlink path -> size
processedInodes map[uint64]struct{} `json:"-"`
totalSize uint64 `json:"-"`
}
var (
@@ -75,8 +75,8 @@ func Initialize(source settings.Source, mock bool) {
mock: mock,
Source: source,
Directories: make(map[string]*iteminfo.FileInfo),
DirectoriesLedger: make(map[string]bool),
processedInodes: make(map[uint64]bool),
DirectoriesLedger: make(map[string]struct{}),
processedInodes: make(map[uint64]struct{}),
FoundHardLinks: make(map[string]uint64),
}
newIndex.ReducedIndex = ReducedIndex{
@@ -123,7 +123,7 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
if recursive {
// Prevent race conditions if scanning becomes concurrent in the future.
idx.mu.Lock()
idx.DirectoriesLedger[adjustedPath] = true
idx.DirectoriesLedger[adjustedPath] = struct{}{}
idx.mu.Unlock()
}
combinedPath := adjustedPath + "/"
@@ -570,7 +570,7 @@ func (idx *Index) handleFile(file os.FileInfo, fullCombined string) (size uint64
return realSize, false
}
// First time seeing this inode.
idx.processedInodes[ino] = true
idx.processedInodes[ino] = struct{}{}
idx.FoundHardLinks[fullCombined] = realSize
idx.totalSize += realSize
return realSize, true // Count size for directory total.
+2 -2
View File
@@ -74,7 +74,7 @@ func (idx *Index) garbageCollection() {
}
}
// Reset the ledger for the next scan.
idx.DirectoriesLedger = make(map[string]bool)
idx.DirectoriesLedger = make(map[string]struct{})
}
func (idx *Index) UpdateSchedule() {
@@ -140,7 +140,7 @@ func (idx *Index) RunIndexing(origin string, quick bool) {
idx.mu.Lock()
idx.NumDirs = 0
idx.NumFiles = 0
idx.processedInodes = make(map[uint64]bool)
idx.processedInodes = make(map[uint64]struct{})
idx.FoundHardLinks = make(map[string]uint64)
idx.mu.Unlock()
}
+51
View File
@@ -0,0 +1,51 @@
.card,
#listingView.gallery .item,
#listingView.normal .item,
.button,
button,
.button-action,
.input,
input,
#listingView.gallery .item img,
#listingView .item img,
.sizeInputWrapper,
.sizeInput,
#search #input,
.floating-tooltip,
.settings-items>.item,
.button-group {
border-radius: 0.25em !important;
}
#listingView.compact .header {
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
#listingView.list .item:last-child,
#results {
border-bottom-left-radius: 0.25em !important;
border-bottom-right-radius: 0.25em !important;
}
#listingView.list .item:first-child {
border-top-left-radius: 0.25em !important;
border-top-right-radius: 0.25em !important;
}
#breadcrumbs ul li:last-child a {
border-top-right-radius: 0.25em !important;
border-bottom-right-radius: 0.25em !important;
}
#breadcrumbs ul li:first-child a {
border-top-left-radius: 0.25em !important;
border-bottom-left-radius: 0.25em !important;
}
.button-group button {
border-radius: 0 !important;
}
.icon {
border-radius: 0.1em !important;
}
+48
View File
@@ -2028,6 +2028,19 @@ const docTemplate = `{
}
}
},
"settings.CustomTheme": {
"type": "object",
"properties": {
"css": {
"description": "The css file path and filename to use for the theme.",
"type": "string"
},
"description": {
"description": "The description of the theme to display in the UI.",
"type": "string"
}
}
},
"settings.ExcludeIndexFilter": {
"type": "object",
"properties": {
@@ -2128,6 +2141,9 @@ const docTemplate = `{
"name": {
"description": "display name",
"type": "string"
},
"styling": {
"$ref": "#/definitions/settings.StylingConfig"
}
}
},
@@ -2524,9 +2540,37 @@ const docTemplate = `{
}
}
},
"settings.StylingConfig": {
"type": "object",
"properties": {
"customCSS": {
"description": "if a valid path to a css file is provided, it will be applied for all users. (eg. \"reduce-rounded-corners.css\")",
"type": "string"
},
"customThemes": {
"description": "A list of custom css files that each user can select to override the default styling. if \"default\" is key name then it will be the default option.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/settings.CustomTheme"
}
},
"darkBackground": {
"description": "Specify a valid CSS color property value to use as the background color in dark mode",
"type": "string"
},
"lightBackground": {
"description": "specify a valid CSS color property value to use as the background color in light mode",
"type": "string"
}
}
},
"settings.UserDefaults": {
"type": "object",
"properties": {
"customTheme": {
"description": "Name of theme to use chosen from custom themes config.",
"type": "string"
},
"darkMode": {
"description": "should dark mode be enabled",
"type": "boolean"
@@ -2797,6 +2841,10 @@ const docTemplate = `{
"$ref": "#/definitions/users.AuthToken"
}
},
"customTheme": {
"description": "Name of theme to use chosen from custom themes config.",
"type": "string"
},
"darkMode": {
"description": "should dark mode be enabled",
"type": "boolean"
+48
View File
@@ -2017,6 +2017,19 @@
}
}
},
"settings.CustomTheme": {
"type": "object",
"properties": {
"css": {
"description": "The css file path and filename to use for the theme.",
"type": "string"
},
"description": {
"description": "The description of the theme to display in the UI.",
"type": "string"
}
}
},
"settings.ExcludeIndexFilter": {
"type": "object",
"properties": {
@@ -2117,6 +2130,9 @@
"name": {
"description": "display name",
"type": "string"
},
"styling": {
"$ref": "#/definitions/settings.StylingConfig"
}
}
},
@@ -2513,9 +2529,37 @@
}
}
},
"settings.StylingConfig": {
"type": "object",
"properties": {
"customCSS": {
"description": "if a valid path to a css file is provided, it will be applied for all users. (eg. \"reduce-rounded-corners.css\")",
"type": "string"
},
"customThemes": {
"description": "A list of custom css files that each user can select to override the default styling. if \"default\" is key name then it will be the default option.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/settings.CustomTheme"
}
},
"darkBackground": {
"description": "Specify a valid CSS color property value to use as the background color in dark mode",
"type": "string"
},
"lightBackground": {
"description": "specify a valid CSS color property value to use as the background color in light mode",
"type": "string"
}
}
},
"settings.UserDefaults": {
"type": "object",
"properties": {
"customTheme": {
"description": "Name of theme to use chosen from custom themes config.",
"type": "string"
},
"darkMode": {
"description": "should dark mode be enabled",
"type": "boolean"
@@ -2786,6 +2830,10 @@
"$ref": "#/definitions/users.AuthToken"
}
},
"customTheme": {
"description": "Name of theme to use chosen from custom themes config.",
"type": "string"
},
"darkMode": {
"description": "should dark mode be enabled",
"type": "boolean"
+39
View File
@@ -142,6 +142,15 @@ definitions:
description: secret used to encrypt TOTP secrets
type: string
type: object
settings.CustomTheme:
properties:
css:
description: The css file path and filename to use for the theme.
type: string
description:
description: The description of the theme to display in the UI.
type: string
type: object
settings.ExcludeIndexFilter:
properties:
fileEndsWith:
@@ -220,6 +229,8 @@ definitions:
name:
description: display name
type: string
styling:
$ref: '#/definitions/settings.StylingConfig'
type: object
settings.IncludeIndexFilter:
properties:
@@ -513,8 +524,33 @@ definitions:
type: string
type: array
type: object
settings.StylingConfig:
properties:
customCSS:
description: if a valid path to a css file is provided, it will be applied
for all users. (eg. "reduce-rounded-corners.css")
type: string
customThemes:
additionalProperties:
$ref: '#/definitions/settings.CustomTheme'
description: A list of custom css files that each user can select to override
the default styling. if "default" is key name then it will be the default
option.
type: object
darkBackground:
description: Specify a valid CSS color property value to use as the background
color in dark mode
type: string
lightBackground:
description: specify a valid CSS color property value to use as the background
color in light mode
type: string
type: object
settings.UserDefaults:
properties:
customTheme:
description: Name of theme to use chosen from custom themes config.
type: string
darkMode:
description: should dark mode be enabled
type: boolean
@@ -710,6 +746,9 @@ definitions:
additionalProperties:
$ref: '#/definitions/users.AuthToken'
type: object
customTheme:
description: Name of theme to use chosen from custom themes config.
type: string
darkMode:
description: should dark mode be enabled
type: boolean
View File
Whitespace-only changes.
+9
View File
@@ -90,6 +90,14 @@ frontend:
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
styling:
customCSS: "" # if a valid path to a css file is provided, it will be applied for all users. (eg. "reduce-rounded-corners.css")
lightBackground: "white" # specify a valid CSS color property value to use as the background color in light mode
darkBackground: "#141D24" # Specify a valid CSS color property value to use as the background color in dark mode
customThemes: # A list of custom css files that each user can select to override the default styling. if "default" is key name then it will be the default option.
default:
description: The default theme
css: ""
userDefaults:
editorQuickSave: false # show quick save button in editor
hideSidebarFileActions: false # hide the file actions in the sidebar
@@ -132,6 +140,7 @@ userDefaults:
uploadChunkSizeMb: 10
disableOfficePreviewExt: "" # deprecated: use disablePreviewExt instead
disableOnlyOfficeExt: "" # list of file extensions to disable onlyoffice editor for
customTheme: "" # Name of theme to use chosen from custom themes config.
integrations:
office: # validate:omitempty
url: "" # The URL to the OnlyOffice Document Server, needs to be accessible to the user. validate:required
+25 -3
View File
@@ -59,6 +59,29 @@
</script>
<style>
/* Basic Styles */
:root {
--background: {{ .LightBackground }};
--alt-background: #ddd;
--surfacePrimary: gray;
--surfaceSecondary: lightgray;
--textPrimary: #546e7a;
--textSecondary: gray;
--iconBackground: #dddddd;
--activeWhiteIcon: gray;
}
.dark-mode {
--background: {{ .DarkBackground }};
--alt-background: #283136;
--surfacePrimary: #20292F;
--surfaceSecondary: #3A4147;
--divider: rgba(255, 255, 255, 0.12);
--textPrimary: rgba(255, 255, 255, 0.87);
--textSecondary: rgba(255, 255, 255, 0.6);
--iconBackground: #1e1f20;
--activeWhiteIcon: white;
}
#loading {
position: fixed;
top: 0;
@@ -132,6 +155,8 @@
transform: scale(1.0);
}
}
{{ .CustomCSS }}
{{ .userSelectedTheme }}
</style>
</head>
@@ -157,9 +182,6 @@
{{end}}
<script type="module" src="/src/main.ts"></script>
{{ if .CSS }}
<link rel="stylesheet" href="{{ .StaticURL }}/custom.css">
{{ end }}
</body>
</html>
+20
View File
@@ -76,6 +76,8 @@ async function translateText(text, targetLanguage, keyPath = '') {
// --- Recursive key processor ---
async function processKeys(masterObj, targetObj, targetLangCode, currentPathParts = []) {
let changesMade = false;
// First pass: Add/update keys from master to target
for (const key in masterObj) {
if (Object.prototype.hasOwnProperty.call(masterObj, key)) {
const currentPathPartsNext = [...currentPathParts, key];
@@ -123,6 +125,24 @@ async function processKeys(masterObj, targetObj, targetLangCode, currentPathPart
}
}
}
// Second pass: Remove obsolete keys that exist in target but not in master
const keysToRemove = [];
for (const key in targetObj) {
if (Object.prototype.hasOwnProperty.call(targetObj, key)) {
if (!masterObj.hasOwnProperty(key)) {
keysToRemove.push(key);
}
}
}
for (const key of keysToRemove) {
const currentKeyPath = [...currentPathParts, key].join('.');
console.log(`🗑️ Removing obsolete key "${currentKeyPath}" from ${targetLangCode}.json`);
delete targetObj[key];
changesMade = true;
}
return changesMade;
}
+3 -5
View File
@@ -147,14 +147,12 @@ export function post(
}
});
return { xhr: request, promise };
promise.xhr = request;
return promise;
} catch (err) {
notify.showError(err.message || "Error posting resource");
// We are returning a promise, so we should return a rejected promise on error.
return {
xhr: null,
promise: Promise.reject(err),
};
return Promise.reject(err);
}
}
+1 -3
View File
@@ -18,7 +18,7 @@
</router-link>
</li>
</ul>
<div v-if="isCardView" class="gallery-size">
<div v-if="isCardView" class="gallery-size card">
{{ $t("files.size") }}<span class="sr-only">:</span> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
<input
v-model="gallerySize"
@@ -225,10 +225,8 @@ export default {
.gallery-size {
display: flex;
background: var(--alt-background);
width: fit-content;
padding: 0.5em;
border-radius: 1em;
margin-bottom: 0.5em;
}
input[type="range"] {
+2 -2
View File
@@ -754,8 +754,8 @@ body.rtl #search #result ul > * {
border-color: black;
border-style: solid;
border-bottom-style: none;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
/* Search Input Placeholder */
+103 -1
View File
@@ -1,5 +1,5 @@
<template>
<div class="image-ex-container" ref="container" @touchstart="touchStart" @touchmove="touchMove" @dblclick="zoomAuto"
<div class="image-ex-container" ref="container" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @dblclick="zoomAuto"
@mousedown="mousedownStart" @mousemove="mouseMove" @mouseup="mouseUp" @wheel="wheelMove">
<div v-if="!isLoaded">{{ $t('files.loading') }}</div>
@@ -46,6 +46,18 @@ export default {
maxScale: 4,
minScale: 0.25,
isTiff: false, // Determine if the image is a TIFF
// Swipe navigation properties
swipeStartTime: null,
swipeStartX: 0,
swipeStartY: 0,
swipeCurrentX: 0,
swipeCurrentY: 0,
isSwipeGesture: false,
hasStartedSwipe: false,
gestureDecided: false, // Track if we've made a decision about this gesture
swipeMinDistance: 150, // Minimum horizontal distance for swipe
swipeMaxTime: 500, // Maximum time for swipe in milliseconds
swipeMaxVerticalDistance: 50, // Maximum vertical movement to still be considered horizontal swipe
};
},
mounted() {
@@ -90,6 +102,7 @@ export default {
this.scale = 1; // Reset zoom level
this.position.relative = { x: 0, y: 0 }; // Reset position
this.showSpinner = true; // Show spinner while loading
this.resetSwipeTracking(); // Reset swipe tracking for new image
},
},
methods: {
@@ -164,6 +177,24 @@ export default {
this.lastX = null;
this.lastY = null;
this.lastTouchDistance = null;
// Initialize swipe tracking for single touch
if (event.targetTouches.length === 1) {
const touch = event.targetTouches[0];
this.swipeStartTime = Date.now();
this.swipeStartX = touch.pageX;
this.swipeStartY = touch.pageY;
this.swipeCurrentX = touch.pageX;
this.swipeCurrentY = touch.pageY;
this.isSwipeGesture = false;
this.hasStartedSwipe = false;
this.gestureDecided = false; // Reset decision for new touch
} else {
// Reset swipe tracking for multi-touch (zoom gestures)
this.resetSwipeTracking();
}
if (event.targetTouches.length < 2) {
setTimeout(() => {
this.touches = 0;
@@ -194,6 +225,39 @@ export default {
},
touchMove(event) {
event.preventDefault();
// Update current swipe position for single touch
if (event.targetTouches.length === 1) {
const touch = event.targetTouches[0];
this.swipeCurrentX = touch.pageX;
this.swipeCurrentY = touch.pageY;
// Only make gesture decision once per touch sequence
if (!this.gestureDecided) {
const deltaX = Math.abs(this.swipeCurrentX - this.swipeStartX);
const deltaY = Math.abs(this.swipeCurrentY - this.swipeStartY);
// Only decide after some meaningful movement
if (deltaX > 10 || deltaY > 10) {
this.gestureDecided = true; // Mark that we've made a decision
if (deltaX > deltaY * 2) {
// Horizontal movement is significantly more than vertical - it's a swipe
this.isSwipeGesture = true;
} else {
// Not horizontal enough - it's a pan gesture
this.isSwipeGesture = false;
}
}
}
// If we've decided it's a swipe gesture, don't do normal panning
if (this.gestureDecided && this.isSwipeGesture) {
return; // Block normal pan behavior for swipes
}
}
// Normal touch move logic for pan/zoom (only runs if not a swipe gesture)
if (this.lastX === null) {
this.lastX = event.targetTouches[0].pageX;
this.lastY = event.targetTouches[0].pageY;
@@ -250,6 +314,44 @@ export default {
pxStringToNumber(style) {
return +style.replace("px", "");
},
touchEnd(event) {
event.preventDefault();
// Only process swipe if it was a single touch and we detected a swipe gesture
if (this.isSwipeGesture && this.swipeStartTime) {
const swipeEndTime = Date.now();
const swipeDuration = swipeEndTime - this.swipeStartTime;
const deltaX = this.swipeCurrentX - this.swipeStartX;
const deltaY = Math.abs(this.swipeCurrentY - this.swipeStartY);
const absDelataX = Math.abs(deltaX);
// Check if swipe meets criteria: fast, horizontal, and long enough
if (
swipeDuration <= this.swipeMaxTime &&
absDelataX >= this.swipeMinDistance &&
deltaY <= this.swipeMaxVerticalDistance
) {
if (deltaX > 0) {
this.$emit('navigate-previous');
} else {
this.$emit('navigate-next');
}
}
}
// Reset swipe tracking
this.resetSwipeTracking();
},
resetSwipeTracking() {
this.swipeStartTime = null;
this.swipeStartX = 0;
this.swipeStartY = 0;
this.swipeCurrentX = 0;
this.swipeCurrentY = 0;
this.isSwipeGesture = false;
this.hasStartedSwipe = false;
this.gestureDecided = false; // Reset decision state
},
},
};
</script>
+30 -34
View File
@@ -5,7 +5,7 @@
item: true,
'no-select': true,
'listing-item': true,
activebutton: isMaximized && isSelected,
activebutton: isSelected,
hiddenFile: isHiddenNotSelected && !this.isDraggedOver,
'half-selected': isDraggedOver,
'drag-hover': isDraggedOver,
@@ -30,7 +30,7 @@
@touchend="cancelContext($event)"
@mouseup="cancelContext($event)"
>
<div @click="toggleClick" :class="{ 'gallery-div': galleryView }">
<div :class="{ 'gallery-div': galleryView }">
<Icon
:mimetype="type"
:active="isSelected"
@@ -39,7 +39,7 @@
/>
</div>
<div class="text" :class="{ activecontent: isMaximized && isSelected }">
<div class="text">
<p :class="{ adjustment: quickDownloadEnabled }" class="name">{{ name }}</p>
<p
class="size"
@@ -82,7 +82,6 @@ export default {
data() {
return {
isThumbnailInView: false,
isMaximized: false,
touches: 0,
touchStartX: 0,
touchStartY: 0,
@@ -130,7 +129,7 @@ export default {
if (state.user.singleClick || !this.allowedView) {
return false;
}
return !this.isMaximized;
return this.isSelected;
},
isSelected() {
return this.selected.indexOf(this.index) !== -1;
@@ -246,9 +245,6 @@ export default {
}
});
},
toggleClick() {
this.isMaximized = this.isClicked;
},
humanSize() {
return this.type == "invalid_link"
? "invalid link"
@@ -321,23 +317,26 @@ export default {
action(false, false);
},
addSelected(event) {
if (state.isSafari) {
if (event.type === "touchstart") {
const touch = event.touches[0];
this.touchStartX = touch.clientX;
this.touchStartY = touch.clientY;
this.isLongPress = false; // Reset state
this.isSwipe = false; // Reset swipe detection
if (!state.multiple) {
this.contextTimeout = setTimeout(() => {
if (!this.isSwipe) {
mutations.resetSelected();
mutations.addSelected(this.index);
}
}, 500);
}
}
if (!state.isSafari) {
return;
}
if (event.type !== "touchstart") {
return;
}
const touch = event.touches[0];
this.touchStartX = touch.clientX;
this.touchStartY = touch.clientY;
this.isLongPress = false; // Reset state
this.isSwipe = false; // Reset swipe detection
if (state.multiple) {
return;
}
this.contextTimeout = setTimeout(() => {
if (!this.isSwipe) {
mutations.resetSelected();
mutations.addSelected(this.index);
}
}, 500);
},
click(event) {
if (event.button === 0) {
@@ -392,6 +391,13 @@ export default {
return;
}
// In multiple selection mode, clicking an already selected item should deselect it
if (state.multiple) {
mutations.removeSelected(this.index);
mutations.setLastSelectedIndex(this.index);
return;
}
if (state.selected.length > 1) {
mutations.resetSelected();
mutations.addSelected(this.index);
@@ -440,16 +446,6 @@ export default {
height: 10em;
}
.activecontent {
height: 5em !important;
display: grid !important;
}
.activeimg {
width: 8em !important;
height: 8em !important;
}
.iconActive {
font-size: 6em !important;
}
+9 -2
View File
@@ -66,8 +66,15 @@ export default {
try {
event.preventDefault();
if (this.name === "") return;
await filesApi.post(state.req.source, state.req.path + "/" + this.name, true);
goToItem(state.req.source,state.req.path + "/" + encodeURIComponent(this.name));
await filesApi.post(
state.req.source,
state.req.path + "/" + this.name,
true
);
goToItem(
state.req.source,
state.req.path + "/" + encodeURIComponent(this.name)
);
} catch (error) {
notify.showError(error);
}
+56 -3
View File
@@ -4,15 +4,19 @@
<h2>{{ $t("prompts.rename") }}</h2>
</div>
<div class="card-content">
<p>{{ $t("prompts.renameMessage") }} <code>{{ oldName() }}</code>:</p> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
<div class="card-content">
<p>{{ $t("prompts.renameMessage", { filename: oldName() }) }}</p>
<input
class="input input--block"
:class="{ 'invalid-form': !validateFileName(name) }"
v-focus
type="text"
@keyup.enter="submit"
v-model.trim="name"
/>
<p v-if="!validateFileName(name) && name.length > 0" class="validation-error">
{{ $t("prompts.invalidName") }}
</p>
</div>
<div class="card-action">
@@ -27,6 +31,7 @@
<button
@click="submit"
class="button button--flat"
:disabled="!validateFileName(name) || name.length === 0"
type="submit"
:aria-label="$t('buttons.rename')"
:title="$t('buttons.rename')"
@@ -67,24 +72,54 @@ export default {
closeHovers() {
return mutations.closeHovers;
},
currentPrompt() {
return getters.currentPrompt();
},
},
methods: {
validateFileName(value) {
if (value === "") {
return false;
}
// Check for forbidden characters: forward slash and backslash
const forbiddenChars = /[/\\]/;
return !forbiddenChars.test(value);
},
cancel() {
mutations.closeHovers();
},
oldName() {
// Check if this is being called from upload context with props
if (this.currentPrompt && this.currentPrompt.props && this.currentPrompt.props.folderName) {
return this.currentPrompt.props.folderName;
}
if (!this.isListing) {
return state.req.name;
}
if (getters.selectedCount() === 0 || getters.selectedCount() > 1) {
return;
return "";
}
return state.req.items[this.selected[0]].name;
},
async submit() {
// Validate before submitting
if (!this.validateFileName(this.name) || this.name.length === 0) {
notify.showError(this.$t("prompts.invalidName"));
return;
}
try {
// Check if this is being called from upload context with custom confirm handler
if (this.currentPrompt && this.currentPrompt.confirm) {
// This is for upload rename - call the custom confirm handler
this.currentPrompt.confirm(this.name);
return;
}
// Default file rename operation
const items = [{
from: state.req.path + "/" + state.req.items[this.selected[0]].name,
fromSource: state.req.source,
@@ -105,3 +140,21 @@ export default {
},
};
</script>
<style scoped>
.invalid-form {
border-color: var(--red) !important;
}
.validation-error {
color: var(--red);
font-size: 0.9em;
margin-top: 0.5em;
margin-bottom: 0;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
+7 -2
View File
@@ -3,12 +3,12 @@
<div class="card-title">
<h2>{{ $t("otp.name") }}</h2>
</div>
<div v-if="error !== ''" class="wrong-login">{{ error }}</div>
<div v-if="error !== ''" class="wrong-login card">{{ error }}</div>
<div v-if="succeeded" >{{ $t("otp.verificationSucceed") }}</div>
<div v-if="!succeeded" class="card-content">
<p v-if="generate">{{ $t("otp.generate") }}</p>
<div v-if="generate" class="share__box__element share__box__center">
<p>{{ this.url }}</p>
<p aria-label="otp-url">{{ this.url }}</p>
<qrcode-vue class="qrcode" :value="this.url" size="200" level="M"></qrcode-vue>
</div>
<p>{{ $t("otp.verifyInstructions") }}</p>
@@ -81,6 +81,11 @@ export default {
default: "",
},
},
async mounted() {
if (this.generate) {
this.generateNewCode();
}
},
methods: {
async generateNewCode() {
try {
+56 -3
View File
@@ -194,6 +194,7 @@
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
import { uploadManager } from "@/utils/upload";
import { mutations, state } from "@/store";
import { notify } from "@/notify";
import ProgressBar from "@/components/ProgressBar.vue";
export default {
@@ -238,16 +239,66 @@ export default {
let wakeLock = null;
const handleConflict = (resolver) => {
showConflictPrompt.value = true;
conflictResolver = resolver;
mutations.showHover({
name: "replace-rename",
confirm: (event, option) => {
if (option === "overwrite") {
resolveConflict(true);
} else if (option === "rename") {
showRenamePrompt();
} else {
resolveConflict(false);
}
},
});
};
const resolveConflict = (overwrite) => {
if (conflictResolver) {
conflictResolver(overwrite);
}
showConflictPrompt.value = false;
conflictResolver = null;
mutations.closeTopHover(); // Only close the conflict prompt, return to upload prompt
};
const showRenamePrompt = () => {
mutations.closeTopHover(); // Only close the replace-rename prompt, keep upload prompt open
// Get the conflicting folder name from the upload queue
const conflictingFolder = uploadManager.getConflictingFolder();
if (!conflictingFolder) {
console.error("No conflicting folder found for rename");
return;
}
mutations.showHover({
name: "rename",
confirm: (newName) => {
renameUploadFolder(conflictingFolder, newName);
},
props: { folderName: conflictingFolder }
});
};
const renameUploadFolder = async (oldName, newName) => {
try {
// Check if the new name already exists
const existingItems = new Set(state.req.items.map(i => i.name));
if (existingItems.has(newName)) {
notify.showError(new Error(`A folder named "${newName}" already exists`));
return;
}
// Update upload manager with the new folder name
await uploadManager.renameFolder(oldName, newName);
// Resolve the conflict and continue upload
if (conflictResolver) {
conflictResolver({ rename: newName });
}
mutations.closeTopHover(); // Only close the rename prompt, return to upload prompt
} catch (error) {
notify.showError(error);
}
};
const acquireWakeLock = async () => {
@@ -470,6 +521,8 @@ export default {
hasCompleted,
showConflictPrompt,
resolveConflict,
showRenamePrompt,
renameUploadFolder,
canPauseAll,
canResumeAll,
handleConflictAction,
@@ -43,7 +43,7 @@
<div class="settings-items">
<ToggleSwitch class="item" v-model="user.otpEnabled" :name="$t('otp.name')" />
</div>
<button class="button" type="button" v-if="user.otpEnabled" :onclick="newOTP">
<button class="button" type="button" v-if="user.otpEnabled" :onclick="newOTP" aria-label="Generate Code">
{{ $t("buttons.generateNewOtp") }}
</button>
</div>
+87 -206
View File
@@ -1,56 +1,4 @@
<template>
<div class="tooltip">
<span class="tooltiptext-first" :class="{ visible: hoverText != '' }">
{{ hoverText }}
</span>
</div>
<div v-if="hasSourceInfo" class="tooltip-sources">
<div
class="tooltiptext-sources"
:style="{ top: mouseY - 500 + 'px' }"
:class="{ visible: sourceInfoTooltip != '' }"
>
<table class="tooltip-table">
<thead>
<tr>
<th colspan="2">{{ sourceInfoTooltip.name }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ $t("index.status") }}</td>
<td>{{ sourceInfoTooltip.status }}</td>
</tr>
<tr>
<td>{{ $t("index.assessment") }}</td>
<td>{{ sourceInfoTooltip.assessment }}</td>
</tr>
<tr>
<td>{{ $t("index.files") }}</td>
<td>{{ sourceInfoTooltip.files }}</td>
</tr>
<tr>
<td>{{ $t("index.folders") }}</td>
<td>{{ sourceInfoTooltip.folders }}</td>
</tr>
<tr>
<td>{{ $t("index.lastScanned") }}</td>
<td>{{ gethumanReadable }}</td>
</tr>
<tr>
<td>{{ $t("index.quickScan") }}</td>
<td>{{ humanReadableQuickScan }}</td>
</tr>
<tr>
<td>{{ $t("index.fullScan") }}</td>
<td>{{ humanReadableFullScan }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card headline-card">
<div class="card-wrapper user-card">
<div
@@ -60,8 +8,8 @@
>
<button
class="person-button action"
@mouseover="updateHoverText($t('index.settingsHover'))"
@mouseleave="resetHoverTextToDefault"
@mouseenter="showTooltip($event, $t('index.settingsHover'))"
@mouseleave="hideTooltip"
>
<i class="material-icons">person</i>
{{ user.username }}
@@ -79,37 +27,37 @@
<button
aria-label="logout-button"
class="logout-button action"
@mouseover="updateHoverText($t('index.logout'))"
@mouseleave="resetHoverTextToDefault"
@mouseenter="showTooltip($event, $t('index.logout'))"
@mouseleave="hideTooltip"
>
<i v-if="canLogout" class="material-icons">exit_to_app</i>
</button>
</div>
</div>
<div v-if="!disableQuickToggles" class="card-wrapper" @mouseleave="resetHoverTextToDefault">
<div v-if="!disableQuickToggles" class="card-wrapper" @mouseleave="hideTooltip">
<div class="quick-toggles">
<div
:class="{ active: user?.singleClick }"
@click="toggleClick"
@mouseover="updateHoverText($t('index.toggleClick'))"
@mouseleave="resetHoverTextToDefault"
@mouseenter="showTooltip($event, $t('index.toggleClick'))"
@mouseleave="hideTooltip"
>
<i class="material-icons">ads_click</i>
</div>
<div
:class="{ active: user?.darkMode }"
@click="toggleDarkMode"
@mouseover="updateHoverText($t('index.toggleDark'))"
@mouseleave="resetHoverTextToDefault"
@mouseenter="showTooltip($event, $t('index.toggleDark'))"
@mouseleave="hideTooltip"
>
<i class="material-icons">dark_mode</i>
</div>
<div
:class="{ active: isStickySidebar }"
@click="toggleSticky"
@mouseover="updateHoverText($t('index.toggleSticky'))"
@mouseleave="resetHoverTextToDefault"
@mouseenter="showTooltip($event, $t('index.toggleSticky'))"
@mouseleave="hideTooltip"
v-if="!isMobile"
>
<i class="material-icons">push_pin</i>
@@ -166,8 +114,8 @@
</svg>
<span>{{ name }}</span>
<i class="no-select material-symbols-outlined tooltip-info-icon"
@mouseenter="updateSourceTooltip($event, info)"
@mouseleave="resetSourceTooltip">
@mouseenter="showSourceTooltip($event, info)"
@mouseleave="hideTooltip">
info <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
</i>
</div>
@@ -195,11 +143,7 @@ export default {
ProgressBar,
},
data() {
return {
mouseY: 0,
hoverText: "", // Initially empty
sourceInfoTooltip: "",
};
return {};
},
computed: {
disableQuickToggles: () => state.user.disableQuickToggles,
@@ -224,25 +168,7 @@ export default {
sourceInfo: () => state.sources.info,
activeSource: () => state.sources.current,
realtimeActive: () => state.realtimeActive,
humanReadableQuickScan() {
const tooltip = this.getTooltipInfo();
if (!tooltip || isNaN(Number(tooltip.quickScanDurationSeconds))) return "";
return Number(tooltip.quickScanDurationSeconds);
},
humanReadableFullScan() {
const tooltip = this.getTooltipInfo();
if (!tooltip || isNaN(Number(tooltip.fullScanDurationSeconds))) return "";
return Number(tooltip.fullScanDurationSeconds);
},
gethumanReadable() {
const tooltip = this.getTooltipInfo();
if (!tooltip || isNaN(Number(tooltip.lastIndex))) return "";
let val = Number(tooltip.lastIndex);
if (val === 0) {
return "now";
}
return fromNow(val, state.user.locale);
},
},
watch: {
route() {
@@ -267,25 +193,9 @@ export default {
getHumanReadableFilesize(size) {
return getHumanReadableFilesize(size);
},
getTooltipInfo() {
return this.sourceInfoTooltip || null;
},
checkLogin() {
return getters.isLoggedIn() && !getters.routePath().startsWith("/share");
},
updateSourceTooltip(event, text) {
this.mouseY = event.clientY;
this.sourceInfoTooltip = text;
},
resetSourceTooltip() {
this.sourceInfoTooltip = ""; // Reset to default hover text
},
updateHoverText(text) {
this.hoverText = text;
},
resetHoverTextToDefault() {
this.hoverText = ""; // Reset to default hover text
},
toggleClick() {
mutations.updateCurrentUser({ singleClick: !state.user.singleClick });
},
@@ -300,7 +210,6 @@ export default {
mutations.updateCurrentUser({ stickySidebar: !state.user.stickySidebar });
},
navigateTo(path) {
this.sourceInfoTooltip = ""; // Reset tooltip when navigating
const hashIndex = path.indexOf("#");
if (hashIndex !== -1) {
// Extract the hash
@@ -346,111 +255,83 @@ export default {
el.style.opacity = '0';
setTimeout(done, 300);
},
showTooltip(event, text) {
if (text) {
mutations.showTooltip({
content: text,
x: event.clientX,
y: event.clientY,
});
}
},
hideTooltip() {
mutations.hideTooltip();
},
showSourceTooltip(event, info) {
if (info) {
const tooltipContent = this.buildSourceTooltipContent(info);
mutations.showTooltip({
content: tooltipContent,
x: event.clientX,
y: event.clientY,
});
}
},
buildSourceTooltipContent(info) {
const getHumanReadable = (lastIndex) => {
if (isNaN(Number(lastIndex))) return "";
let val = Number(lastIndex);
if (val === 0) {
return "now";
}
return fromNow(val, state.user.locale);
};
return `
<table style="border-collapse: collapse; text-align: left;">
<thead>
<tr>
<th colspan="2" style="text-align: center; font-weight: bold; font-size: 1.1em; padding-bottom: 0.3em; border-bottom: 1px solid #888;">${info.name || 'Source'}</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${this.$t("index.status")}</td>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${info.status || ''}</td>
</tr>
<tr>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${this.$t("index.assessment")}</td>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${info.assessment || ''}</td>
</tr>
<tr>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${this.$t("index.files")}</td>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${info.files || ''}</td>
</tr>
<tr>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${this.$t("index.folders")}</td>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${info.folders || ''}</td>
</tr>
<tr>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${this.$t("index.lastScanned")}</td>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${getHumanReadable(info.lastIndex)}</td>
</tr>
<tr>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${this.$t("index.quickScan")}</td>
<td style="padding: 0.2em 0.5em; border-bottom: 1px solid #ccc;">${isNaN(Number(info.quickScanDurationSeconds)) ? '' : Number(info.quickScanDurationSeconds)}</td>
</tr>
<tr>
<td style="padding: 0.2em 0.5em;">${this.$t("index.fullScan")}</td>
<td style="padding: 0.2em 0.5em;">${isNaN(Number(info.fullScanDurationSeconds)) ? '' : Number(info.fullScanDurationSeconds)}</td>
</tr>
</tbody>
</table>
`;
},
},
};
</script>
<style>
.tooltip {
position: absolute;
display: inline-block;
left: 50%;
top: 2em;
}
.tooltiptext-first {
visibility: hidden;
opacity: 0;
transition: opacity 0.2s ease;
position: absolute;
width: max-content;
max-width: 20em;
background-color: var(--alt-background);
color: var(--textPrimary);
text-align: center;
border-radius: 1em;
padding: 0.5em;
z-index: 1000;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
box-shadow: 0 0.25em 1em rgba(0, 0, 0, 0.2);
white-space: normal;
overflow-wrap: break-word;
}
.tooltiptext-first.visible {
visibility: visible;
opacity: 1;
}
.tooltiptext-sources.visible {
visibility: visible;
opacity: 1;
}
.tooltip-sources {
position: absolute;
display: inline-block;
left: 50%;
top: 20em;
}
.tooltiptext-sources {
visibility: hidden;
opacity: 0;
transition: opacity 0.2s ease;
position: absolute;
width: max-content;
height: fit-content;
max-width: 20em;
background-color: var(--alt-background);
color: var(--textPrimary);
text-align: center;
border-radius: 1em;
padding: 0.5em;
z-index: 1000;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
box-shadow: 0 0.25em 1em rgba(0, 0, 0, 0.2);
white-space: normal;
overflow-wrap: break-word;
}
.tooltiptext-sources .tooltip-table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.tooltiptext-sources .tooltip-table th {
text-align: center;
font-weight: bold;
font-size: 1.1em;
padding-bottom: 0.3em;
border-bottom: 1px solid #888;
}
.tooltiptext-sources .tooltip-table td {
padding: 0.2em 0.5em;
vertical-align: top;
border-bottom: 1px solid #ccc !important; /* force apply thin gray lines */
border-style: hidden;
}
.tooltiptext-sources .tooltip-table tr {
border-style: hidden;
}
.tooltiptext-sources .tooltip-table tr:last-child td {
border-bottom: none !important;
}
.tooltiptext-first,
.tooltiptext-sources {
pointer-events: none;
}
.user-card {
flex-direction: row !important;
+1 -11
View File
@@ -1,14 +1,4 @@
/* Basic Styles */
:root {
--background: white;
--alt-background: #ddd;
--surfacePrimary: gray;
--surfaceSecondary: lightgray;
--textPrimary: #546e7a;
--textSecondary: gray;
--iconBackground: #dddddd;
--activeWhiteIcon: gray;
}
body {
font-family: "Roboto", sans-serif;
-11
View File
@@ -1,15 +1,4 @@
/* Define a class .dark-mode for dark mode styles */
.dark-mode {
--background: #141D24;
--alt-background: #283136;
--surfacePrimary: #20292F;
--surfaceSecondary: #3A4147;
--divider: rgba(255, 255, 255, 0.12);
--textPrimary: rgba(255, 255, 255, 0.87);
--textSecondary: rgba(255, 255, 255, 0.6);
--iconBackground: #1e1f20;
--activeWhiteIcon: white;
}
.dark-mode #loading {
background: var(--background);
+11 -8
View File
@@ -5,6 +5,7 @@
transition: width 0.25s, height 0.25s, box-shadow 0.2s ease-in-out, transform 0.1s ease-in-out;
user-select: none;
min-height: 75vh;
padding-bottom: 0.5em;
}
body.rtl #listingView {
@@ -30,8 +31,6 @@ body.rtl #listingView {
}
#listingView .item {
background-color: #fff;
position: relative;
display: flex;
flex-wrap: nowrap;
color: #6f6f6f;
@@ -42,6 +41,7 @@ body.rtl #listingView {
overflow: hidden;
width: var(--item-width);
height: var(--item-height);
border-radius: 1em;
}
#listingView .item div:last-of-type {
@@ -95,7 +95,6 @@ body.rtl #listingView {
#listingView.list .item {
margin: .5em;
padding: 0.5em;
border-radius: 1em;
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
}
@@ -265,9 +264,14 @@ body.rtl #listingView {
border-bottom-right-radius: 0px;
}
#listingView.compact .header,
#listingView.list .header {
border: 1px solid rgba(0, 0, 0, .1);
#listingView.compact .item:first-child {
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
#listingView.compact .header {
margin: 0;
border-bottom: unset;
box-shadow: unset;
}
#listingView.compact .header>div:first-child {
@@ -324,7 +328,6 @@ body.rtl #listingView {
#listingView.gallery .item[aria-selected=true] {
border: 1em solid var(--primaryColor) !important;
border-radius: 1em;
padding: 0;
}
@@ -359,7 +362,6 @@ body.rtl #listingView {
#listingView .header {
display: flex !important;
background: white;
border-radius: 1em;
border: 1px solid rgba(0, 0, 0, .1);
z-index: 999;
padding: .85em;
@@ -378,6 +380,7 @@ body.rtl #listingView {
#listingView.list .header {
border-top-left-radius: 1em;
border-top-right-radius: 1em;
border: unset;
}
#listingView.list .item:first-child {
+1 -2
View File
@@ -38,13 +38,12 @@
}
.wrong-login {
background: var(--red);
background: var(--red) !important;
color: #fff;
padding: .5em;
text-align: center;
animation: .2s opac forwards;
margin-bottom: 0.5em;
border-radius: 1em;
}
@keyframes opac {
+2 -1
View File
@@ -2,13 +2,14 @@
#listingView .header .name,
#listingView.list .item .name,
#listingView.compact .item .name {
width: 25%;
width: 50%;
}
#listingView.list .item div:last-of-type{
display:block;
width:100%;
}
body {
padding-bottom: 5em;
}
+5 -18
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "نسخ",
"copyMessage": "رجاء حدد المكان لنسخ ملفاتك فيه:",
"currentlyNavigating": "يتم الإنتقال حاليا إلى:",
"deleteMessageMultiple": "هل تريد بالتأكيد حذف {count} ملف؟",
"deleteMessageSingle": "هل تريد بالتأكيد حذف هذا الملف/المجلد؟",
"deleteTitle": "حذف الملفات",
@@ -164,11 +163,9 @@
"show": "عرض",
"size": "الحجم",
"upload": "التحميل",
"uploadMessage": "حدد خياراً للتحميل.",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"deleteUserMessage": "هل أنت متأكد من رغبتك في حذف هذا المستخدم؟",
"filesSelected": "الملفات المحددة",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "كلمة مرور اختيارية",
"typeName": "النوع:",
"selected": "مختارة",
@@ -178,7 +175,8 @@
"completed": "مكتمل",
"conflictsDetected": "التعارضات المكتشفة",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "الاسم المقدم غير صالح: لا يمكن أن يحتوي على \"/\" أو \"\\\""
},
"search": {
"images": "الصور",
@@ -234,12 +232,7 @@
"passwordUpdated": "تم تغيير كلمة المرور",
"path": "المسار",
"permissions": {
"create": "Create files and directories",
"delete": "Delete files and directories",
"download": "Download",
"execute": "Execute commands",
"modify": "Edit files",
"rename": "Rename or move files and directories",
"share": "Share files",
"admin": "المدير",
"api": "إنشاء مفاتيح API طويلة الأمد وإدارتها",
@@ -274,9 +267,6 @@
"modifyOtherUser": "تعديل المستخدم:",
"modifyCurrentUser": "تعديل المستخدم الحالي",
"themeColor": "لون السمة",
"disableOfficePreviewDescription": "قائمة مفصولة بمسافات لامتدادات الملفات لتعطيل عارض OnlyOffice لها. (على سبيل المثال، \".txt .html\").",
"disableOfficePreview": "تعطيل عارض Onlyoffice لامتدادات ملفات معينة",
"filePreviewOptions": "خيارات معاينة الملف",
"listingOptions": "خيارات الإدراج",
"emptyGlobal": "لا توجد خيارات تهيئة عامة حاليًا، يمكنك تحرير yaml التكوين لضبط إعدادات النظام.",
"scopes": "تحديد المصادر المسموح بها ونطاق المستخدم داخلها",
@@ -289,7 +279,6 @@
"userScopeGenerationPlaceholder": "سيتم إنشاء النطاق تلقائيًا",
"createUserHomeDirectory": "إنشاء الدليل الرئيسي للمستخدم",
"api": "مفاتيح واجهة برمجة التطبيقات",
"setDateFormat": "تعيين تنسيق التاريخ الدقيق",
"shareDeleted": "تم حذف المشاركة",
"loginMethodDescription": "طريقة تسجيل الدخول المسموح بها للمستخدم.",
"userNotAdmin": "المستخدم ليس مسؤولاً",
@@ -298,10 +287,6 @@
"passwordsDoNotMatch": "كلمات المرور غير متطابقة",
"loginMethod": "طريقة تسجيل الدخول",
"shareSettings": "مشاركة الإعدادات",
"disableOfficeEditorDescription": "قائمة مفصولة بمسافات لامتدادات الملفات لتعطيل عارض OnlyOffice لها. (على سبيل المثال، \".txt .html\").",
"disableOfficeEditor": "تعطيل عارض المكتب لامتدادات ملفات معينة",
"disableOfficePreviews": "تعطيل معاينات المكتب لامتدادات ملفات معينة",
"disableOfficePreviewsDescription": "قائمة مفصولة بمسافة من امتدادات الملفات لتعطيل المعاينات المكتبية لها. (على سبيل المثال، \".txt .html\").",
"adminOptions": "خيارات المستخدم المسؤول",
"shareDuration": "تنتهي صلاحيتها",
"searchOptions": "خيارات البحث"
@@ -442,7 +427,9 @@
"disablePreviewExt": "تعطيل معاينات الملفات المكتبية لملحقات ملفات معينة",
"disablePreviewExtDescription": "قائمة مفصولة بمسافات لامتدادات الملفات لتعطيل معاينات ملفات المكتب (على سبيل المثال، '.docx .pptx').",
"disableOfficeEditorDescription": "قائمة مفصولة بمسافات لامتدادات الملفات لتعطيل محرر وعارض OnlyOffice لها. (على سبيل المثال، \".txt .html .pdf\").",
"disableOfficeEditor": "تعطيل عارض المكتب لامتدادات ملفات معينة"
"disableOfficeEditor": "تعطيل عارض المكتب لامتدادات ملفات معينة",
"defaultThemeDescription": "افتراضي - السمة الافتراضية",
"customTheme": "اختر المظهر الخاص بك"
},
"colors": {
"red": "أحمر",
+5 -5
View File
@@ -142,7 +142,6 @@
"prompts": {
"copy": "Kopírovat",
"copyMessage": "Vyberte místo, kam chcete soubory kopírovat:",
"currentlyNavigating": "Aktuálně procházíte:",
"deleteMessageMultiple": "Opravdu chcete smazat {count} soubor(ů)?",
"deleteMessageSingle": "Opravdu chcete smazat tento soubor/složku?",
"deleteMessageShare": "Opravdu chcete smazat toto sdílení: {path} ?",
@@ -174,8 +173,6 @@
"show": "Zobrazit",
"size": "Velikost",
"upload": "Nahrát",
"uploadFiles": "Nahrávám {files} souborů...",
"uploadMessage": "Vyberte možnost pro nahrání.",
"optionalPassword": "Volitelné heslo",
"typeName": "Typ:",
"deleted": "Úspěšně smazáno!",
@@ -184,7 +181,8 @@
"completed": "Dokončeno",
"conflictsDetected": "Zjištěné konflikty",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nesprávný zadaný název: nelze použít kontejner \"/\" nebo \"\\\"."
},
"search": {
"images": "Obrázky",
@@ -246,7 +244,9 @@
"disablePreviewExt": "Zakázat náhledy kancelářských souborů pro určité přípony souborů",
"disablePreviewExtDescription": "Seznam přípon souborů oddělených mezerou, pro které chcete zakázat náhledy kancelářských souborů (např. '.docx .pptx').",
"disableOfficeEditorDescription": "Seznam přípon souborů oddělených mezerou, pro které se má vypnout editor a prohlížeč OnlyOffice. (např. '.txt .html .pdf').",
"disableOfficeEditor": "Zakázat editor a prohlížeč Office pro určité přípony souborů a vrátit se k výchozímu prohlížeči/editoru, velmi užitečné pro soubory pdf a txt."
"disableOfficeEditor": "Zakázat editor a prohlížeč Office pro určité přípony souborů a vrátit se k výchozímu prohlížeči/editoru, velmi užitečné pro soubory pdf a txt.",
"defaultThemeDescription": "default - Výchozí téma",
"customTheme": "Vyberte si téma"
},
"settings": {
"enterPassword": "Zadejte heslo",
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Kopieren",
"copyMessage": "Wählen Sie einen Zielort zum Kopieren:",
"currentlyNavigating": "Aktueller Ort:",
"deleteMessageMultiple": "Sind Sie sicher, dass Sie {count} Datei(en) löschen möchten?",
"deleteMessageSingle": "Sind Sie sicher, dass Sie diesen Ordner/diese Datei löschen möchten?",
"deleteMessageShare": "Sind Sie sicher, dass Sie diese Freigabe löschen möchten ({path})?",
@@ -165,8 +164,6 @@
"show": "Anzeigen",
"size": "Größe",
"upload": "Hochladen",
"uploadFiles": "Hochladen von {files} Dateien...",
"uploadMessage": "Wählen Sie eine Hochladen-Methode",
"optionalPassword": "Optionales Kennwort",
"deleteUserMessage": "Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?",
"filesSelected": "ausgewählte Dateien",
@@ -178,7 +175,8 @@
"completed": "Abgeschlossen",
"conflictsDetected": "Erkannte Widersprüche",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Ungültiger Name angegeben: kann nicht Container \"/\" oder \"\\\""
},
"search": {
"images": "Bilder",
@@ -238,12 +236,7 @@
"passwordUpdated": "Kennwort aktualisiert!",
"path": "Pfad",
"permissions": {
"create": "Dateien und Ordner erstellen",
"delete": "Dateien und Ordner löschen",
"download": "Download",
"execute": "Befehle ausführen",
"modify": "Dateien bearbeiten",
"rename": "Umbenennen oder Verschieben von Dateien oder Ordnern",
"share": "Datei teilen",
"admin": "Verwalter",
"api": "Erstellen und Verwalten von API-Schlüsseln mit langer Lebensdauer",
@@ -257,7 +250,6 @@
"rules": "Regeln",
"rulesHelp": "Hier können Sie erlaubte und verbotene Aktionen für einen einzelnen Benutzer festlegen. Blockierte Dateien werden nicht gelistet und sind für den Nutzer nicht erreichbar. Reguläre Ausdrücke (Regex) und Pfade relativ zum Benutzerordner werden unterstützt. \n",
"scope": "Scope",
"setDateFormat": "Exaktes Datumsformat setzen",
"settingsUpdated": "Einstellungen aktualisiert!",
"shareManagement": "Freigaben verwalten",
"shareDeleted": "Freigabe gelöscht!",
@@ -280,9 +272,6 @@
"modifyOtherUser": "Benutzer ändern:",
"modifyCurrentUser": "Aktuellen Benutzer ändern",
"themeColor": "Designfarbe",
"disableOfficePreviewDescription": "Eine durch Leerzeichen getrennte Liste von Dateierweiterungen, für die der OnlyOffice-Viewer deaktiviert werden soll. (z. B. '.txt .html').",
"disableOfficePreview": "Onlyoffice-Viewer für bestimmte Dateierweiterungen deaktivieren",
"filePreviewOptions": "Optionen für die Dateivorschau",
"listingOptions": "Auflistung der Optionen",
"emptyGlobal": "Derzeit gibt es keine globalen Konfigurationsoptionen. Sie können die Konfigurations-Yaml bearbeiten, um die Systemeinstellungen anzupassen.",
"scopes": "Definition der zulässigen Quellen und des Benutzerbereichs innerhalb dieser Quellen",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Office-Dateivorschau für bestimmte Dateierweiterungen deaktivieren",
"disablePreviewExtDescription": "Eine durch Leerzeichen getrennte Liste von Dateierweiterungen, für die die Office-Dateivorschau deaktiviert werden soll (z. B. '.docx .pptx').",
"disableOfficeEditorDescription": "Eine durch Leerzeichen getrennte Liste von Dateierweiterungen, für die der Editor und der Viewer von OnlyOffice deaktiviert werden sollen. (z. B. '.txt .html .pdf').",
"disableOfficeEditor": "Office Viewer für bestimmte Dateierweiterungen deaktivieren"
"disableOfficeEditor": "Office Viewer für bestimmte Dateierweiterungen deaktivieren",
"defaultThemeDescription": "default - Das Standardthema",
"customTheme": "Wählen Sie Ihr Thema"
}
}
+5 -14
View File
@@ -167,10 +167,7 @@
"show": "Εμφάνιση",
"size": "Μέγεθος",
"upload": "Μεταφόρτωση",
"uploadFiles": "Μεταφόρτωση {files} αρχείων…",
"uploadMessage": "Επιλέξτε μια επιλογή για τη μεταφόρτωση.",
"optionalPassword": "Προαιρετικός κωδικός πρόσβασης",
"currentlyNavigating": "Αυτή τη στιγμή πλοηγείστε στο:",
"deleteUserMessage": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτόν τον χρήστη;",
"filesSelected": "επιλεγμένα αρχεία",
"typeName": "Τύπος:",
@@ -181,7 +178,8 @@
"completed": "Ολοκληρωμένο",
"conflictsDetected": "Ανιχνεύονται συγκρούσεις",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Μη έγκυρο όνομα: δεν μπορεί να γίνει container \"/\" ή \"\\\""
},
"search": {
"images": "Εικόνες",
@@ -244,12 +242,7 @@
"passwordUpdated": "Ο κωδικός πρόσβασης ενημερώθηκε!",
"path": "Διαδρομή",
"permissions": {
"create": "Δημιουργία αρχείων και φακέλων",
"delete": "Διαγραφή αρχείων και φακέλων",
"download": "Λήψη",
"execute": "Εκτέλεση εντολών",
"modify": "Επεξεργασία αρχείων",
"rename": "Μετονομασία ή μετακίνηση αρχείων και φακέλων",
"share": "Κοινοποίηση αρχείων",
"admin": "Διαχειριστής",
"api": "Δημιουργία και διαχείριση κλειδιών API μακράς διάρκειας ζωής",
@@ -263,7 +256,6 @@
"rules": "Κανόνες",
"rulesHelp": "Εδώ μπορείτε να ορίσετε ένα σύνολο κανόνων που επιτρέπουν και απαγορεύουν διάφορες λειτουργίες για τον συγκεκριμένο χρήστη. Τα αποκλεισμένα αρχεία δεν θα εμφανίζονται στα περιεχόμενα των αντίστοιχων φακέλων και δεν θα είναι προσβάσιμα από τον χρήστη. Υποστηρίζονται εκφράσεις regex και διαδρομές σχετικές με την εμβέλεια αρχείων των χρηστών.\n",
"scope": "Εμβέλεια",
"setDateFormat": "Ορισμός ακριβούς μορφής ημερομηνίας",
"settingsUpdated": "Οι ρυθμίσεις ενημερώθηκαν!",
"shareManagement": "Διαχείριση κοινοποίησης",
"shareDeleted": "Η κοινοποίηση διαγράφηκε!",
@@ -286,9 +278,6 @@
"modifyOtherUser": "Τροποποίηση χρήστη:",
"modifyCurrentUser": "Τροποποίηση τρέχοντος χρήστη",
"themeColor": "Χρώμα θέματος",
"disableOfficePreviewDescription": "Ένας κατάλογος επεκτάσεων αρχείων με διαχωρισμό διαστήματος για την απενεργοποίηση της προβολής OnlyOffice. (π.χ. '.txt .html').",
"disableOfficePreview": "Απενεργοποίηση της προβολής onlyoffice για ορισμένες επεκτάσεις αρχείων",
"filePreviewOptions": "Επιλογές προεπισκόπησης αρχείων",
"listingOptions": "Επιλογές καταχώρισης",
"emptyGlobal": "Προς το παρόν δεν υπάρχουν καθολικές επιλογές ρυθμίσεων, Μπορείτε να επεξεργαστείτε το yaml ρυθμίσεων για να προσαρμόσετε τις ρυθμίσεις του συστήματος.",
"scopes": "Καθορισμός των επιτρεπόμενων πηγών και του πεδίου εφαρμογής του χρήστη σε αυτές",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Απενεργοποίηση προεπισκοπήσεων αρχείων γραφείου για ορισμένες επεκτάσεις αρχείων",
"disablePreviewExtDescription": "Μια λίστα επεκτάσεων αρχείων με διαχωρισμό διαστήματος για την απενεργοποίηση της προεπισκόπησης αρχείων γραφείου (π.χ. '.docx .pptx').",
"disableOfficeEditorDescription": "Μια λίστα επεκτάσεων αρχείων με διαχωρισμό διαστήματος για την απενεργοποίηση του επεξεργαστή και της προβολής του OnlyOffice. (π.χ. '.txt .html .pdf').",
"disableOfficeEditor": "Απενεργοποίηση της προβολής γραφείου για ορισμένες επεκτάσεις αρχείων"
"disableOfficeEditor": "Απενεργοποίηση της προβολής γραφείου για ορισμένες επεκτάσεις αρχείων",
"defaultThemeDescription": "default - Το προεπιλεγμένο θέμα",
"customTheme": "Επιλέξτε το θέμα σας"
}
}
+4 -4
View File
@@ -140,10 +140,10 @@
"notice": "Note: anyone who has access to the link (and optional password) can access the shared files. There is no requirement to be logged in."
},
"prompts": {
"invalidName": "Invalid name provided: cannot container \"/\" or \"\\\"",
"destinationSource": "Destination Source:",
"copy": "Copy",
"copyMessage": "Choose the place to copy your files:",
"currentlyNavigating": "Currently navigating on:",
"deleteMessageMultiple": "Are you sure you want to delete {count} file(s)?",
"deleteMessageSingle": "Are you sure you want to delete this file/folder?",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
@@ -169,7 +169,7 @@
"numberDirs": "Number of directories",
"numberFiles": "Number of files",
"rename": "Rename",
"renameMessage": "Insert a new name for",
"renameMessage": "An item named \"{filename}\" already exists! Enter a new name:",
"replace": "Replace",
"replaceMessage": "One of the files you're trying to upload is conflicting because of its name. Do you wish to continue to upload or replace the existing one?\n",
"schedule": "Schedule",
@@ -177,8 +177,6 @@
"show": "Show",
"size": "Size",
"upload": "Upload",
"uploadFiles": "Uploading {files} files...",
"uploadMessage": "Select an option to upload.",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"optionalPassword": "Optional password",
@@ -202,6 +200,8 @@
"helpText2": "The index: Search utilizes the index which automatically gets updated on the configured interval (default: 5 minutes). Searching when the program has just started may result in incomplete results."
},
"profileSettings": {
"customTheme": "Choose your theme",
"defaultThemeDescription": "default - The default theme",
"setDateFormat": "Set exact date format",
"showHiddenFiles": "Show hidden files",
"showHiddenFilesDescription": "Show hidden files in listing (e.g. .thumbnails, ProgramData), but they are still manually accessible if configured to be indexed.",
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Copiar",
"copyMessage": "Elige el lugar donde quieres copiar tus archivos:",
"currentlyNavigating": "Actualmente estás en:",
"deleteMessageMultiple": "¿Estás seguro que quieres eliminar {count} archivo(s)?",
"deleteMessageSingle": "¿Estás seguro que quieres eliminar este archivo/carpeta?",
"deleteMessageShare": "¿Está seguro de que quiere eliminar este recurso compartido({path})?",
@@ -165,8 +164,6 @@
"show": "Mostrar",
"size": "Tamaño",
"upload": "Subir",
"uploadFiles": "Subiendo {files} archivos...",
"uploadMessage": "Seleccione una opción para subir.",
"optionalPassword": "Contraseña opcional",
"deleteUserMessage": "¿Está seguro de que desea eliminar este usuario?",
"filesSelected": "archivos seleccionados",
@@ -178,7 +175,8 @@
"completed": "Completado",
"conflictsDetected": "Conflictos detectados",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nombre no válido proporcionado: no puede contener \"/\" o \"\\\"."
},
"search": {
"images": "Imágenes",
@@ -238,12 +236,7 @@
"passwordUpdated": "¡Contraseña actualizada!",
"path": "Ruta",
"permissions": {
"create": "Crear ficheros y directorios",
"delete": "Eliminar ficheros y directorios",
"download": "Descargar",
"execute": "Executar comandos",
"modify": "Editar ficheros",
"rename": "Renombrar o mover ficheros y directorios",
"share": "Compartir ficheros",
"admin": "Administrador",
"api": "Crear y gestionar claves API de larga duración",
@@ -257,7 +250,6 @@
"rules": "Reglas",
"rulesHelp": "Aquí puedes definir un conjunto de reglas de permisos para este usuario específico. Los archivos bloqueados no se mostrarán en las listas y no serán accesibles por el usuario. Puedes utilizar regex y rutas relativas a la raíz del usuario.\n",
"scope": "Raíz",
"setDateFormat": "Establecer el formato exacto de la fecha",
"settingsUpdated": "¡Ajustes actualizados!",
"shareManagement": "Gestión Compartida",
"shareDeleted": "¡Recurso compartido eliminado!",
@@ -280,9 +272,6 @@
"modifyOtherUser": "Modificar usuario:",
"modifyCurrentUser": "Modificar el usuario actual",
"themeColor": "Tema Color",
"disableOfficePreviewDescription": "Una lista separada por espacios de las extensiones de archivo para las que desea desactivar el visor de OnlyOffice. (por ejemplo, '.txt .html').",
"disableOfficePreview": "Desactivar onlyoffice viewer para determinadas extensiones de archivo",
"filePreviewOptions": "Opciones de previsualización de archivos",
"listingOptions": "Opciones de listado",
"emptyGlobal": "Actualmente no hay opciones de configuración global, puede editar el yaml de configuración para ajustar la configuración del sistema.",
"UserManagement": "Gestión de usuarios",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Desactivar la vista previa de archivos de Office para determinadas extensiones de archivo",
"disablePreviewExtDescription": "Una lista separada por espacios de las extensiones de archivo para las que se desactivará la vista previa de los archivos de Office (por ejemplo, '.docx .pptx').",
"disableOfficeEditorDescription": "Una lista separada por espacios de las extensiones de archivo para las que desea desactivar el editor y el visor de OnlyOffice. (por ejemplo, '.txt .html .pdf').",
"disableOfficeEditor": "Desactivar el visor de office para determinadas extensiones de archivo"
"disableOfficeEditor": "Desactivar el visor de office para determinadas extensiones de archivo",
"defaultThemeDescription": "default - El tema por defecto",
"customTheme": "Elija su tema"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Copier",
"copyMessage": "Choisissez l'emplacement où copier la sélection :",
"currentlyNavigating": "Dossier courant :",
"deleteMessageMultiple": "Êtes-vous sûr de vouloir supprimer ces {count} élément(s) ?",
"deleteMessageSingle": "Êtes-vous sûr de vouloir supprimer cet élément ?",
"deleteMessageShare": "Êtes-vous sûr de vouloir supprimer ce partage ({path}) ?",
@@ -165,8 +164,6 @@
"show": "Montrer",
"size": "Taille",
"upload": "Importer",
"uploadFiles": "Importation de {files} fichiers...",
"uploadMessage": "Sélectionnez une option d'import.",
"optionalPassword": "Mot de passe optionnel",
"deleteUserMessage": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
"filesSelected": "fichiers sélectionnés",
@@ -178,7 +175,8 @@
"completed": "Terminé",
"conflictsDetected": "Conflits détectés",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nom invalide fourni : ne peut contenir \"/\" ou \"\\\"."
},
"search": {
"images": "Images",
@@ -235,12 +233,7 @@
"passwordUpdated": "Mot de passe mis à jour !",
"path": "Chemin",
"permissions": {
"create": "Créer des fichiers et des dossiers",
"delete": "Supprimer des fichiers et des dossiers",
"download": "Télécharger",
"execute": "Exécuter des commandes",
"modify": "Modifier des fichiers",
"rename": "Renommer ou déplacer des fichiers ou des dossiers",
"share": "Partager des fichiers",
"admin": "Administrateur",
"api": "Créer et gérer des clés API à longue durée de vie",
@@ -254,7 +247,6 @@
"rules": "Règles",
"rulesHelp": "Vous pouvez définir ici un ensemble de règles pour cet utilisateur. Les fichiers bloqués ne seront pas affichés et ne seront pas accessibles par l'utilisateur. Les expressions régulières sont supportées et les chemins d'accès sont relatifs par rapport au dossier de l'utilisateur.\n",
"scope": "Portée du dossier utilisateur",
"setDateFormat": "Définir le format de la date",
"settingsUpdated": "Les paramètres ont été mis à jour !",
"shareManagement": "Gestion des partages",
"shareDeleted": "Partage supprimé !",
@@ -277,9 +269,6 @@
"modifyOtherUser": "Modifier l'utilisateur :",
"modifyCurrentUser": "Modifier l'utilisateur actuel",
"themeColor": "Couleur du thème",
"disableOfficePreviewDescription": "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').",
"disableOfficePreview": "Désactiver la visionneuse d'Office pour certaines extensions de fichiers",
"filePreviewOptions": "Options de prévisualisation des fichiers",
"listingOptions": "Options d'inscription",
"emptyGlobal": "Actuellement, il n'y a pas d'options de configuration globale. Vous pouvez éditer le fichier de configuration yaml pour ajuster les paramètres du système.",
"UserManagement": "Gestion des utilisateurs",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Désactiver les aperçus de fichiers Office pour certaines extensions de fichiers",
"disablePreviewExtDescription": "Une liste d'extensions de fichiers séparées par des espaces pour lesquelles les aperçus de fichiers Office doivent être désactivés (par exemple, '.docx .pptx').",
"disableOfficeEditorDescription": "Une liste d'extensions de fichiers séparées par des espaces pour lesquelles l'éditeur et la visionneuse OnlyOffice doivent être désactivés. (par exemple, '.txt .html .pdf').",
"disableOfficeEditor": "Désactiver la visionneuse d'office pour certaines extensions de fichiers"
"disableOfficeEditor": "Désactiver la visionneuse d'office pour certaines extensions de fichiers",
"defaultThemeDescription": "default - Le thème par défaut",
"customTheme": "Choisissez votre thème"
}
}
+5 -11
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "העתק",
"copyMessage": "בחר לאן להעתיק את הקבצים:",
"currentlyNavigating": "כרגע מנווט ב:",
"deleteMessageMultiple": "האם אתה בטוח שברצונך למחוק {count} קבצים?",
"deleteMessageSingle": "האם אתה בטוח שברצונך למחוק את הקובץ או התקייה?",
"deleteMessageShare": "האם אתה בטוח שברצונך למחוק את השיתוף הזה?({path})?",
@@ -165,8 +164,6 @@
"show": "הצג",
"size": "גודל",
"upload": "העלאה",
"uploadFiles": "מעלה {files} קבצים...",
"uploadMessage": "בחר אפשרות העלאה.",
"optionalPassword": "סיסמא אופציונלית",
"deleteUserMessage": "האם אתה בטוח שברצונך למחוק משתמש זה?",
"selected": "נבחר",
@@ -178,7 +175,8 @@
"completed": "הושלם",
"conflictsDetected": "סכסוכים שזוהו",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "שם לא חוקי: לא ניתן להכיל \"/\" או \"\\\""
},
"search": {
"images": "תמונות",
@@ -236,12 +234,7 @@
"passwordUpdated": "הסיסמא עודכנה!",
"path": "נתיב",
"permissions": {
"create": "יצירת קבצים ותקיות",
"delete": "מחיקת קבצים ותקיות",
"download": "הורדת קבצים ותקיות",
"execute": "ביצוע פקודות",
"modify": "עריכת קבצים קבצים",
"rename": "שינוי שם או העברת קבצים ותקיות",
"share": "שיתוף קבצים",
"admin": "מנהל",
"api": "יצירת מפתחות API ארוכי טווח וניהולם",
@@ -255,7 +248,6 @@
"rules": "חוקים",
"rulesHelp": "כאן אתה יכול להגדיר רשימה של כללים למשתמש ספציפי, רשימה שחורה ולבנה. הקבצים החסומים לא יופיעו ברשימת הקבצים ולא יהיו נגישים למשתמש. יש תמיכה בנתיבים (ביחס לתקייה הראשית של המשתמש), וגם בביטוי רגולרי.\n",
"scope": "תחום",
"setDateFormat": "הגדר פורמט תאריך",
"settingsUpdated": "ההגדרות עודכנו!",
"shareManagement": "ניהול שיתוף",
"shareDeleted": "שיתוף נמחק!",
@@ -443,6 +435,8 @@
"disablePreviewExt": "השבת תצוגה מקדימה של קבצי Office עבור סיומות קבצים מסוימות",
"disablePreviewExtDescription": "רשימה מופרדת ברווחים של סיומות קבצים שצריך להשבית את תצוגה מקדימה של קבצי Office עבורן (לדוגמה, '.docx .pptx').",
"disableOfficeEditorDescription": "רשימה מופרדת ברווחים של סיומות קבצים עבורן יש להשבית את עורך ומציג OnlyOffice. (לדוגמה, '.txt .html .pdf').",
"disableOfficeEditor": "השבת תצוגת Office עבור סיומות קבצים מסוימות"
"disableOfficeEditor": "השבת תצוגת Office עבור סיומות קבצים מסוימות",
"defaultThemeDescription": "ברירת מחדל - ערכת הנושא המוגדרת כברירת מחדל",
"customTheme": "בחר את הנושא שלך"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Másolása",
"copyMessage": "Válassza ki a másolás célját:",
"currentlyNavigating": "Jelenlegi helyzet:",
"deleteMessageMultiple": "Biztosan törölni szeretne {count} fájlt?",
"deleteMessageSingle": "Biztosan törölni szeretné ezt a fájl vagy mappát?",
"deleteMessageShare": "Biztosan törölni szeretné ezt a megosztást ({path})?",
@@ -165,8 +164,6 @@
"show": "Megjelenítés",
"size": "Méret",
"upload": "Feltöltés",
"uploadFiles": "{files} fájl feltöltése…",
"uploadMessage": "Válasszon egy feltöltési módot.",
"optionalPassword": "Választható jelszó",
"deleteUserMessage": "Biztos, hogy törölni szeretné ezt a felhasználót?",
"filesSelected": "kiválasztott fájlok",
@@ -178,7 +175,8 @@
"completed": "Befejezett",
"conflictsDetected": "Feltárt konfliktusok",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Érvénytelen név: nem lehet \"/\" vagy \"\\\" konténer"
},
"search": {
"images": "Képek",
@@ -236,12 +234,7 @@
"passwordUpdated": "Jelszó frissítve!",
"path": "Elérési útvonal",
"permissions": {
"create": "Fájlok és mappák létrehozása",
"delete": "Fájlok és mappák törlése",
"download": "Letöltése",
"execute": "Parancsok futtatása",
"modify": "Fájlok szerkesztése",
"rename": "Fájlok és mappák átnevezése vagy mozgatása",
"share": "Fájlok megosztása",
"admin": "Adminisztrátor",
"api": "Hosszú élettartamú API-kulcsok létrehozása és kezelése",
@@ -255,7 +248,6 @@
"rules": "Szabályok",
"rulesHelp": "Meghatározhat egy sor engedélyezési és tiltási szabályt az adott felhasználó számára. A letiltott fájlok nem jelennek meg a listákban, és nem lesznek elérhetőek a felhasználó számára. A reguláris kifejezések és a felhasználói hatókörhöz viszonyított elérési utak támogatottak.\n",
"scope": "Hatókör",
"setDateFormat": "Pontos dátumformátum beállítása",
"settingsUpdated": "Beállítások frissítve!",
"shareManagement": "Megosztáskezelés",
"shareDeleted": "Megosztás törölve!",
@@ -278,9 +270,6 @@
"modifyOtherUser": "Felhasználó módosítása:",
"modifyCurrentUser": "Jelenlegi felhasználó módosítása",
"themeColor": "Téma színe",
"disableOfficePreviewDescription": "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').",
"disableOfficePreview": "Letiltja a onlyoffice viewer-t bizonyos fájlkiterjesztések esetén",
"filePreviewOptions": "Fájl előnézeti beállítások",
"listingOptions": "Listázási lehetőségek",
"emptyGlobal": "Jelenleg nincsenek globális konfigurációs beállítások, A rendszerbeállítások módosításához szerkesztheti a konfigurációs yaml-t.",
"scopes": "Az engedélyezett források és az azokon belüli felhasználói hatókörök meghatározása",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Office-fájlok előnézetének letiltása bizonyos fájlkiterjesztések esetében",
"disablePreviewExtDescription": "Az irodai fájlok előnézetének letiltására szolgáló fájlkiterjesztések szóközzel elválasztott listája (pl. '.docx .pptx').",
"disableOfficeEditorDescription": "Az OnlyOffice szerkesztő és megjelenítő letiltandó fájlkiterjesztések szóközzel elválasztott listája. (pl. \".txt .html .pdf\").",
"disableOfficeEditor": "Bizonyos fájlkiterjesztéseknél letilthatja az Office Viewer-t"
"disableOfficeEditor": "Bizonyos fájlkiterjesztéseknél letilthatja az Office Viewer-t",
"defaultThemeDescription": "default - Az alapértelmezett téma",
"customTheme": "Válasszon témát"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Copia",
"copyMessage": "Seleziona la cartella in cui copiare i file:",
"currentlyNavigating": "Attualmente navigando su:",
"deleteMessageMultiple": "Sei sicuro di voler eliminare {count} file?",
"deleteMessageSingle": "Sei sicuro di voler eliminare questo file/cartella?",
"deleteTitle": "Elimina",
@@ -164,11 +163,9 @@
"show": "Mostra",
"size": "Dimensione",
"upload": "Carica",
"uploadMessage": "Seleziona un'opzione per il caricamento.",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"deleteUserMessage": "Siete sicuri di voler eliminare questo utente?",
"filesSelected": "file selezionati",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Password opzionale",
"typeName": "Tipo:",
"selected": "selezionato",
@@ -178,7 +175,8 @@
"completed": "Completato",
"conflictsDetected": "Conflitti rilevati",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nome non valido: non può contenere \"/\" o \"\\\"."
},
"search": {
"images": "Immagini",
@@ -234,12 +232,7 @@
"passwordUpdated": "Password aggiornata!",
"path": "Percorso",
"permissions": {
"create": "Creare file e cartelle",
"delete": "Eliminare file e cartelle",
"download": "Scaricare",
"execute": "Eseguire comandi",
"modify": "Modificare file",
"rename": "Rinominare o spostare file e cartelle",
"share": "Condividere file",
"admin": "Amministratore",
"api": "Creare e gestire chiavi API a lunga durata",
@@ -274,9 +267,6 @@
"modifyOtherUser": "Modificare l'utente:",
"modifyCurrentUser": "Modificare l'utente corrente",
"themeColor": "Colore del tema",
"disableOfficePreviewDescription": "Un elenco separato da spazi di estensioni di file per cui disabilitare il visualizzatore di OnlyOffice. (ad esempio, '.txt .html').",
"disableOfficePreview": "Disabilitare il visualizzatore onlyoffice per alcune estensioni di file",
"filePreviewOptions": "Opzioni di anteprima dei file",
"listingOptions": "Opzioni di quotazione",
"emptyGlobal": "Attualmente non ci sono opzioni di configurazione globale, ma è possibile modificare lo yaml di configurazione per regolare le impostazioni del sistema.",
"scopes": "Definire le fonti consentite e l'ambito dell'utente all'interno di esse",
@@ -289,7 +279,6 @@
"userScopeGenerationPlaceholder": "L'ambito sarà generato automaticamente",
"createUserHomeDirectory": "Creare la directory home dell'utente",
"api": "Chiavi API",
"setDateFormat": "Impostare il formato esatto della data",
"shareDeleted": "Condividi cancellato!",
"loginMethodDescription": "metodo di accesso consentito per l'utente.",
"userNotAdmin": "L'utente non è un amministratore",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Disattivare le anteprime dei file di Office per alcune estensioni di file",
"disablePreviewExtDescription": "Un elenco separato da spazi di estensioni di file per cui disabilitare le anteprime dei file di Office (ad esempio, '.docx .pptx').",
"disableOfficeEditorDescription": "Un elenco separato da spazi di estensioni di file per cui disabilitare l'editor e il visualizzatore di OnlyOffice. (ad esempio, '.txt .html .pdf').",
"disableOfficeEditor": "Disattivare il visualizzatore di Office per alcune estensioni di file"
"disableOfficeEditor": "Disattivare il visualizzatore di Office per alcune estensioni di file",
"defaultThemeDescription": "default - Il tema predefinito",
"customTheme": "Scegliete il vostro tema"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "コピー",
"copyMessage": "コピーの目標ディレクトリを選択してください:",
"currentlyNavigating": "現在閲覧しているディレクトリ:",
"deleteMessageMultiple": "{count} つのファイルを本当に削除してよろしいですか。",
"deleteMessageSingle": "このファイル/フォルダを本当に削除してよろしいですか。",
"deleteTitle": "ファイルを削除",
@@ -164,11 +163,9 @@
"show": "表示",
"size": "サイズ",
"upload": "アップロード",
"uploadMessage": "アップロードするオプションを選択します。",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"deleteUserMessage": "本当にこのユーザーを削除しますか?",
"filesSelected": "選択されたファイル",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "任意のパスワード",
"typeName": "タイプ",
"selected": "選ばれた",
@@ -178,7 +175,8 @@
"completed": "完成",
"conflictsDetected": "コンフリクト検出",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "無効な名前が指定されました:\"/\"または\"˶\"をコンテナに入れることはできません。"
},
"search": {
"images": "画像",
@@ -234,12 +232,7 @@
"passwordUpdated": "パスワードは更新されました!",
"path": "パス",
"permissions": {
"create": "Create files and directories",
"delete": "Delete files and directories",
"download": "Download",
"execute": "Execute commands",
"modify": "Edit files",
"rename": "Rename or move files and directories",
"share": "Share files",
"admin": "管理者",
"api": "長寿命のAPIキーの作成と管理",
@@ -274,9 +267,6 @@
"modifyOtherUser": "ユーザーを変更する:",
"modifyCurrentUser": "現在のユーザーを変更する",
"themeColor": "テーマカラー",
"disableOfficePreviewDescription": "OnlyOffice ビューアを無効にするファイル拡張子をスペース区切りで指定します。(例: '.txt .html')。",
"disableOfficePreview": "特定のファイル拡張子でonlyofficeビューアを無効にする",
"filePreviewOptions": "ファイルプレビューオプション",
"listingOptions": "リストオプション",
"emptyGlobal": "現在のところ、グローバルな設定オプションはありませんが、コンフィギュレーションyamlを編集してシステム設定を調整することができます。",
"scopes": "許可されるソースとその中のユーザースコープを定義する",
@@ -289,7 +279,6 @@
"userScopeGenerationPlaceholder": "スコープは自動生成されます。",
"createUserHomeDirectory": "ユーザーホームディレクトリの作成",
"api": "APIキー",
"setDateFormat": "正確な日付書式を設定する",
"shareDeleted": "削除されたシェア",
"loginMethodDescription": "ユーザーに許可されたログイン方法。",
"userNotAdmin": "ユーザーが管理者でない",
@@ -446,6 +435,8 @@
"disablePreviewExt": "特定のファイル拡張子でオフィスファイルのプレビューを無効にする",
"disablePreviewExtDescription": "オフィスファイルのプレビューを無効にするファイル拡張子をスペースで区切ったリスト (例: '.docx .pptx')。",
"disableOfficeEditorDescription": "OnlyOfficeエディタおよびビューアを無効にするファイル拡張子のスペース区切りリスト。(例: '.txt .html .pdf')。",
"disableOfficeEditor": "特定のファイル拡張子のオフィスビューアを無効にする"
"disableOfficeEditor": "特定のファイル拡張子のオフィスビューアを無効にする",
"defaultThemeDescription": "default - デフォルトのテーマ",
"customTheme": "テーマを選ぶ"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "복사",
"copyMessage": "복사할 디렉토리:",
"currentlyNavigating": "현재 위치:",
"deleteMessageMultiple": "{count} 개의 파일을 삭제하시겠습니까?",
"deleteMessageSingle": "파일 혹은 디렉토리를 삭제하시겠습니까?",
"deleteTitle": "파일 삭제",
@@ -164,11 +163,9 @@
"show": "보기",
"size": "크기",
"upload": "업로드",
"uploadMessage": "업로드할 옵션을 선택합니다.",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"deleteUserMessage": "이 사용자를 삭제하시겠습니까?",
"filesSelected": "선택한 파일",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "선택적 비밀번호",
"typeName": "유형:",
"selected": "선택",
@@ -178,7 +175,8 @@
"completed": "완료",
"conflictsDetected": "충돌 감지",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "제공된 이름이 잘못되었습니다: \"/\" 또는 \"\\\"를 포함할 수 없습니다."
},
"search": {
"images": "이미지",
@@ -234,12 +232,7 @@
"passwordUpdated": "비밀번호 수정 완료!",
"path": "경로",
"permissions": {
"create": "파일이나 디렉토리 생성하기",
"delete": "화일이나 디렉토리 삭제하기",
"download": "다운로드",
"execute": "명령 실행",
"modify": "파일 편집",
"rename": "파일 이름 변경 또는 디렉토리 이동",
"share": "파일 공유하기",
"admin": "관리자",
"api": "긴 수명의 API 키 생성 및 관리",
@@ -274,9 +267,6 @@
"modifyOtherUser": "사용자를 수정합니다:",
"modifyCurrentUser": "현재 사용자 수정",
"themeColor": "테마 색상",
"disableOfficePreviewDescription": "OnlyOffice 뷰어를 비활성화할 파일 확장명을 공백으로 구분한 목록입니다. (예: '.txt .html').",
"disableOfficePreview": "특정 파일 확장자에 대해서만오피스 뷰어 사용 안 함",
"filePreviewOptions": "파일 미리 보기 옵션",
"listingOptions": "리스팅 옵션",
"emptyGlobal": "현재 글로벌 설정 옵션은 없으며, 구성 yaml을 편집하여 시스템 설정을 조정할 수 있습니다.",
"scopes": "허용된 소스와 그 안에서 사용자 범위 정의하기",
@@ -289,7 +279,6 @@
"userScopeGenerationPlaceholder": "범위가 자동으로 생성됩니다.",
"createUserHomeDirectory": "사용자 홈 디렉터리 만들기",
"api": "API 키",
"setDateFormat": "정확한 날짜 형식 설정",
"shareDeleted": "공유 삭제됨!",
"loginMethodDescription": "사용자에게 허용된 로그인 방법을 설정합니다.",
"userNotAdmin": "사용자가 관리자가 아닙니다.",
@@ -446,6 +435,8 @@
"disablePreviewExt": "특정 파일 확장명에 대한 Office 파일 미리 보기 비활성화",
"disablePreviewExtDescription": "Office 파일 미리 보기를 사용하지 않도록 설정할 파일 확장명 목록(예: '.docx .pptx')을 공백으로 구분하여 표시합니다.",
"disableOfficeEditorDescription": "OnlyOffice 편집기 및 뷰어를 비활성화할 파일 확장명을 공백으로 구분한 목록입니다. (예: '.txt .html .pdf').",
"disableOfficeEditor": "특정 파일 확장자에 대해 Office 뷰어 사용 안 함"
"disableOfficeEditor": "특정 파일 확장자에 대해 Office 뷰어 사용 안 함",
"defaultThemeDescription": "default - 기본 테마",
"customTheme": "테마 선택"
}
}
+5 -10
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Kopiëren",
"copyMessage": "Kies een locatie om uw bestanden te kopiëren: ",
"currentlyNavigating": "Momenteel zoeken op: ",
"deleteMessageMultiple": "Weet u zeker dat u {count} bestand(en) wil verwijderen?",
"deleteMessageSingle": "Weet u zeker dat u dit bestand/map wil verwijderen?",
"deleteTitle": "Bestanden verwijderen",
@@ -164,12 +163,10 @@
"show": "Tonen",
"size": "Grootte",
"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!",
@@ -178,7 +175,8 @@
"completed": "Voltooid",
"conflictsDetected": "Gedetecteerde conflicten",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Ongeldige naam opgegeven: kan geen \"/\" of \"˜\" bevatten."
},
"search": {
"images": "Afbeeldingen",
@@ -234,12 +232,7 @@
"passwordUpdated": "Wachtwoord bijgewerkt!",
"path": "Pad",
"permissions": {
"create": "Bestanden en mappen aanmaken",
"delete": "Bestanden en mappen verwijderen",
"download": "Downloaden",
"execute": "Commando's uitvoeren",
"modify": "Bestanden aanpassen",
"rename": "Bestanden of mappen hernoemen of verplaatsen",
"share": "Bestanden delen",
"admin": "Beheerder",
"api": "Langlevende API-sleutels maken en beheren",
@@ -442,6 +435,8 @@
"disablePreviewExt": "Office-bestandsvoorvertoningen uitschakelen voor bepaalde bestandsextensies",
"disablePreviewExtDescription": "Een door spaties gescheiden lijst met bestandsextensies waarvoor Office-bestandsvoorvertoningen moeten worden uitgeschakeld (bijvoorbeeld '.docx .pptx').",
"disableOfficeEditorDescription": "Een door spaties gescheiden lijst met bestandsextensies waarvoor de OnlyOffice editor en viewer uitgeschakeld moeten worden. (bijv. '.txt .html .pdf').",
"disableOfficeEditor": "Office Viewer uitschakelen voor bepaalde bestandsextensies"
"disableOfficeEditor": "Office Viewer uitschakelen voor bepaalde bestandsextensies",
"defaultThemeDescription": "standaard - Het standaardthema",
"customTheme": "Kies je thema"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Kopiuj",
"copyMessage": "Wybierz lokalizację do której mają być skopiowane wybrane pliki",
"currentlyNavigating": "Obecnie przeglądasz:",
"deleteMessageMultiple": "Czy jesteś pewien że chcesz usunąć {count} plik(ów)?",
"deleteMessageSingle": "Czy jesteś pewien, że chcesz usunąć ten plik/folder?",
"deleteTitle": "Usuń pliki",
@@ -164,11 +163,9 @@
"show": "Pokaż",
"size": "Rozmiar",
"upload": "Prześlij",
"uploadMessage": "Proszę wybrać metodę przesyłania",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"deleteUserMessage": "Czy na pewno chcesz usunąć tego użytkownika?",
"filesSelected": "wybrane pliki",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Hasło opcjonalne",
"typeName": "Typ:",
"selected": "wybrany",
@@ -178,7 +175,8 @@
"completed": "Zakończono",
"conflictsDetected": "Wykryte konflikty",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nieprawidłowa nazwa: nie można użyć kontenera \"/\" lub \"\\\""
},
"search": {
"images": "Zdjęcia",
@@ -234,12 +232,7 @@
"passwordUpdated": "Hasło zostało zapisane!",
"path": "Ścieżka",
"permissions": {
"create": "Tworzenie plików i katalogów",
"delete": "Usuwanie plików i katalogów",
"download": "Pobieranie",
"execute": "Wykonywanie poleceń",
"modify": "Edycja plików",
"rename": "Zmiana nazw lub przenoszenie plików i katalogów",
"share": "Udostępnianie plików",
"admin": "Administrator",
"api": "Tworzenie i zarządzanie kluczami API o długiej żywotności",
@@ -274,9 +267,6 @@
"modifyOtherUser": "Modyfikacja użytkownika:",
"modifyCurrentUser": "Modyfikacja bieżącego użytkownika",
"themeColor": "Kolor motywu",
"disableOfficePreviewDescription": "Rozdzielana spacjami lista rozszerzeń plików, dla których przeglądarka OnlyOffice ma zostać wyłączona. (np. \".txt .html\").",
"disableOfficePreview": "Wyłącz przeglądarkę onlyoffice dla niektórych rozszerzeń plików",
"filePreviewOptions": "Opcje podglądu plików",
"listingOptions": "Opcje aukcji",
"emptyGlobal": "Obecnie nie ma globalnych opcji konfiguracyjnych, można edytować konfigurację yaml, aby dostosować ustawienia systemu.",
"scopes": "Definiowanie dozwolonych źródeł i zakresu użytkowników w ich obrębie",
@@ -289,7 +279,6 @@
"userScopeGenerationPlaceholder": "Zakres zostanie wygenerowany automatycznie",
"createUserHomeDirectory": "Tworzenie katalogu domowego użytkownika",
"api": "Klucze API",
"setDateFormat": "Ustaw dokładny format daty",
"shareDeleted": "Usunięto udostępnianie!",
"loginMethodDescription": "dozwolona metoda logowania dla użytkownika.",
"userNotAdmin": "Użytkownik nie jest administratorem",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Wyłącz podgląd plików pakietu Office dla niektórych rozszerzeń plików",
"disablePreviewExtDescription": "Rozdzielana spacjami lista rozszerzeń plików do wyłączenia podglądu plików biurowych (np. \".docx .pptx\").",
"disableOfficeEditorDescription": "Rozdzielana spacjami lista rozszerzeń plików, dla których edytor i przeglądarka OnlyOffice mają zostać wyłączone. (np. \".txt .html .pdf\").",
"disableOfficeEditor": "Wyłącz przeglądarkę Office dla niektórych rozszerzeń plików"
"disableOfficeEditor": "Wyłącz przeglądarkę Office dla niektórych rozszerzeń plików",
"defaultThemeDescription": "default - domyślny motyw",
"customTheme": "Wybierz motyw"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Copiar",
"copyMessage": "Escolha um lugar para copiar os arquivos:",
"currentlyNavigating": "Navegando em:",
"deleteMessageMultiple": "Deseja apagar {count} arquivo(s)?",
"deleteMessageSingle": "Deseja apagar esta pasta/arquivo?",
"deleteMessageShare": "Deseja apagar este compartilhamento ({path})?",
@@ -165,8 +164,6 @@
"show": "Mostrar",
"size": "Tamanho",
"upload": "Enviar",
"uploadFiles": "Enviando {files} arquivos...",
"uploadMessage": "Selecione uma opção para enviar.",
"optionalPassword": "Senha opcional",
"deleteUserMessage": "Tem certeza de que deseja excluir esse usuário?",
"filesSelected": "arquivos selecionados",
@@ -178,7 +175,8 @@
"completed": "Concluído",
"conflictsDetected": "Conflitos detectados",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nome inválido fornecido: não pode conter \"/\" ou \"\\\""
},
"search": {
"images": "Imagens",
@@ -237,12 +235,7 @@
"passwordUpdated": "Senha atualizada!",
"path": "Caminho",
"permissions": {
"create": "Criar arquivos e diretórios",
"delete": "Apagar arquivos e diretórios",
"download": "Baixar",
"execute": "Executar comandos",
"modify": "Editar arquivos",
"rename": "Renomear ou mover arquivos e diretórios",
"share": "Compartilhar arquivos",
"admin": "Administrador",
"api": "Criar e gerenciar chaves de API de longa duração",
@@ -256,7 +249,6 @@
"rules": "Regras",
"rulesHelp": "Aqui você pode definir um conjunto de regras para permitir ou bloquear o acesso do usuário a determinados arquivos ou pastas. Os arquivos bloqueados não irão aparecer durante a navegação. Suportamos expressões regulares e os caminhos dos arquivos devem ser relativos à base do usuário.\n",
"scope": "Escopo",
"setDateFormat": "Definir formato exato de data",
"settingsUpdated": "Configurações atualizadas!",
"shareManagement": "Gerenciamento do compartilhamento",
"shareDeleted": "Compartilhamento apagado!",
@@ -279,9 +271,6 @@
"modifyOtherUser": "Modificar usuário:",
"modifyCurrentUser": "Modificar o usuário atual",
"themeColor": "Cor do tema",
"disableOfficePreviewDescription": "Uma lista de extensões de arquivo separada por espaços para desativar o visualizador do OnlyOffice. (por exemplo, '.txt .html').",
"disableOfficePreview": "Desativar o visualizador do onlyoffice para determinadas extensões de arquivo",
"filePreviewOptions": "Opções de visualização de arquivos",
"listingOptions": "Opções de listagem",
"emptyGlobal": "Atualmente, não há opções de configuração global. Você pode editar o yaml de configuração para ajustar as configurações do sistema.",
"scopes": "Definir fontes permitidas e o escopo do usuário dentro delas",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Desativar as visualizações prévias de arquivos do Office para determinadas extensões de arquivos",
"disablePreviewExtDescription": "Uma lista de extensões de arquivo separada por espaços para desativar as visualizações de arquivos do Office (por exemplo, '.docx .pptx').",
"disableOfficeEditorDescription": "Uma lista de extensões de arquivo separada por espaços para desativar o editor e o visualizador do OnlyOffice. (por exemplo, '.txt .html .pdf').",
"disableOfficeEditor": "Desativar o visualizador do Office para determinadas extensões de arquivo"
"disableOfficeEditor": "Desativar o visualizador do Office para determinadas extensões de arquivo",
"defaultThemeDescription": "default - O tema padrão",
"customTheme": "Escolha seu tema"
}
}
+5 -10
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Copiar",
"copyMessage": "Escolha um lugar para onde copiar os ficheiros:",
"currentlyNavigating": "A navegar em:",
"deleteMessageMultiple": "Quer eliminar {count} ficheiro(s)?",
"deleteMessageSingle": "Quer eliminar esta pasta/ficheiro?",
"deleteTitle": "Eliminar ficheiros",
@@ -164,12 +163,10 @@
"show": "Mostrar",
"size": "Tamanho",
"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!",
@@ -178,7 +175,8 @@
"completed": "Concluído",
"conflictsDetected": "Conflitos detectados",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nome inválido fornecido: não pode conter \"/\" ou \"\\\""
},
"search": {
"images": "Imagens",
@@ -234,12 +232,7 @@
"passwordUpdated": "Palavra-passe atualizada!",
"path": "Caminho",
"permissions": {
"create": "Criar ficheiros e pastas",
"delete": "Eliminar ficheiros e pastas",
"download": "Descarregar",
"execute": "Executar comandos",
"modify": "Editar ficheiros",
"rename": "Alterar o nome ou mover ficheiros e pastas",
"share": "Partilhar ficheiros",
"admin": "Administrador",
"api": "Criar e gerir chaves de API de longa duração",
@@ -442,6 +435,8 @@
"disablePreviewExt": "Desativar as pré-visualizações de ficheiros do Office para determinadas extensões de ficheiros",
"disablePreviewExtDescription": "Uma lista de extensões de ficheiros, separada por espaços, para desativar as pré-visualizações de ficheiros do Office (por exemplo, '.docx .pptx').",
"disableOfficeEditorDescription": "Uma lista de extensões de ficheiros separada por espaços para desativar o editor e visualizador do OnlyOffice. (por exemplo, '.txt .html .pdf').",
"disableOfficeEditor": "Desativar o visualizador do Office para determinadas extensões de ficheiros"
"disableOfficeEditor": "Desativar o visualizador do Office para determinadas extensões de ficheiros",
"defaultThemeDescription": "default - O tema predefinido",
"customTheme": "Escolha o seu tema"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Copiază",
"copyMessage": "Alege unde vrei să copiezi fișierele:",
"currentlyNavigating": "Navigare curentă în:",
"deleteMessageMultiple": "Ești sigur că vrei să ștergi aceste {count} fișier(e)?",
"deleteMessageSingle": "Ești sigur că vrei să ștergi acest fișier/director?",
"deleteTitle": "Șterge fișiere",
@@ -164,11 +163,9 @@
"show": "Arată",
"size": "Dimensiune",
"upload": "Încărcare",
"uploadMessage": "Selectați o opțiune pentru încărcare.",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"deleteUserMessage": "Sunteți sigur că doriți să ștergeți acest utilizator?",
"filesSelected": "fișiere selectate",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "Parolă opțională",
"typeName": "Tip:",
"selected": "selectat",
@@ -178,7 +175,8 @@
"completed": "Completat",
"conflictsDetected": "Conflicte detectate",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Nume invalid furnizat: nu poate conține \"/\" sau \"\\\""
},
"search": {
"images": "Imagini",
@@ -234,12 +232,7 @@
"passwordUpdated": "Parola actualizată!",
"path": "Calea",
"permissions": {
"create": "Crează fișiere și directoare",
"delete": "Șterge fișiere și directoare",
"download": "Descarcă",
"execute": "Execută comenzi",
"modify": "Modifică fișiere",
"rename": "Redenumește sau mută fișiere și directoare",
"share": "Distribuie fișiere",
"admin": "Administrator",
"api": "Crearea și gestionarea cheilor API cu durată lungă de viață",
@@ -274,9 +267,6 @@
"modifyOtherUser": "Modificați utilizatorul:",
"modifyCurrentUser": "Modificarea utilizatorului curent",
"themeColor": "Culoare temă",
"disableOfficePreviewDescription": "O listă de extensii de fișiere separate prin spațiu pentru care se dezactivează vizualizatorul OnlyOffice. (de exemplu, \".txt .html\").",
"disableOfficePreview": "Dezactivați vizualizatorul onlyoffice pentru anumite extensii de fișiere",
"filePreviewOptions": "Opțiuni de previzualizare a fișierelor",
"listingOptions": "Opțiuni de listare",
"emptyGlobal": "În prezent nu există opțiuni de configurare globală, Puteți edita yaml-ul de configurare pentru a ajusta setările sistemului.",
"scopes": "Definirea surselor permise și a domeniului de aplicare al utilizatorului în cadrul acestora",
@@ -289,7 +279,6 @@
"userScopeGenerationPlaceholder": "Domeniul de aplicare va fi generat automat",
"createUserHomeDirectory": "Crearea directorului home al utilizatorului",
"api": "Chei API",
"setDateFormat": "Setați formatul exact al datei",
"shareDeleted": "Share șters!",
"loginMethodDescription": "metoda de conectare permisă pentru utilizator.",
"userNotAdmin": "Utilizatorul nu este administrator",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Dezactivați previzualizarea fișierelor office pentru anumite extensii de fișiere",
"disablePreviewExtDescription": "O listă de extensii de fișiere separate prin spațiu pentru care se dezactivează previzualizările fișierelor office (de exemplu, '.docx .pptx').",
"disableOfficeEditorDescription": "O listă de extensii de fișiere separate prin spațiu pentru care se dezactivează editorul și vizualizatorul OnlyOffice. (de exemplu, \".txt .html .pdf\").",
"disableOfficeEditor": "Dezactivați vizualizatorul office pentru anumite extensii de fișiere"
"disableOfficeEditor": "Dezactivați vizualizatorul office pentru anumite extensii de fișiere",
"defaultThemeDescription": "default - Tema implicită",
"customTheme": "Alege-ți tema"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Копировать",
"copyMessage": "Копировать в:",
"currentlyNavigating": "Текущий каталог:",
"deleteMessageMultiple": "Удалить эти файлы ({count})?",
"deleteMessageSingle": "Удалить этот файл/каталог?",
"deleteMessageShare": "Удалить этот общий файл/каталог ({path})?",
@@ -165,11 +164,9 @@
"show": "Показать",
"size": "Размер",
"upload": "Загрузить",
"uploadMessage": "Выберите вариант для загрузки.",
"optionalPassword": "Необязательный пароль",
"deleteUserMessage": "Вы уверены, что хотите удалить этого пользователя?",
"filesSelected": "выбранные файлы",
"uploadFiles": "Uploading {files} files...",
"typeName": "Тип:",
"selected": "выбранные",
"deleted": "Удалено успешно!",
@@ -178,7 +175,8 @@
"completed": "Завершено",
"conflictsDetected": "Обнаруженные конфликты",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Неверное имя: не может содержать \"/\" или \"\\\"."
},
"search": {
"images": "Изображения",
@@ -234,12 +232,7 @@
"passwordUpdated": "Пароль обновлен!",
"path": "Путь",
"permissions": {
"create": "Создавать файлы и каталоги",
"delete": "Удалять файлы и каталоги",
"download": "Скачивать",
"execute": "Выполнять команды",
"modify": "Редактировать файлы",
"rename": "Переименовывать или перемещать файлы и каталоги",
"share": "Делиться файлами",
"admin": "Администратор",
"api": "Создание и управление долгоживущими ключами API",
@@ -253,7 +246,6 @@
"rules": "Права",
"rulesHelp": "Здесь вы можете определить набор разрешающих и запрещающих правил для этого конкретного пользователь. Блокированные файлы не будут отображаться в списках, и не будут доступны для пользователя. Есть поддержка регулярных выражений и относительных путей.\n",
"scope": "Корень",
"setDateFormat": "Установить точный формат даты",
"settingsUpdated": "Настройки применены!",
"shareManagement": "Управление расшаренными ссылками",
"shareDeleted": "Расшаренная ссылка удалена!",
@@ -276,9 +268,6 @@
"modifyOtherUser": "Измените пользователя:",
"modifyCurrentUser": "Изменить текущего пользователя",
"themeColor": "Цвет темы",
"disableOfficePreviewDescription": "Список расширений файлов, для которых необходимо отключить программу просмотра OnlyOffice, разделенный пробелами. (например, '.txt .html').",
"disableOfficePreview": "Отключите просмотрщик Onlyoffice для определенных расширений файлов",
"filePreviewOptions": "Параметры предварительного просмотра файлов",
"listingOptions": "Варианты листинга",
"emptyGlobal": "В настоящее время нет глобальных опций конфигурации, вы можете редактировать конфигурационный yaml для настройки параметров системы.",
"scopes": "Определите разрешенные источники и область действия пользователей в них",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Отключение предварительного просмотра офисных файлов для определенных расширений файлов",
"disablePreviewExtDescription": "Список расширений файлов, для которых нужно отключить предварительный просмотр офисных файлов (например, '.docx .pptx'), разделенный пробелами.",
"disableOfficeEditorDescription": "Список расширений файлов, для которых необходимо отключить редактор и просмотрщик OnlyOffice, разделенный пробелами. (например, '.txt .html .pdf').",
"disableOfficeEditor": "Отключение просмотра офисных файлов для определенных расширений файлов"
"disableOfficeEditor": "Отключение просмотра офисных файлов для определенных расширений файлов",
"defaultThemeDescription": "default - Тема по умолчанию",
"customTheme": "Выберите тему"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Kopírovať",
"copyMessage": "Zvoľte miesto, kde chcete kopírovať súbory:",
"currentlyNavigating": "Aktuálna cesta:",
"deleteMessageMultiple": "Naozaj chcete odstrániť {count} súbor(ov)?",
"deleteMessageSingle": "Naozaj chcete odstrániť tento súbor/priečinok?",
"deleteMessageShare": "Naozaj chcete odstrániť toto zdieľanie({path})?",
@@ -165,11 +164,9 @@
"show": "Zobraziť",
"size": "Veľkosť",
"upload": "Nahrať",
"uploadMessage": "Zvoľte možnosť nahrávania.",
"optionalPassword": "Voliteľné heslo",
"deleteUserMessage": "Ste si istí, že chcete odstrániť tohto používateľa?",
"filesSelected": "vybrané súbory",
"uploadFiles": "Uploading {files} files...",
"typeName": "Typ:",
"selected": "vybrané",
"deleted": "Úspešne vymazané!",
@@ -178,7 +175,8 @@
"completed": "Dokončené",
"conflictsDetected": "Zistené konflikty",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Zadaný neplatný názov: nemôže byť kontajner \"/\" alebo \"\\\""
},
"search": {
"images": "Obrázky",
@@ -234,12 +232,7 @@
"passwordUpdated": "Heslo zmenené!",
"path": "Cesta",
"permissions": {
"create": "Vytvárať súbory a priečinky",
"delete": "Odstraňovať súbory a priečinky",
"download": "Stiahnuť",
"execute": "Vykonávať príkazy",
"modify": "Upravovať súbory",
"rename": "Premenovať a presúvať súbory a priečinky",
"share": "Zdieľať súbory",
"admin": "Správca",
"api": "Vytváranie a správa kľúčov API s dlhou životnosťou",
@@ -275,9 +268,6 @@
"modifyOtherUser": "Upravte používateľa:",
"modifyCurrentUser": "Úprava aktuálneho používateľa",
"themeColor": "Farba témy",
"disableOfficePreviewDescription": "Medzerou oddelený zoznam prípon súborov, pre ktoré sa má prehliadač OnlyOffice vypnúť. (napr. \".txt .html\").",
"disableOfficePreview": "Zakázať prehliadač OnlyOffice pre určité prípony súborov",
"filePreviewOptions": "Možnosti náhľadu súborov",
"listingOptions": "Možnosti uvedenia v zozname",
"emptyGlobal": "V súčasnosti neexistujú žiadne globálne konfiguračné možnosti, Môžete upraviť konfiguračný yaml upraviť nastavenia systému.",
"scopes": "Definovanie povolených zdrojov a používateľského rozsahu v rámci nich",
@@ -290,7 +280,6 @@
"userScopeGenerationPlaceholder": "Rozsah sa vygeneruje automaticky",
"createUserHomeDirectory": "Vytvorenie domovského adresára používateľa",
"api": "Kľúče API",
"setDateFormat": "Nastavenie presného formátu dátumu",
"loginMethodDescription": "povolená metóda prihlásenia pre používateľa.",
"userNotAdmin": "Používateľ nie je správca",
"enterPassword": "Zadajte heslo",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Zakázanie náhľadov kancelárskych súborov pre určité prípony súborov",
"disablePreviewExtDescription": "Zoznam prípon súborov oddelených medzerou, pre ktoré sa majú vypnúť náhľady kancelárskych súborov (napr. \".docx .pptx\").",
"disableOfficeEditorDescription": "Medzerou oddelený zoznam prípon súborov, pre ktoré sa má vypnúť editor a prehliadač OnlyOffice. (napr. \".txt .html .pdf\").",
"disableOfficeEditor": "Zakázanie prehliadača Office pre určité prípony súborov"
"disableOfficeEditor": "Zakázanie prehliadača Office pre určité prípony súborov",
"defaultThemeDescription": "default - Predvolená téma",
"customTheme": "Vyberte si tému"
}
}
+5 -10
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Kopiera",
"copyMessage": "Välj var dina filer skall sparas:",
"currentlyNavigating": "För närvarande navigerar du på:",
"deleteMessageMultiple": "Är du säker på att du vill radera {count} filer(na)?",
"deleteMessageSingle": "Är du säker på att du vill radera denna fil/mapp",
"deleteTitle": "Ta bort filer",
@@ -164,12 +163,10 @@
"show": "Visa",
"size": "Storlek",
"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!",
@@ -178,7 +175,8 @@
"completed": "Färdigställd",
"conflictsDetected": "Konflikter upptäckta",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Ogiltigt namn anges: kan inte innehålla \"/\" eller \"\\\""
},
"search": {
"images": "Bilder",
@@ -234,12 +232,7 @@
"passwordUpdated": "Lösenord uppdaterat",
"path": "Väg",
"permissions": {
"create": "Skapa filer och mappar",
"delete": "Ta bort filer och mappar",
"download": "Ladda ner",
"execute": "Exekvera kommandon",
"modify": "Ändra fil",
"rename": "Byta namn på eller flytta filer och kataloger",
"share": "Dela filer",
"admin": "Administratör",
"api": "Skapa och hantera API-nycklar med lång livslängd",
@@ -442,6 +435,8 @@
"disablePreviewExt": "Avaktivera förhandsgranskning av Office-filer för vissa filtillägg",
"disablePreviewExtDescription": "En mellanslagsseparerad lista över filtillägg för vilka förhandsgranskningar av Office-filer ska inaktiveras (t.ex. '.docx .pptx').",
"disableOfficeEditorDescription": "En mellanslagsseparerad lista över filtillägg som OnlyOffice redigerare och visare ska inaktiveras för. (t.ex. '.txt .html .pdf').",
"disableOfficeEditor": "Avaktivera Office Viewer för vissa filtillägg"
"disableOfficeEditor": "Avaktivera Office Viewer för vissa filtillägg",
"defaultThemeDescription": "default - Standardtema",
"customTheme": "Välj ditt tema"
}
}
+5 -14
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Kopyala",
"copyMessage": "Dosyalarınızı kopyalayacağınız yeri seçin:",
"currentlyNavigating": "Şu anki lokasyon:",
"deleteMessageMultiple": "{count} dosyayı/dosyaları silmek istediğinizden emin misiniz?",
"deleteMessageSingle": "Bu dosyayı/klasörü silmek istediğinizden emin misiniz?",
"deleteMessageShare": "Bu paylaşımı({path}) silmek istediğinizden emin misiniz?",
@@ -165,11 +164,9 @@
"show": "Göster",
"size": "Boyut",
"upload": "Gönder",
"uploadMessage": "Yüklemek için bir seçenek belirleyin.",
"optionalPassword": "İsteğe bağlı şifre",
"deleteUserMessage": "Bu kullanıcıyı silmek istediğinizden emin misiniz?",
"filesSelected": "seçilen dosyalar",
"uploadFiles": "Uploading {files} files...",
"typeName": "Tip:",
"selected": "seçilmiş",
"deleted": "Başarıyla silindi!",
@@ -178,7 +175,8 @@
"completed": "Tamamlandı",
"conflictsDetected": "Tespit edilen çatışmalar",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Geçersiz ad sağlandı: \"/\" veya \"\\\" kapsayıcı olamaz"
},
"search": {
"images": "Görseller",
@@ -234,12 +232,7 @@
"passwordUpdated": "Şifre güncellendi",
"path": "Yol",
"permissions": {
"create": "Dosyalar ve dizinler oluşturun",
"delete": "Dosyalar ve dizinleri silin",
"download": "İndir",
"execute": "Komutları çalıştır",
"modify": "Dosyaları değiştir",
"rename": "Dosyaları ve dizinleri yeniden adlandırın veya taşıyın",
"share": "Dosyaları paylaş",
"admin": "Yönetici",
"api": "Uzun ömürlü API anahtarları oluşturma ve yönetme",
@@ -275,9 +268,6 @@
"modifyOtherUser": "Kullanıcıyı değiştir:",
"modifyCurrentUser": "Geçerli kullanıcıyı değiştir",
"themeColor": "Tema Rengi",
"disableOfficePreviewDescription": "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').",
"disableOfficePreview": "Belirli dosya uzantıları için onlyoffice görüntüleyiciyi devre dışı bırakma",
"filePreviewOptions": "Dosya önizleme seçenekleri",
"listingOptions": "Listeleme seçenekleri",
"emptyGlobal": "Şu anda global yapılandırma seçenekleri yoktur, sistem ayarlarını yapmak için yapılandırma yaml'ını düzenleyebilirsiniz.",
"scopes": "İzin verilen kaynakları ve bunlar içindeki kullanıcı kapsamını tanımlama",
@@ -290,7 +280,6 @@
"userScopeGenerationPlaceholder": "Kapsam otomatik olarak oluşturulacaktır",
"createUserHomeDirectory": "Kullanıcı ev dizini oluşturma",
"api": "API Anahtarları",
"setDateFormat": "Tam tarih biçimini ayarlama",
"loginMethodDescription": "kullanıcı için izin verilen oturum açma yöntemi.",
"userNotAdmin": "Kullanıcı yönetici değil",
"enterPassword": "Şifre girin",
@@ -446,6 +435,8 @@
"disablePreviewExt": "Belirli dosya uzantıları için ofis dosyası önizlemelerini devre dışı bırakma",
"disablePreviewExtDescription": "Ofis dosyası önizlemelerinin devre dışı bırakılacağı dosya uzantılarının boşluk bırakılarak ayrılmış listesi (örn. '.docx .pptx').",
"disableOfficeEditorDescription": "OnlyOffice düzenleyicisini ve görüntüleyicisini devre dışı bırakmak için dosya uzantılarının boşluk bırakılarak ayrılmış bir listesi. (örneğin, '.txt .html .pdf').",
"disableOfficeEditor": "Belirli dosya uzantıları için ofis görüntüleyiciyi devre dışı bırakma"
"disableOfficeEditor": "Belirli dosya uzantıları için ofis görüntüleyiciyi devre dışı bırakma",
"defaultThemeDescription": "default - Varsayılan tema",
"customTheme": "Temanızı seçin"
}
}
+5 -11
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "Копіювати",
"copyMessage": "Копіювати в:",
"currentlyNavigating": "Поточний каталог:",
"deleteMessageMultiple": "Видалити ці файли ({count})?",
"deleteMessageSingle": "Видалити цей файл/каталог?",
"deleteMessageShare": "Видалити цей спільний файл/каталог ({path})?",
@@ -165,12 +164,10 @@
"show": "Показати",
"size": "Розмір",
"upload": "Завантажити",
"uploadMessage": "Виберіть варіант для завантаження.",
"optionalPassword": "Необов'язковий пароль",
"deleteUserMessage": "Ви впевнені, що хочете видалити цього користувача?",
"selected": "вибрано",
"filesSelected": "вибрані файли",
"uploadFiles": "Uploading {files} files...",
"typeName": "Друкуй:",
"deleted": "Видалено успішно!",
"destinationSource": "Місце призначення Джерело:",
@@ -178,7 +175,8 @@
"completed": "Завершено",
"conflictsDetected": "Виявлені конфлікти",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "Вказано невірне ім'я: не можна використовувати символи \"/\" або \"\\\""
},
"search": {
"images": "Зображення",
@@ -234,12 +232,7 @@
"passwordUpdated": "Пароль оновлено!",
"path": "Шлях",
"permissions": {
"create": "Створювати файли та каталоги",
"delete": "Видаляти файли та каталоги",
"download": "Завантажувати",
"execute": "Виконувати команди",
"modify": "Редагувати файли",
"rename": "Перейменовувати або переміщувати файли та каталоги",
"share": "Ділітися файлами",
"admin": "Адміністратор",
"api": "Створюйте та керуйте довготривалими ключами API",
@@ -253,7 +246,6 @@
"rules": "Права",
"rulesHelp": "Тут ви можете визначити набір дозволяючих та забороняючих правил для цього конкретного користувача. Блоковані файли не відображатимуться у списках, і не будуть доступні для користувача. Є підтримка регулярних виразів та відносних шляхів.\n",
"scope": "Корінь",
"setDateFormat": "Встановити точний формат дати",
"settingsUpdated": "Налаштування застосовані!",
"shareManagement": "Управління спільними посиланнями",
"shareDeleted": "Спільне посилання видалено!",
@@ -443,6 +435,8 @@
"disablePreviewExt": "Вимкнути попередній перегляд офісних файлів для певних розширень",
"disablePreviewExtDescription": "Список розширень файлів через пробіл, для яких слід вимкнути попередній перегляд офісних файлів (наприклад, '.docx .pptx).",
"disableOfficeEditorDescription": "Список розділених пробілами розширень файлів, для яких слід вимкнути редактор і переглядач OnlyOffice. (наприклад, '.txt .html .pdf').",
"disableOfficeEditor": "Вимкнути офісний переглядач для певних розширень файлів"
"disableOfficeEditor": "Вимкнути офісний переглядач для певних розширень файлів",
"defaultThemeDescription": "default - тема за замовчуванням",
"customTheme": "Виберіть свою тему"
}
}
+5 -11
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "复制",
"copyMessage": "请选择欲复制至的目录:",
"currentlyNavigating": "当前目录:",
"deleteMessageMultiple": "你确定要删除这 {count} 个文件吗?",
"deleteMessageSingle": "你确定要删除这个文件/文件夹吗?",
"deleteMessageShare": "你确定要删除这个分享({path})吗?",
@@ -165,12 +164,10 @@
"show": "点击以显示",
"size": "大小",
"upload": "上传",
"uploadMessage": "选择上传选项。",
"optionalPassword": "密码(选填,不填即无密码)",
"deleteUserMessage": "您确定要删除该用户吗?",
"selected": "选定的",
"filesSelected": "所选文件",
"uploadFiles": "Uploading {files} files...",
"typeName": "类型",
"deleted": "已成功删除!",
"destinationSource": "目的地来源:",
@@ -178,7 +175,8 @@
"completed": "已完成",
"conflictsDetected": "检测到冲突",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "提供的名称无效:不能包含\"/\"或\"\\\"。"
},
"search": {
"images": "图像",
@@ -235,12 +233,7 @@
"passwordUpdated": "密码已更新!",
"path": "路径",
"permissions": {
"create": "创建文件和文件夹",
"delete": "删除文件和文件夹",
"download": "下载",
"execute": "执行命令",
"modify": "编辑",
"rename": "重命名或移动文件和文件夹",
"share": "分享文件",
"admin": "管理员",
"api": "创建和管理长效 API 密钥",
@@ -254,7 +247,6 @@
"rules": "规则",
"rulesHelp": "您可以为该用户制定一组黑名单或白名单式的规则,被屏蔽的文件将不会显示在列表中,用户也无权限访问,支持正则表达式和相对于用户范围的路径。\n",
"scope": "目录范围",
"setDateFormat": "显示精确的日期格式",
"settingsUpdated": "设置已更新!",
"shareManagement": "分享管理",
"shareDeleted": "分享已删除!",
@@ -443,6 +435,8 @@
"disablePreviewExt": "禁用某些文件扩展名的 Office 文件预览功能",
"disablePreviewExtDescription": "用空格分隔的文件扩展名列表,用于禁用办公文件预览(如\".docx .pptx\")。",
"disableOfficeEditorDescription": "用空格分隔的文件扩展名列表,用于禁用 OnlyOffice 编辑器和查看器。(例如,\".txt .html .pdf\")。",
"disableOfficeEditor": "禁用某些文件扩展名的 Office 查看器"
"disableOfficeEditor": "禁用某些文件扩展名的 Office 查看器",
"defaultThemeDescription": "default - 默认主题",
"customTheme": "选择主题"
}
}
+5 -10
View File
@@ -136,7 +136,6 @@
"prompts": {
"copy": "複製",
"copyMessage": "請選擇欲複製至的目錄:",
"currentlyNavigating": "目前目錄:",
"deleteMessageMultiple": "你確定要刪除這 {count} 個檔案嗎?",
"deleteMessageSingle": "你確定要刪除這個檔案/資料夾嗎?",
"deleteTitle": "刪除檔案",
@@ -164,7 +163,6 @@
"show": "顯示",
"size": "大小",
"upload": "上傳",
"uploadMessage": "選擇上傳項。",
"deleteMessageShare": "Are you sure you want to delete this share: {path} ?",
"destinationSource": "目的地來源:",
"deleteUserMessage": "您確定要刪除此使用者嗎?",
@@ -172,13 +170,13 @@
"completed": "已完成",
"selected": "選定",
"filesSelected": "選取的檔案",
"uploadFiles": "Uploading {files} files...",
"optionalPassword": "選購密碼",
"typeName": "類型:",
"deleted": "已成功刪除!",
"conflictsDetected": "偵測到衝突",
"uploadSettingsChunked": "Max concurrent uploads: {maxConcurrentUpload}, chunk size: {uploadChunkSizeMb} MB",
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled"
"uploadSettingsNoChunk": "Max concurrent uploads: {maxConcurrentUpload}, chunked upload disabled",
"invalidName": "提供的名稱無效:不能包含 \"/「 或 」\\\" 的容器"
},
"search": {
"images": "影像",
@@ -234,12 +232,7 @@
"passwordUpdated": "密碼已更新!",
"path": "路徑",
"permissions": {
"create": "建立檔案和資料夾",
"delete": "刪除檔案和資料夾",
"download": "下載",
"execute": "執行命令",
"modify": "編輯檔案",
"rename": "重命名或移動檔案/資料夾",
"share": "分享檔案",
"admin": "管理員",
"api": "建立與管理長效 API 金鑰",
@@ -416,7 +409,9 @@
"disablePreviewExt": "停用特定檔案副檔名的 Office 檔案預覽",
"disablePreviewExtDescription": "以空格分隔的檔案副檔名清單,以停用 Office 檔案預覽 (例如 '.docx .pptx')。",
"disableOfficeEditorDescription": "以空格分隔的檔案副檔名清單,用來停用 OnlyOffice 編輯器和檢視器。(例如,'.txt .html .pdf')。",
"disableOfficeEditor": "停用特定檔案副檔名的 Office 檢視器"
"disableOfficeEditor": "停用特定檔案副檔名的 Office 檢視器",
"defaultThemeDescription": "default - 預設主題",
"customTheme": "選擇您的主題"
},
"otp": {
"name": "雙因素驗證",
+10 -6
View File
@@ -213,7 +213,7 @@ export const getters = {
}
if (state.req.type == 'directory') {
listingView = 'listingView'
} else if (state.req.onlyOfficeId && !getters.fileViewingDisabled(state.req.name)) {
} else if (state.req.onlyOfficeId && !getters.officeViewingDisabled(state.req.name)) {
listingView = 'onlyOfficeEditor'
} else if (
'content' in state.req &&
@@ -336,11 +336,15 @@ export const getters = {
return true
}
}
if (state.req?.onlyOfficeId && state.user.disableOfficePreviewExt) {
const disabledExts = ' ' + state.user.disableOfficePreviewExt.toLowerCase()
if (disabledExts.includes(ext.toLowerCase())) {
return true
}
return false
},
officeViewingDisabled: filename => {
const ext = ' ' + getFileExtension(filename)
if (state.user.disableOfficePreviewExt) {
const disabledExts = ' ' + state.user.disableOfficePreviewExt.toLowerCase()
if (disabledExts.includes(ext.toLowerCase())) {
return true
}
}
return false
}
+2 -3
View File
@@ -85,7 +85,7 @@ const actions = {
context.commit("moveJob");
if (item.file.type == "directory") {
await filesApi.post(item.source, item.path).catch(Vue.prototype.$showError);
await filesApi.post(item.source, item.path);
} else {
let onUpload = throttle(
(event) =>
@@ -98,8 +98,7 @@ const actions = {
);
await filesApi
.post(item.source, item.path, item.file, item.overwrite, onUpload)
.catch(Vue.prototype.$showError);
.post(item.source, item.path, item.file, item.overwrite, onUpload);
}
context.dispatch("finishUpload", item);
+2
View File
@@ -22,6 +22,7 @@ const mediaAvailable = window.FileBrowser.MediaAvailable;
const muPdfAvailable = window.FileBrowser.MuPdfAvailable;
const updateAvailable = window.FileBrowser.UpdateAvailable;
const disableNavButtons = window.FileBrowser.DisableNavButtons;
const userSelectableThemes = window.FileBrowser.UserSelectableThemes;
const origin = window.location.origin;
const settings = [
@@ -34,6 +35,7 @@ const settings = [
];
export {
userSelectableThemes,
disableNavButtons,
updateAvailable,
muPdfAvailable,
+95 -61
View File
@@ -12,6 +12,8 @@ class UploadManager {
this.isOverallPaused = false;
this.onConflict = () => {}; // Callback for UI
this.hadActiveUploads = false; // Track if we've had active uploads
this.conflictingFolder = null; // Track the folder name that caused conflict
this.pendingItems = null; // Store pending items during conflict resolution
}
setOnConflict(handler) {
@@ -19,10 +21,50 @@ class UploadManager {
}
async add(basePath, items, overwrite = false) {
if (basePath.slice(-1) !== "/") {
basePath += "/";
}
// Pre-upload conflict check for top-level directories
if (this.overwriteAll === null && !overwrite) {
const topLevelDirs = new Set();
for (const item of items) {
if (item.relativePath && item.relativePath.includes('/')) {
topLevelDirs.add(item.relativePath.split('/')[0]);
}
}
if (topLevelDirs.size > 0) {
const existingItems = new Set(state.req.items.map(i => i.name));
const conflictingDirs = [...topLevelDirs].filter(dir => existingItems.has(dir));
if (conflictingDirs.length > 0) {
// Store the conflicting folder name (take the first one for now)
this.conflictingFolder = conflictingDirs[0];
this.pendingItems = items;
this.onConflict(resolution => {
if (resolution === true) {
// User chose overwrite
this.overwriteAll = true;
this.add(basePath, items, true);
} else if (resolution && resolution.rename) {
// User chose rename - continue with renamed items
this.conflictingFolder = null;
this.add(basePath, this.pendingItems, false);
} else {
// User cancelled
this.overwriteAll = null;
this.conflictingFolder = null;
this.pendingItems = null;
}
});
return;
}
}
}
const effectiveOverwrite = this.overwriteAll || overwrite;
const dirs = new Set();
for (const item of items) {
if (item.relativePath) {
@@ -54,9 +96,10 @@ class UploadManager {
progress: 0,
status: "pending",
type: "directory",
isToplevelDir: pathParts.length === 1,
path: `${basePath}${dir}`,
source: state.req.source,
overwrite: overwrite,
overwrite: effectiveOverwrite,
};
newUploads.push(upload);
@@ -79,13 +122,17 @@ class UploadManager {
xhr: null,
path: destinationPath, // Full destination path
source: state.req.source,
overwrite: overwrite,
overwrite: effectiveOverwrite,
};
return upload;
});
this.queue.push(...newUploads, ...fileUploads);
// Clean up pending items after successful add
this.pendingItems = null;
this.conflictingFolder = null;
this.processQueue();
return newUploads;
}
@@ -118,6 +165,7 @@ class UploadManager {
console.log("all uploads processed ", this.queue);
mutations.setReload(true);
this.hadActiveUploads = false; // Reset the flag
this.overwriteAll = null; // Reset for next batch of uploads
}
}
@@ -144,13 +192,12 @@ class UploadManager {
upload.status = "uploading";
try {
const { promise } = filesApi.post(
await filesApi.post(
upload.source,
upload.path,
new Blob([]),
upload.overwrite
);
await promise;
upload.status = "completed";
upload.progress = 100;
@@ -174,7 +221,7 @@ class UploadManager {
};
try {
const { xhr, promise } = filesApi.post(
const promise = filesApi.post(
upload.source,
upload.path,
upload.file,
@@ -185,7 +232,7 @@ class UploadManager {
}
);
upload.xhr = xhr;
upload.xhr = promise.xhr;
await promise;
upload.status = "completed";
@@ -214,7 +261,7 @@ class UploadManager {
};
try {
const { xhr, promise } = filesApi.post(
const promise = filesApi.post(
upload.source,
upload.path,
chunk,
@@ -226,7 +273,7 @@ class UploadManager {
}
);
upload.xhr = xhr;
upload.xhr = promise.xhr;
await promise;
upload.chunkOffset += chunk.size;
@@ -322,6 +369,36 @@ class UploadManager {
return this.queue.some((item) => item.status === "pending");
}
getConflictingFolder() {
return this.conflictingFolder;
}
async renameFolder(oldName, newName) {
if (!oldName || !newName || !this.pendingItems) {
throw new Error("Invalid parameters for folder rename");
}
// Update all items in the pending items that reference the old folder name
const updatedItems = this.pendingItems.map(item => {
if (item.relativePath) {
const pathParts = item.relativePath.split("/");
if (pathParts[0] === oldName) {
// Replace the first part of the path with the new name
pathParts[0] = newName;
return {
...item,
relativePath: pathParts.join("/")
};
}
}
return item;
});
// Store the updated items for processing
this.pendingItems = updatedItems;
return true;
}
async handleUploadError(upload, err) {
// Check if the error is a 409 Conflict
if (err?.response?.status === 409) {
@@ -332,63 +409,20 @@ class UploadManager {
console.log(`upload.js: Upload aborted for id ${upload.id}`, upload);
}
}
resolveConflict(overwrite) {
this.overwriteAll = overwrite;
this.isPausedForConflict = false;
if (overwrite) {
// Find all items that hit a conflict and requeue them.
for (const item of this.queue) {
if (item.status === "conflict") {
item.status = "pending";
item.overwrite = true;
if (item.type !== 'directory') {
item.chunkOffset = 0; // Reset progress for resume
}
}
}
} else {
// Cancel all uploads in the queue.
for (let i = this.queue.length - 1; i >= 0; i--) {
this.cancel(this.queue[i].id)
}
}
this.processQueue();
}
}
export const uploadManager = new UploadManager();
export function checkConflict(files, items) {
if (typeof items === 'undefined' || items === null) {
items = [];
// Check for conflicts between items to be uploaded/moved and existing items
export function checkConflict(newItems, existingItems) {
if (!newItems || !existingItems) {
return false;
}
let folder_upload = files[0].path !== undefined;
const existingNames = new Set(existingItems.map(item => item.name));
let conflict = false;
for (let i = 0; i < files.length; i++) {
let file = files[i];
let name = file.name;
if (folder_upload) {
let dirs = file.path.split('/');
if (dirs.length > 1) {
name = dirs[0];
}
}
let res = items.findIndex(function hasConflict(element) {
return element.name === this;
}, name);
if (res >= 0) {
conflict = true;
break;
}
}
return conflict;
return newItems.some(item => {
const itemName = item.name || item.file?.name;
return itemName && existingNames.has(itemName);
});
}
+25 -1
View File
@@ -109,6 +109,12 @@ export default {
behavior: "instant",
block: "center",
});
// Add glow effect
element.classList.add('scroll-glow');
// Remove glow effect after animation completes
setTimeout(() => {
element.classList.remove('scroll-glow');
}, 1000);
}
}
},
@@ -153,7 +159,7 @@ export default {
// Fetch initial data
let res = await filesApi.fetchFiles(fetchSource, fetchPath );
// If not a directory, fetch content
if (res.type != "directory") {
if (res.type != "directory" && !res.type.startsWith("image")) {
const content = !getters.fileViewingDisabled(res.name);
res = await filesApi.fetchFiles(res.source, res.path, content);
}
@@ -185,3 +191,21 @@ export default {
},
};
</script>
<style>
.scroll-glow {
animation: scrollGlowAnimation 1s ease-out;
}
@keyframes scrollGlowAnimation {
0% {
color: inherit;
}
50% {
color: var(--primaryColor);
}
100% {
color: inherit;
}
}
</style>
+1 -1
View File
@@ -8,7 +8,7 @@
<h3>{{ loginName }}</h3>
</div>
<div v-if="passwordAvailable" class="password-entry">
<div v-if="error !== ''" class="wrong-login">{{ error }}</div>
<div v-if="error !== ''" class="wrong-login card">{{ error }}</div>
<input
autofocus
class="input input--block"
+2 -3
View File
@@ -50,7 +50,7 @@
class="file-icons"
>
<div>
<div class="header" :class="{ 'dark-mode-item-header': isDarkMode }">
<div class="header card" :class="{ 'dark-mode-item-header': isDarkMode }">
<p
:class="{ active: nameSorted }"
class="name"
@@ -818,7 +818,7 @@ export default {
if (state.user.viewMode == "gallery") {
document.documentElement.style.setProperty(
"--item-height",
`calc(${this.columnWidth / 25}em)`
`calc(${this.columnWidth / 20}em)`
);
} else {
document.documentElement.style.setProperty("--item-height", `auto`);
@@ -959,7 +959,6 @@ export default {
#listingView.dropping {
transform: scale(0.97);
border-radius: 1em;
box-shadow: var(--primaryColor) 0 0 1em;
}
+3 -1
View File
@@ -1,7 +1,9 @@
<template>
<div id="previewer" @mousemove="toggleNavigation" @touchstart="toggleNavigation">
<div class="preview" v-if="!isDeleted">
<ExtendedImage v-if="previewType == 'image' || pdfConvertable" :src="raw">
<ExtendedImage v-if="previewType == 'image' || pdfConvertable" :src="raw"
@navigate-previous="prev"
@navigate-next="next">
</ExtendedImage>
<audio v-else-if="previewType == 'audio'" ref="player" :src="raw" controls :autoplay="autoPlay"
@play="autoPlay = true"></audio>
+34 -1
View File
@@ -211,6 +211,22 @@
@button-clicked="setColor"
:initialActive="localuser.themeColor"
/>
<h3 v-if="Object.keys(availableThemes).length > 0">{{ $t("profileSettings.customTheme") }}</h3>
<div v-if="Object.keys(availableThemes).length > 0" class="form-group">
<select
class="input input--block"
v-model="selectedTheme"
@change="updateCustomTheme"
>
<option
v-for="(theme, key) in availableThemes"
:key="key"
:value="key"
>
{{ key === 'default' ? $t('profileSettings.defaultThemeDescription') : `${key} - ${theme.description}` }}
</option>
</select>
</div>
<h3>{{ $t("settings.language") }}</h3>
<Languages
class="input input--block"
@@ -225,7 +241,7 @@
<script>
import { notify } from "@/notify";
import { mediaAvailable, muPdfAvailable, onlyOfficeUrl } from "@/utils/constants.js";
import { mediaAvailable, muPdfAvailable, onlyOfficeUrl, userSelectableThemes } from "@/utils/constants.js";
import { state, mutations } from "@/store";
import { usersApi } from "@/api";
import Languages from "@/components/settings/Languages.vue";
@@ -268,6 +284,9 @@ export default {
},
},
computed: {
availableThemes() {
return userSelectableThemes || {};
},
onlyOfficeAvailable() {
return onlyOfficeUrl !== "";
},
@@ -286,6 +305,14 @@ export default {
active() {
return state.activeSettingsView === "profile-main";
},
selectedTheme: {
get() {
return this.localuser.customTheme || "default";
},
set(value) {
this.localuser.customTheme = value;
}
},
},
mounted() {
this.localuser = { ...state.user };
@@ -347,12 +374,14 @@ export default {
}
try {
const data = this.localuser;
const themeChanged = state.user.customTheme != this.localuser.customTheme;
mutations.updateCurrentUser(data);
await usersApi.update(data, [
"locale",
"showHidden",
"dateFormat",
"themeColor",
"customTheme",
"quickDownload",
"disablePreviewExt",
"disableViewingExt",
@@ -364,7 +393,11 @@ export default {
"hideSidebarFileActions",
"editorQuickSave",
]);
if (themeChanged) {
window.location.reload();
}
notify.showSuccess(this.$t("settings.settingsUpdated"));
} catch (e) {
notify.showError(e);
}
@@ -4,7 +4,6 @@ test("redirect to login from root", async ({ page, context }) => {
await context.clearCookies();
await page.goto("/");
await expect(page).toHaveURL(/\/login/);
});
test("redirect to login from files", async ({ page, context }) => {
@@ -26,3 +26,35 @@ test('create and delete testuser', async ({
await page.goto(editLink!)
checkForErrors()
})
test("two factor auth check", async ({ page, checkForErrors, context }) => {
// go to settings
await page.goto("/settings");
await expect(page).toHaveURL(/\/settings/);
// click the edit button for testuser
const userRow = page.locator('tr.item', { hasText: 'admin' })
const editLink = await userRow
.locator('td[aria-label="Edit User"] a')
.getAttribute('href')
await page.goto(editLink!)
// Toggle the two factor authentication switch
const twoFactorCheckbox = page.locator('.toggle-container:has-text("Two-Factor Authentication") input[type="checkbox"]');
const twoFactorToggle = page.locator('.toggle-container:has-text("Two-Factor Authentication") label.switch');
// Check if it's currently enabled
const isChecked = await twoFactorCheckbox.isChecked();
// Toggle it by clicking the label (since checkbox is hidden)
await twoFactorToggle.click();
// Verify it changed state
await expect(twoFactorCheckbox).toBeChecked();
await page.locator('button[aria-label="Generate Code"]').click();
// check for the otp url
await expect(page.locator('p[aria-label="otp-url"]')).toBeVisible();
// check that the otp-url is not empty
const otpUrl = await page.locator('p[aria-label="otp-url"]').textContent();
expect(otpUrl).not.toBe("");
checkForErrors();
});
View File
Whitespace-only changes.