Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
785c93c52a | ||
|
|
51cd55fe8e | ||
|
|
794e23762d | ||
|
|
7d55dd90a1 | ||
|
|
1ab544cea3 | ||
|
|
587db41837 | ||
|
|
72c983bd56 | ||
|
|
3efadf1809 | ||
|
|
1ee18cc1ec | ||
|
|
6e9c77cab4 | ||
|
|
0996ecffb5 | ||
|
|
8ba3570740 | ||
|
|
86e5fce385 | ||
|
|
c72bd93cc6 | ||
|
|
89d3dfc901 | ||
|
|
b21810308f | ||
|
|
d5edac2415 | ||
|
|
28be159067 | ||
|
|
f95fa49cf6 |
No files matched your search
@@ -3,6 +3,7 @@ name: PR Request
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "dev/v*.*.*"
|
||||
- "beta/v*.*.*"
|
||||
- "stable/v*.*.*"
|
||||
|
||||
@@ -48,3 +48,4 @@ yarn-error.log*
|
||||
bin/
|
||||
build/
|
||||
*__debug*
|
||||
backend/custom.css
|
||||
@@ -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
@@ -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" ]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
@@ -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")
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Whitespace-only changes.
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"] {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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": "أحمر",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": "Επιλέξτε το θέμα σας"
|
||||
}
|
||||
}
|
||||
@@ -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.",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": "בחר את הנושא שלך"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": "テーマを選ぶ"
|
||||
}
|
||||
}
|
||||
@@ -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": "테마 선택"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": "Выберите тему"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": "Виберіть свою тему"
|
||||
}
|
||||
}
|
||||
@@ -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": "选择主题"
|
||||
}
|
||||
}
|
||||
@@ -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": "雙因素驗證",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
-1
@@ -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();
|
||||
});
|
||||
Reference in new issue
Block a user