Browse Source

WebClient: various UI/UX improvements

Signed-off-by: Nicola Murino <[email protected]>
Nicola Murino 1 year ago
parent
commit
a9341d7c0f

+ 4 - 4
go.mod

@@ -16,7 +16,7 @@ require (
 	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.9
 	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.9
 	github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.5
 	github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.5
 	github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7
 	github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7
-	github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.6
+	github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.0
 	github.com/aws/aws-sdk-go-v2/service/sts v1.26.6
 	github.com/aws/aws-sdk-go-v2/service/sts v1.26.6
 	github.com/bmatcuk/doublestar/v4 v4.6.1
 	github.com/bmatcuk/doublestar/v4 v4.6.1
 	github.com/cockroachdb/cockroach-go/v2 v2.3.5
 	github.com/cockroachdb/cockroach-go/v2 v2.3.5
@@ -109,7 +109,7 @@ require (
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.1 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.1 // indirect
-	github.com/go-logr/logr v1.4.0 // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-ole/go-ole v1.3.0 // indirect
 	github.com/go-ole/go-ole v1.3.0 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
@@ -165,7 +165,7 @@ require (
 	go.opentelemetry.io/otel/metric v1.21.0 // indirect
 	go.opentelemetry.io/otel/metric v1.21.0 // indirect
 	go.opentelemetry.io/otel/trace v1.21.0 // indirect
 	go.opentelemetry.io/otel/trace v1.21.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
+	golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect
 	golang.org/x/mod v0.14.0 // indirect
 	golang.org/x/mod v0.14.0 // indirect
 	golang.org/x/sync v0.5.0 // indirect
 	golang.org/x/sync v0.5.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
@@ -176,7 +176,7 @@ require (
 	google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect
 	google.golang.org/grpc v1.60.1 // indirect
 	google.golang.org/grpc v1.60.1 // indirect
-	google.golang.org/protobuf v1.31.0 // indirect
+	google.golang.org/protobuf v1.32.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 )

+ 8 - 8
go.sum

@@ -65,8 +65,8 @@ github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.5 h1:Fp3Gcbp3lAJA
 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.5/go.mod h1:XABJbVXMa0xnVqaGbhkfUeVV0GrPsc3Jqscu87IovXU=
 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.5/go.mod h1:XABJbVXMa0xnVqaGbhkfUeVV0GrPsc3Jqscu87IovXU=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7 h1:o0ASbVwUAIrfp/WcCac+6jioZt4Hd8k/1X8u7GJ/QeM=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7 h1:o0ASbVwUAIrfp/WcCac+6jioZt4Hd8k/1X8u7GJ/QeM=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.47.7/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA=
-github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.6 h1:L9Cu6ejuozkr5ipYnaXuRBZoyaFIIXZiurN4gUrQL+U=
-github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.6/go.mod h1:4Ae1NCLK6ghmjzd45Tc33GgCKhUWD2ORAlULtMO1Cbs=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.0 h1:dPCRgAL4WD9tSMaDglRNGOiAtSTjkwNiUW5GDpWFfHA=
+github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.0/go.mod h1:4Ae1NCLK6ghmjzd45Tc33GgCKhUWD2ORAlULtMO1Cbs=
 github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
 github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
 github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
 github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
@@ -154,8 +154,8 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
 github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
 github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
 github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.0 h1:wx+BduGRXjIL6VPeeb7DRX+ii7sR/ch8DlRifHR589o=
-github.com/go-logr/logr v1.4.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@@ -432,8 +432,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
 gocloud.dev v0.35.0 h1:x/Gtt5OJdT4j+ir1AXAIXb7bBnFawXAAaJptCUGk3HU=
 gocloud.dev v0.35.0 h1:x/Gtt5OJdT4j+ir1AXAIXb7bBnFawXAAaJptCUGk3HU=
 gocloud.dev v0.35.0/go.mod h1:wbyF+BhfdtLWyUtVEWRW13hFLb1vXnV2ovEhYGQe3ck=
 gocloud.dev v0.35.0/go.mod h1:wbyF+BhfdtLWyUtVEWRW13hFLb1vXnV2ovEhYGQe3ck=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
-golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
+golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4=
+golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -558,8 +558,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
+google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

File diff suppressed because it is too large
+ 0 - 0
static/assets/css/style.bundle.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/plugins/global/plugins.bundle.css


+ 26 - 11
static/locales/en/translation.json

@@ -53,7 +53,8 @@
         "reset_pwd_err_generic": "Unexpected error while resetting password",
         "reset_pwd_err_generic": "Unexpected error while resetting password",
         "reset_ok_login_error": "The password reset completed successfully but an unexpected error occurred while signing in",
         "reset_ok_login_error": "The password reset completed successfully but an unexpected error occurred while signing in",
         "ip_not_allowed": "Login is not allowed from this IP address",
         "ip_not_allowed": "Login is not allowed from this IP address",
-        "two_factor_required": "Set up two-factor authentication, it is required for the following protocols: {{val}}"
+        "two_factor_required": "Set up two-factor authentication, it is required for the following protocols: {{val}}",
+        "link": "Go to {{link}}"
     },
     },
     "theme": {
     "theme": {
         "light": "Light",
         "light": "Light",
@@ -72,7 +73,7 @@
         "error_edit_dir": "Cannot edit a directory",
         "error_edit_dir": "Cannot edit a directory",
         "error_edit_size": "The file size exceeds the maximum allowed size",
         "error_edit_size": "The file size exceeds the maximum allowed size",
         "invalid_form": "Invalid form",
         "invalid_form": "Invalid form",
-        "invalid_credentials": "Invalid credentials",
+        "invalid_credentials": "Invalid credentials, please retry",
         "invalid_csrf": "The form token is not valid",
         "invalid_csrf": "The form token is not valid",
         "invalid_token": "Invalid permissions",
         "invalid_token": "Invalid permissions",
         "confirm_logout": "Are you sure you want to sign out?",
         "confirm_logout": "Are you sure you want to sign out?",
@@ -80,7 +81,7 @@
         "or": "or",
         "or": "or",
         "ok": "OK",
         "ok": "OK",
         "none": "None",
         "none": "None",
-        "cancel": "Cancel",
+        "cancel": "No, back",
         "submit": "Save",
         "submit": "Save",
         "back": "Back",
         "back": "Back",
         "add": "Add",
         "add": "Add",
@@ -91,9 +92,10 @@
         "configuration": "Configuration",
         "configuration": "Configuration",
         "config_saved": "Configuration saved",
         "config_saved": "Configuration saved",
         "rename": "Rename",
         "rename": "Rename",
-        "confirm": "Confirm",
+        "confirm": "Yes, proceed",
         "edit": "Edit",
         "edit": "Edit",
         "delete": "Delete",
         "delete": "Delete",
+        "delete_confirm_btn": "Yes, delete",
         "delete_confirm": "Do you want to delete \"{{- name}}\"? This action is irreversible",
         "delete_confirm": "Do you want to delete \"{{- name}}\"? This action is irreversible",
         "delete_confirm_generic": "Do you want to delete the selected item? This action is irreversible",
         "delete_confirm_generic": "Do you want to delete the selected item? This action is irreversible",
         "delete_multi_confirm": "Do you want to delete the selected items? This action is irreversible",
         "delete_multi_confirm": "Do you want to delete the selected items? This action is irreversible",
@@ -124,8 +126,6 @@
         "pub_key_placeholder": "Paste your public key here",
         "pub_key_placeholder": "Paste your public key here",
         "verify": "Verify",
         "verify": "Verify",
         "problems": "Having problems?",
         "problems": "Having problems?",
-        "generate": "Generate",
-        "view": "View",
         "allowed_ip_mask": "Allowed IP/Mask",
         "allowed_ip_mask": "Allowed IP/Mask",
         "allowed_ip_mask_help": "Comma separated IP/Mask in CIDR format, for example \"192.168.1.0/24,10.8.0.100/32\"",
         "allowed_ip_mask_help": "Comma separated IP/Mask in CIDR format, for example \"192.168.1.0/24,10.8.0.100/32\"",
         "allowed_ip_mask_invalid": "Invalid allowed IP/Mask",
         "allowed_ip_mask_invalid": "Invalid allowed IP/Mask",
@@ -144,7 +144,9 @@
         "err_quota_read": "Read denied due to quota limit",
         "err_quota_read": "Read denied due to quota limit",
         "profile_updated": "Your profile has been successfully updated",
         "profile_updated": "Your profile has been successfully updated",
         "share_ok": "Share access successful, you can now use your link",
         "share_ok": "Share access successful, you can now use your link",
-        "qr_code": "QR Code"
+        "qr_code": "QR Code",
+        "copy_link": "Copy link",
+        "copied": "Copied"
     },
     },
     "fs": {
     "fs": {
         "view_file": "View file \"{{- path}}\"",
         "view_file": "View file \"{{- path}}\"",
@@ -212,6 +214,7 @@
             "err_429": "$t(fs.move.err_generic). $t(fs.err_429)"
             "err_429": "$t(fs.move.err_generic). $t(fs.err_429)"
         },
         },
         "rename": {
         "rename": {
+            "title": "Rename \"{{- name}}\"",
             "new_name": "New name",
             "new_name": "New name",
             "err_generic": "Unable to rename \"{{- name}}\"",
             "err_generic": "Unable to rename \"{{- name}}\"",
             "err_403": "$t(fs.rename.err_generic). $t(fs.err_403)",
             "err_403": "$t(fs.rename.err_generic). $t(fs.err_403)",
@@ -260,7 +263,7 @@
         "msg_disabled": "Secure Your Account",
         "msg_disabled": "Secure Your Account",
         "msg_info": "Two-factor authentication adds an extra layer of security to your account. To log in you'll need to provide an additional authentication code.",
         "msg_info": "Two-factor authentication adds an extra layer of security to your account. To log in you'll need to provide an additional authentication code.",
         "require_for": "Require 2FA for",
         "require_for": "Require 2FA for",
-        "generate": "Generate new secret",
+        "generate": "Generate new secret key",
         "recovery_codes": "Recovery codes",
         "recovery_codes": "Recovery codes",
         "new_recovery_codes": "New recovery codes",
         "new_recovery_codes": "New recovery codes",
         "recovery_codes_msg1": "Recovery codes are a set of one time use codes that can be used in place of the authentication code to login to the web UI. You can use them if you lose access to your phone to login to your account and disable or regenerate two-factor auth configuration.",
         "recovery_codes_msg1": "Recovery codes are a set of one time use codes that can be used in place of the authentication code to login to the web UI. You can use them if you lose access to your phone to login to your account and disable or regenerate two-factor auth configuration.",
@@ -275,7 +278,7 @@
         "setup_msg": "Use your preferred Authenticator App (e.g. Microsoft Authenticator, Google Authenticator, Authy, 1Password etc. ) to scan the QR code. It will generate an authentication code for you to enter below.",
         "setup_msg": "Use your preferred Authenticator App (e.g. Microsoft Authenticator, Google Authenticator, Authy, 1Password etc. ) to scan the QR code. It will generate an authentication code for you to enter below.",
         "setup_help": "If you have trouble using the QR code, select manual entry on your app, and enter the code:",
         "setup_help": "If you have trouble using the QR code, select manual entry on your app, and enter the code:",
         "disable_question": "Do you want to disable two-factor authentication?",
         "disable_question": "Do you want to disable two-factor authentication?",
-        "generate_question": "Do you want to generate a new secret and invalidate the previous one? Any registered Authenticator App will stop working",
+        "generate_question": "Do you want to generate a new secret key and invalidate the previous one? Any registered Authenticator App will stop working",
         "disabled": "Two-factor authentication is disabled",
         "disabled": "Two-factor authentication is disabled",
         "recovery_codes_gen_err": "Failed to generate new recovery codes",
         "recovery_codes_gen_err": "Failed to generate new recovery codes",
         "recovery_codes_get_err": "Unable to obtain recovery codes",
         "recovery_codes_get_err": "Unable to obtain recovery codes",
@@ -284,7 +287,9 @@
         "save_err": "Failed to save configuration",
         "save_err": "Failed to save configuration",
         "auth_code_required": "The authentication code is required",
         "auth_code_required": "The authentication code is required",
         "no_protocol": "Please select at least a protocol",
         "no_protocol": "Please select at least a protocol",
-        "required_protocols": "The following protocols are required: {{val}}"
+        "required_protocols": "The security policy configured for your account requires two-factor authentication for the following protocols: {{val}}",
+        "recovery_codes_generate": "Generate new recovery codes",
+        "recovery_codes_view": "View recovery codes"
     },
     },
     "share": {
     "share": {
         "scope": "Scope",
         "scope": "Scope",
@@ -319,7 +324,17 @@
         "usage_exceed": "Maximum sharing usage exceeded",
         "usage_exceed": "Maximum sharing usage exceeded",
         "expired": "Sharing has expired",
         "expired": "Sharing has expired",
         "browsable_multiple_paths": "A share with multiple paths is not browsable",
         "browsable_multiple_paths": "A share with multiple paths is not browsable",
-        "browsable_non_dir": "The share is not a directory so it is not browsable"
+        "browsable_non_dir": "The share is not a directory so it is not browsable",
+        "go": "Go to share",
+        "access_links_title": "Share access links",
+        "link_single_title": "Single zip file",
+        "link_single_desc": "You can download the shared content as a single zip file",
+        "link_dir_title": "Single directory",
+        "link_dir_desc": "If the share consists of a single directory, you can browse and download files",
+        "link_uncompressed_title": "Uncompressed file",
+        "link_uncompressed_desc": "If the share consists of a single file, it can also be downloaded uncompressed",
+        "upload_desc": "You can upload one or more files to the shared directory",
+        "expired_desc": "This share is no longer accessible because it has expired"
     },
     },
     "select2": {
     "select2": {
         "no_results": "No results found",
         "no_results": "No results found",

+ 26 - 11
static/locales/it/translation.json

@@ -53,7 +53,8 @@
         "reset_pwd_err_generic": "Errore imprevisto durante la reimpostazione della password",
         "reset_pwd_err_generic": "Errore imprevisto durante la reimpostazione della password",
         "reset_ok_login_error": "La reimpostazione della password è stata completata correttamente ma si è verificato un errore imprevisto durante l'accesso",
         "reset_ok_login_error": "La reimpostazione della password è stata completata correttamente ma si è verificato un errore imprevisto durante l'accesso",
         "ip_not_allowed": "L'accesso non è consentito da questo indirizzo IP",
         "ip_not_allowed": "L'accesso non è consentito da questo indirizzo IP",
-        "two_factor_required": "Configura l'autenticazione a due fattori, è obbligatoria per i seguenti protocolli: {{val}}"
+        "two_factor_required": "Configura l'autenticazione a due fattori, è obbligatoria per i seguenti protocolli: {{val}}",
+        "link": "Vai a {{link}}"
     },
     },
     "theme": {
     "theme": {
         "light": "Chiaro",
         "light": "Chiaro",
@@ -72,7 +73,7 @@
         "error_edit_dir": "Impossibile modificare una cartella",
         "error_edit_dir": "Impossibile modificare una cartella",
         "error_edit_size": "La dimensione del file supera la dimensione massima consentita",
         "error_edit_size": "La dimensione del file supera la dimensione massima consentita",
         "invalid_form": "Modulo non valido",
         "invalid_form": "Modulo non valido",
-        "invalid_credentials": "Credenziali non valide",
+        "invalid_credentials": "Credenziali non valide, riprovare",
         "invalid_csrf": "Il token del modulo non è valido",
         "invalid_csrf": "Il token del modulo non è valido",
         "invalid_token": "Permessi non validi",
         "invalid_token": "Permessi non validi",
         "confirm_logout": "Sei sicuro di volerti disconnettere?",
         "confirm_logout": "Sei sicuro di volerti disconnettere?",
@@ -80,7 +81,7 @@
         "or": "o",
         "or": "o",
         "ok": "OK",
         "ok": "OK",
         "none": "Nessuna",
         "none": "Nessuna",
-        "cancel": "Annulla",
+        "cancel": "No, indietro",
         "submit": "Salva",
         "submit": "Salva",
         "back": "Indietro",
         "back": "Indietro",
         "add": "Aggiungi",
         "add": "Aggiungi",
@@ -91,9 +92,10 @@
         "configuration": "Configurazione",
         "configuration": "Configurazione",
         "config_saved": "Configurazione salvata",
         "config_saved": "Configurazione salvata",
         "rename": "Rinomina",
         "rename": "Rinomina",
-        "confirm": "Conferma",
+        "confirm": "Si, procedi",
         "edit": "Modifica",
         "edit": "Modifica",
         "delete": "Elimina",
         "delete": "Elimina",
+        "delete_confirm_btn": "Si, elimina",
         "delete_confirm": "Vuoi eliminare \"{{- name}}\"? Questa azione è irreversibile",
         "delete_confirm": "Vuoi eliminare \"{{- name}}\"? Questa azione è irreversibile",
         "delete_confirm_generic": "Vuoi eliminare l'elemento selezionato? Questa azione è irreversibile",
         "delete_confirm_generic": "Vuoi eliminare l'elemento selezionato? Questa azione è irreversibile",
         "delete_multi_confirm": "Vuoi eliminare gli elementi selezionati? Questa azione è irreversibile",
         "delete_multi_confirm": "Vuoi eliminare gli elementi selezionati? Questa azione è irreversibile",
@@ -124,8 +126,6 @@
         "pub_key_placeholder": "Incolla qui la tua chiave pubblica",
         "pub_key_placeholder": "Incolla qui la tua chiave pubblica",
         "verify": "Verifica",
         "verify": "Verifica",
         "problems": "Hai problemi?",
         "problems": "Hai problemi?",
-        "generate": "Genera",
-        "view": "Visualizza",
         "allowed_ip_mask": "IP/Reti permesse",
         "allowed_ip_mask": "IP/Reti permesse",
         "allowed_ip_mask_help": "IP/reti separate da virgola in formato CIDR, ad esempio \"192.168.1.0/24,10.8.0.100/32\"",
         "allowed_ip_mask_help": "IP/reti separate da virgola in formato CIDR, ad esempio \"192.168.1.0/24,10.8.0.100/32\"",
         "allowed_ip_mask_invalid": "IP/reti permesse non valide",
         "allowed_ip_mask_invalid": "IP/reti permesse non valide",
@@ -144,7 +144,9 @@
         "err_quota_read": "Lettura negata a causa del limite di quota",
         "err_quota_read": "Lettura negata a causa del limite di quota",
         "profile_updated": "Il tuo profilo è stato aggiornato con successo",
         "profile_updated": "Il tuo profilo è stato aggiornato con successo",
         "share_ok": "Accesso alla condivisione riuscito, ora puoi utilizzare il tuo collegamento",
         "share_ok": "Accesso alla condivisione riuscito, ora puoi utilizzare il tuo collegamento",
-        "qr_code": "Codice QR"
+        "qr_code": "Codice QR",
+        "copy_link": "Copia collegamento",
+        "copied": "Copiato"
     },
     },
     "fs": {
     "fs": {
         "view_file": "Visualizza file \"{{- path}}\"",
         "view_file": "Visualizza file \"{{- path}}\"",
@@ -212,6 +214,7 @@
             "err_429": "$t(fs.move.err_generic). $t(fs.err_429)"
             "err_429": "$t(fs.move.err_generic). $t(fs.err_429)"
         },
         },
         "rename": {
         "rename": {
+            "title": "Rinomina \"{{- name}}\"",
             "new_name": "Nuovo nome",
             "new_name": "Nuovo nome",
             "err_generic": "Impossibile rinominare \"{{- name}}\"",
             "err_generic": "Impossibile rinominare \"{{- name}}\"",
             "err_403": "$t(fs.rename.err_generic): $t(fs.err_403)",
             "err_403": "$t(fs.rename.err_generic): $t(fs.err_403)",
@@ -260,7 +263,7 @@
         "msg_disabled": "Metti il tuo account in sicurezza",
         "msg_disabled": "Metti il tuo account in sicurezza",
         "msg_info": "L'autenticazione a due fattori aggiunge un ulteriore livello di sicurezza al tuo account. Per accedere dovrai fornire un ulteriore codice di autenticazione.",
         "msg_info": "L'autenticazione a due fattori aggiunge un ulteriore livello di sicurezza al tuo account. Per accedere dovrai fornire un ulteriore codice di autenticazione.",
         "require_for": "Richiedi 2FA per",
         "require_for": "Richiedi 2FA per",
-        "generate": "Genera nuovo segreto",
+        "generate": "Genera una nuova chiave segreta",
         "recovery_codes": "Codici di ripristino",
         "recovery_codes": "Codici di ripristino",
         "new_recovery_codes": "Nuovi codici di ripristino",
         "new_recovery_codes": "Nuovi codici di ripristino",
         "recovery_codes_msg1": "I codici di ripristino sono un insieme di codici monouso che possono essere utilizzati al posto del codice di autenticazione per accedere all'interfaccia utente Web. Puoi utilizzarli se perdi l'accesso al tuo telefono per accedere al tuo account e disabilitare o rigenerare la configurazione dell'autenticazione a due fattori.",
         "recovery_codes_msg1": "I codici di ripristino sono un insieme di codici monouso che possono essere utilizzati al posto del codice di autenticazione per accedere all'interfaccia utente Web. Puoi utilizzarli se perdi l'accesso al tuo telefono per accedere al tuo account e disabilitare o rigenerare la configurazione dell'autenticazione a due fattori.",
@@ -275,7 +278,7 @@
         "setup_msg": "Utilizza la tua app di autenticazione preferita (ad esempio Microsoft Authenticator, Google Authenticator, Authy, 1Password ecc.) per scansionare il codice QR. Verrà generato un codice di autenticazione da inserire di seguito.",
         "setup_msg": "Utilizza la tua app di autenticazione preferita (ad esempio Microsoft Authenticator, Google Authenticator, Authy, 1Password ecc.) per scansionare il codice QR. Verrà generato un codice di autenticazione da inserire di seguito.",
         "setup_help": "Se hai problemi con l'utilizzo del codice QR, seleziona l'inserimento manuale sulla tua app e inserisci il codice:",
         "setup_help": "Se hai problemi con l'utilizzo del codice QR, seleziona l'inserimento manuale sulla tua app e inserisci il codice:",
         "disable_question": "Vuoi disattivare l'autenticazione a due fattori?",
         "disable_question": "Vuoi disattivare l'autenticazione a due fattori?",
-        "generate_question": "Vuoi generare un nuovo segreto e invalidare quello precedente? Qualsiasi app di autenticazione registrata smetterà di funzionare",
+        "generate_question": "Vuoi generare un nuova chiave segreta e invalidare quella precedente? Qualsiasi app di autenticazione registrata smetterà di funzionare",
         "disabled": "L'autenticazione a due fattori è disabilitata",
         "disabled": "L'autenticazione a due fattori è disabilitata",
         "recovery_codes_gen_err": "Impossibile generare nuovi codici di ripristino",
         "recovery_codes_gen_err": "Impossibile generare nuovi codici di ripristino",
         "recovery_codes_get_err": "Impossibile ottenere i codici di ripristino",
         "recovery_codes_get_err": "Impossibile ottenere i codici di ripristino",
@@ -284,7 +287,9 @@
         "save_err": "Impossibile salvare la configurazione",
         "save_err": "Impossibile salvare la configurazione",
         "auth_code_required": "Il codice di autenticazione è obbligatorio",
         "auth_code_required": "Il codice di autenticazione è obbligatorio",
         "no_protocol": "Seleziona almeno un protocollo",
         "no_protocol": "Seleziona almeno un protocollo",
-        "required_protocols": "I seguenti protocolli sono obbligatori: {{val}}"
+        "required_protocols": "La politica di sicurezza configurata per il tuo account richiede l'autenticazione a due fattori per i seguenti protocolli: {{val}}",
+        "recovery_codes_generate": "Genera nuovi codici di ripristino",
+        "recovery_codes_view": "Visualizza codici di ripristino"
     },
     },
     "share": {
     "share": {
         "scope": "Ambito",
         "scope": "Ambito",
@@ -319,7 +324,17 @@
         "usage_exceed": "Utilizzo massimo della condivisione superato",
         "usage_exceed": "Utilizzo massimo della condivisione superato",
         "expired": "La condivisione è scaduta",
         "expired": "La condivisione è scaduta",
         "browsable_multiple_paths": "Una condivisione con più percorsi non è navigabile",
         "browsable_multiple_paths": "Una condivisione con più percorsi non è navigabile",
-        "browsable_non_dir": "La condivisione non è una directory quindi non è navigabile"
+        "browsable_non_dir": "La condivisione non è una directory quindi non è navigabile",
+        "go": "Vai alla condivisione",
+        "access_links_title": "Accesso alla condivisione",
+        "link_single_title": "File zip singolo",
+        "link_single_desc": "È possibile scaricare il contenuto condiviso come un singolo file zip",
+        "link_dir_title": "Directory singola",
+        "link_dir_desc": "Se la condivisione è costituita da un'unica directory, è possibile navigarla e scaricare file",
+        "link_uncompressed_title": "File non compresso",
+        "link_uncompressed_desc": "Se la condivisione è costituita da un unico file è possibile scaricarlo anche non compresso",
+        "upload_desc": "È possibile caricare uno o più file nella directory condivisa",
+        "expired_desc": "Questa condivisione non è più accessibile perché è scaduta"
     },
     },
     "select2": {
     "select2": {
         "no_results": "Nessun risultato trovato",
         "no_results": "Nessun risultato trovato",

+ 45 - 5
templates/common/base.html

@@ -15,7 +15,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 -->
 -->
 {{- define "errmsg"}}
 {{- define "errmsg"}}
 <div id="errorMsg" class="{{if not . }}d-none {{end}}rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
 <div id="errorMsg" class="{{if not . }}d-none {{end}}rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
-    <i class="ki-duotone ki-information fs-3x text-warning me-5">
+    <i class="ki-duotone ki-information-5 fs-3x text-warning me-5">
         <span class="path1"></span>
         <span class="path1"></span>
         <span class="path2"></span>
         <span class="path2"></span>
         <span class="path3"></span>
         <span class="path3"></span>
@@ -24,10 +24,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 		<span {{if .}}data-i18n="{{.Message}}" {{if .HasArgs}}data-i18n-options="{{.Args}}"{{end}}{{end}} id="errorTxt"></span>
 		<span {{if .}}data-i18n="{{.Message}}" {{if .HasArgs}}data-i18n-options="{{.Args}}"{{end}}{{end}} id="errorTxt"></span>
 	</div>
 	</div>
     <button id="id_dismiss_error_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
     <button id="id_dismiss_error_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
-        <i class="ki-duotone ki-cross fs-2x text-primary">
-            <span class="path1"></span>
-            <span class="path2"></span>
-        </i>
+        <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
     </button>
     </button>
 </div>
 </div>
 {{- end}}
 {{- end}}
@@ -231,6 +228,33 @@ explicit grant from the SFTPGo Team ([email protected]).
         el.localize(options);
         el.localize(options);
     }
     }
 
 
+    function handlePasswordInputVisibility(el) {
+		let pwdVisibility = el.querySelector('[data-password-control="visibility"]');
+		let passwordInput = el.querySelector('[data-password-control="input"]');
+		pwdVisibility.addEventListener('click', function(){
+			let visibleIcon = this.querySelector(':scope > i:not(.d-none)');
+            let hiddenIcon = this.querySelector(':scope > i.d-none');
+            visibleIcon.classList.add('d-none');
+            hiddenIcon.classList.remove('d-none');
+            if (passwordInput){
+				if (passwordInput.getAttribute('type').toLowerCase() === 'password' ) {
+            		passwordInput.setAttribute('type', 'text');
+        		} else {
+            		passwordInput.setAttribute('type', 'password');
+        		}
+				passwordInput.focus();
+			}
+		});
+	}
+
+    function getCurrentURI(){
+        let port = window.location.port;
+        if (port){
+            return window.location.protocol+"//"+window.location.hostname+":"+port;
+        }
+        return window.location.protocol+"//"+window.location.hostname;
+    }
+
     KTUtil.onDOMContentLoaded(function () {
     KTUtil.onDOMContentLoaded(function () {
         var dismissErrorBtn = $('#id_dismiss_error_msg');
         var dismissErrorBtn = $('#id_dismiss_error_msg');
         if (dismissErrorBtn){
         if (dismissErrorBtn){
@@ -246,6 +270,10 @@ explicit grant from the SFTPGo Team ([email protected]).
             });
             });
         }
         }
 
 
+        document.querySelectorAll('[data-password-control="container"]').forEach(el => {
+			handlePasswordInputVisibility(el);
+		});
+
         initLocalizer();
         initLocalizer();
 	});
 	});
 </script>
 </script>
@@ -268,6 +296,18 @@ explicit grant from the SFTPGo Team ([email protected]).
     .line-through {
     .line-through {
         text-decoration: line-through;
         text-decoration: line-through;
     }
     }
+
+    .section-title {
+        color: var(--bs-text-gray-800);
+        font-weight: 600 !important;
+        font-size: 1.45rem !important;
+    }
+
+    .section-title-inner {
+        color: var(--bs-text-gray-800);
+        font-weight: 600 !important;
+        font-size: 1.3rem !important;
+    }
 </style>
 </style>
 {{- end}}
 {{- end}}
 
 

+ 5 - 9
templates/webclient/base.html

@@ -297,17 +297,14 @@ explicit grant from the SFTPGo Team ([email protected]).
         <div class="toast-container position-fixed top-0 end-0 p-3">
         <div class="toast-container position-fixed top-0 end-0 p-3">
             <div id="toast_container" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
             <div id="toast_container" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
                 <div class="toast-body border-0 d-flex align-items-center">
                 <div class="toast-body border-0 d-flex align-items-center">
-                    <i class="ki-duotone ki-information fs-3x text-warning me-5">
+                    <i class="ki-duotone ki-information-5 fs-3x text-warning me-5">
                         <span class="path1"></span>
                         <span class="path1"></span>
                         <span class="path2"></span>
                         <span class="path2"></span>
                         <span class="path3"></span>
                         <span class="path3"></span>
                     </i>
                     </i>
                     <span id="toast_msg" class="fs-5 fw-semibold text-gray-800 me-auto"></span>
                     <span id="toast_msg" class="fs-5 fw-semibold text-gray-800 me-auto"></span>
-                    <button data-i18n="[aria-label]general.close" type="button" class="btn btn-icon btn-sm btn-close position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 ms-sm-auto" data-bs-dismiss="toast" aria-label="Close">
-                        <i class="ki-duotone ki-cross fs-2x text-primary">
-                            <span class="path1"></span>
-                            <span class="path2"></span>
-                        </i>
+                    <button data-i18n="[aria-label]general.close" type="button" class="btn btn-icon btn-sm btn-active-light-primary position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 ms-sm-auto" data-bs-dismiss="toast" aria-label="Close">
+                        <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                     </button>
                     </button>
                 </div>
                 </div>
             </div>
             </div>
@@ -382,17 +379,16 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
                         switch (params.icon){
                         switch (params.icon){
                             case "warning":
                             case "warning":
-                                $("#modal_alert_icon").html(`<i class="ki-duotone ki-information fs-5x text-danger">
+                                $("#modal_alert_icon").html(`<i class="ki-duotone ki-information-5 fs-5x text-danger">
                                                                 <span class="path1"></span>
                                                                 <span class="path1"></span>
                                                                 <span class="path2"></span>
                                                                 <span class="path2"></span>
                                                                 <span class="path3"></span>
                                                                 <span class="path3"></span>
                                                             </i>`);
                                                             </i>`);
                                 break;
                                 break;
                             case "success":
                             case "success":
-                                $("#modal_alert_icon").html(`<i class="ki-duotone ki-information-2 fs-5x text-success">
+                                $("#modal_alert_icon").html(`<i class="ki-duotone ki-check-square fs-5x text-success">
                                                                 <span class="path1"></span>
                                                                 <span class="path1"></span>
                                                                 <span class="path2"></span>
                                                                 <span class="path2"></span>
-                                                                <span class="path3"></span>
                                                             </i>`);
                                                             </i>`);
                                 break;
                                 break;
                             default:
                             default:

+ 52 - 7
templates/webclient/changepassword.html

@@ -18,7 +18,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 {{- define "page_body"}}
 {{- define "page_body"}}
 <div class="card shadow-sm">
 <div class="card shadow-sm">
     <div class="card-header bg-light">
     <div class="card-header bg-light">
-        <h3 data-i18n="title.change_password" class="card-title text-primary">Change password</h3>
+        <h3 data-i18n="title.change_password" class="card-title section-title">Change password</h3>
     </div>
     </div>
     <div class="card-body">
     <div class="card-body">
         <div class="notice d-flex bg-light-primary rounded border-primary border border-dashed p-6 mb-5">
         <div class="notice d-flex bg-light-primary rounded border-primary border border-dashed p-6 mb-5">
@@ -41,22 +41,67 @@ explicit grant from the SFTPGo Team ([email protected]).
             <div class="form-group row">
             <div class="form-group row">
                 <label data-i18n="change_pwd.current" class="col-md-3 col-form-label required">Current password</label>
                 <label data-i18n="change_pwd.current" class="col-md-3 col-form-label required">Current password</label>
                 <div class="col-md-9">
                 <div class="col-md-9">
-                    <input type="password" class="form-control" placeholder="" name="current_password"
-                        spellcheck="false" required />
+                    <div class="position-relative" data-password-control="container">
+                        <input type="password" data-password-control="input" class="form-control" placeholder=""
+                            name="current_password" spellcheck="false" required />
+                        <span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                            <i class="ki-duotone ki-eye-slash fs-1">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                                <span class="path3"></span>
+                                <span class="path4"></span>
+                            </i>
+                            <i class="ki-duotone ki-eye d-none fs-1">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                                <span class="path3"></span>
+                            </i>
+                        </span>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
             <div class="form-group row mt-10">
             <div class="form-group row mt-10">
                 <label data-i18n="change_pwd.new" class="col-md-3 col-form-label required">New password</label>
                 <label data-i18n="change_pwd.new" class="col-md-3 col-form-label required">New password</label>
                 <div class="col-md-9">
                 <div class="col-md-9">
-                    <input type="password" class="form-control" placeholder="" name="new_password1"
-                        autocomplete="new-password" spellcheck="false" required />
+                    <div class="position-relative" data-password-control="container">
+                        <input type="password" data-password-control="input" class="form-control" placeholder=""
+                            name="new_password1" autocomplete="new-password" spellcheck="false" required />
+                        <span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                            <i class="ki-duotone ki-eye-slash fs-1">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                                <span class="path3"></span>
+                                <span class="path4"></span>
+                            </i>
+                            <i class="ki-duotone ki-eye d-none fs-1">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                                <span class="path3"></span>
+                            </i>
+                        </span>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
             <div class="form-group row mt-10">
             <div class="form-group row mt-10">
                 <label data-i18n="change_pwd.confirm" class="col-md-3 col-form-label required">Confirm password</label>
                 <label data-i18n="change_pwd.confirm" class="col-md-3 col-form-label required">Confirm password</label>
                 <div class="col-md-9">
                 <div class="col-md-9">
-                    <input type="password" class="form-control" placeholder="" name="new_password2"
-                        autocomplete="new-password" spellcheck="false" required />
+                    <div class="position-relative" data-password-control="container">
+                        <input type="password" data-password-control="input" class="form-control" placeholder=""
+                            name="new_password2" autocomplete="new-password" spellcheck="false" required />
+                        <span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                            <i class="ki-duotone ki-eye-slash fs-1">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                                <span class="path3"></span>
+                                <span class="path4"></span>
+                            </i>
+                            <i class="ki-duotone ki-eye d-none fs-1">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                                <span class="path3"></span>
+                            </i>
+                        </span>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
             <div class="d-flex justify-content-end mt-12">
             <div class="d-flex justify-content-end mt-12">

+ 3 - 3
templates/webclient/editfile.html

@@ -42,7 +42,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 {{- template "errmsg" ""}}
 {{- template "errmsg" ""}}
 <div class="card shadow-sm">
 <div class="card shadow-sm">
     <div class="card-header">
     <div class="card-header">
-        <h6 id="card_title" class="card-title"></h6>
+        <h6 id="card_title" class="card-title section-title"></h6>
         <div class="card-toolbar">
         <div class="card-toolbar">
             <a data-i18n="general.back" class="btn btn-light-primary px-10 me-5" href='{{.FilesURL}}?path={{.CurrentDir}}' role="button">Back</a>
             <a data-i18n="general.back" class="btn btn-light-primary px-10 me-5" href='{{.FilesURL}}?path={{.CurrentDir}}' role="button">Back</a>
             {{- if not .ReadOnly}}
             {{- if not .ReadOnly}}
@@ -68,8 +68,8 @@ explicit grant from the SFTPGo Team ([email protected]).
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
                 <h3 data-i18n="editor.keybinding" class="modal-title">Editor keybindings</h3>
                 <h3 data-i18n="editor.keybinding" class="modal-title">Editor keybindings</h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary ms-2" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
             <div class="modal-body">
             <div class="modal-body">

+ 61 - 51
templates/webclient/files.html

@@ -49,7 +49,25 @@ explicit grant from the SFTPGo Team ([email protected]).
                 </button>
                 </button>
                 {{- end}}
                 {{- end}}
             </div>
             </div>
-            <div class="d-flex justify-content-end align-items-center d-none" data-kt-filemanager-table-toolbar="selected">
+        </div>
+    </div>
+    <div class="card-body">
+        <div class="d-flex flex-stack">
+            <div class="badge badge-lg badge-light-primary" data-kt-filemanager-table-nav-bar="base">
+                <div class="d-flex align-items-center flex-wrap">
+                    <i class="ki-duotone ki-home fs-1 text-primary me-3"></i>
+                    <a data-i18n="fs.home" href="{{.FilesURL}}?path=%2F">Home</a>
+                    {{- range .Paths}}
+                    <i class="ki-duotone ki-right fs-2x text-primary mx-1"></i>
+                    {{- if eq .Href ""}}
+                    <span>{{.DirName}}</span>
+                    {{- else}}
+                    <a href="{{.Href}}">{{.DirName}}</a>
+                    {{- end}}
+                    {{- end}}
+                </div>
+            </div>
+            <div class="d-flex align-items-center d-none" data-kt-filemanager-table-toolbar="selected">
                 <div class="fw-bold me-5">
                 <div class="fw-bold me-5">
                     <span class="me-2" data-kt-filemanager-table-select="selected_count"></span>
                     <span class="me-2" data-kt-filemanager-table-select="selected_count"></span>
                 </div>
                 </div>
@@ -101,25 +119,10 @@ explicit grant from the SFTPGo Team ([email protected]).
                 {{- end}}
                 {{- end}}
             </div>
             </div>
         </div>
         </div>
-    </div>
-    <div class="card-body">
-        <div class="d-flex flex-stack">
-            <div class="badge badge-lg badge-light-primary">
-                <div class="d-flex align-items-center flex-wrap">
-                    <i class="ki-duotone ki-home fs-1 text-primary me-3"></i>
-                    <a data-i18n="fs.home" href="{{.FilesURL}}?path=%2F">Home</a>
-                    {{- range .Paths}}
-                    <i class="ki-duotone ki-right fs-2x text-primary mx-1"></i>
-                    {{- if eq .Href ""}}
-                    <span>{{.DirName}}</span>
-                    {{- else}}
-                    <a href="{{.Href}}">{{.DirName}}</a>
-                    {{- end}}
-                    {{- end}}
-                </div>
-            </div>
+        <div class="new_folder_divider py-2 d-none">
+            <hr>
         </div>
         </div>
-        <div id="file_manager_new_folder" class="d-flex align-items-center py-7 d-none">
+        <div id="file_manager_new_folder" class="d-flex align-items-center d-none">
             <span>
             <span>
                 <i class="ki-duotone ki-folder fs-2x text-primary me-4">
                 <i class="ki-duotone ki-folder fs-2x text-primary me-4">
                     <span class="path1"></span>
                     <span class="path1"></span>
@@ -136,15 +139,15 @@ explicit grant from the SFTPGo Team ([email protected]).
                 </span>
                 </span>
             </button>
             </button>
             <button class="btn btn-icon btn-light-danger" id="file_manager_cancel_folder">
             <button class="btn btn-icon btn-light-danger" id="file_manager_cancel_folder">
-                <i class="ki-duotone ki-cross fs-1">
-                    <span class="path1"></span>
-                    <span class="path2"></span>
-                </i>
+                <i class="ki-solid ki-cross fs-1"></i>
             </button>
             </button>
         </div>
         </div>
+        <div class="new_folder_divider py-2 d-none">
+            <hr>
+        </div>
         <table id="file_manager_list" class="table align-middle table-row-dashed fs-6 gy-5">
         <table id="file_manager_list" class="table align-middle table-row-dashed fs-6 gy-5">
             <thead>
             <thead>
-                <tr class="text-start text-muted fw-bold fs-6 gs-0">
+                <tr class="text-start text-muted fw-bold fs-6 gs-0 text-gray-500">
                     <th class="w-10px pe-2">
                     <th class="w-10px pe-2">
                         <div class="form-check form-check-sm form-check-custom form-check-solid me-3">
                         <div class="form-check form-check-sm form-check-custom form-check-solid me-3">
                             <input id="select_checkbox" class="form-check-input" type="checkbox"/>
                             <input id="select_checkbox" class="form-check-input" type="checkbox"/>
@@ -375,6 +378,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                     submitButton.removeAttribute('data-kt-indicator');
                     submitButton.removeAttribute('data-kt-indicator');
 		            submitButton.disabled = false;
 		            submitButton.disabled = false;
                     cancelButton.disabled = false;
                     cancelButton.disabled = false;
+                    $('.dir_browser_folder_divider').addClass("d-none");
                     $('#dirsbrowser_new_folder').addClass("d-none");
                     $('#dirsbrowser_new_folder').addClass("d-none");
                 }).catch(function (error) {
                 }).catch(function (error) {
                     let errorMessage = "";
                     let errorMessage = "";
@@ -770,7 +774,7 @@ explicit grant from the SFTPGo Team ([email protected]).
         }
         }
 
 
         function handleToogleToolbar(pageSelected, totalSelected) {
         function handleToogleToolbar(pageSelected, totalSelected) {
-            const toolbarBase = document.querySelector('[data-kt-filemanager-table-toolbar="base"]');
+            const navBar = document.querySelector('[data-kt-filemanager-table-nav-bar="base"]');
             const toolbarSelected = document.querySelector('[data-kt-filemanager-table-toolbar="selected"]');
             const toolbarSelected = document.querySelector('[data-kt-filemanager-table-toolbar="selected"]');
             const selectedCount = document.querySelector('[data-kt-filemanager-table-select="selected_count"]');
             const selectedCount = document.querySelector('[data-kt-filemanager-table-select="selected_count"]');
             const selectAllContainer = document.querySelector('[data-kt-filemanager-table-select="select_all_pages_container"]');
             const selectAllContainer = document.querySelector('[data-kt-filemanager-table-select="select_all_pages_container"]');
@@ -789,8 +793,8 @@ explicit grant from the SFTPGo Team ([email protected]).
                 if (selectedCount){
                 if (selectedCount){
                     selectedCount.innerHTML =  $.t('general.selected_items', { count: totalSelected});
                     selectedCount.innerHTML =  $.t('general.selected_items', { count: totalSelected});
                 }
                 }
-                if (toolbarBase){
-                    toolbarBase.classList.add('d-none');
+                if (navBar){
+                    navBar.classList.add('d-none');
                 }
                 }
                 if (toolbarSelected){
                 if (toolbarSelected){
                     toolbarSelected.classList.remove('d-none');
                     toolbarSelected.classList.remove('d-none');
@@ -798,10 +802,10 @@ explicit grant from the SFTPGo Team ([email protected]).
             } else {
             } else {
                 $('#select_checkbox').prop("checked", false);
                 $('#select_checkbox').prop("checked", false);
                 selectAllCheck.checked = false;
                 selectAllCheck.checked = false;
-                if (toolbarBase) {
-                    toolbarBase.classList.remove('d-none');
+                if (navBar) {
+                    navBar.classList.remove('d-none');
                 }
                 }
-                if (toolbarBase) {
+                if (toolbarSelected) {
                     toolbarSelected.classList.add('d-none');
                     toolbarSelected.classList.add('d-none');
                 }
                 }
             }
             }
@@ -961,7 +965,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                     ModalAlert.fire({
                     ModalAlert.fire({
                         text: $.t('general.delete_multi_confirm'),
                         text: $.t('general.delete_multi_confirm'),
                         icon: "warning",
                         icon: "warning",
-                        confirmButtonText: $.t('general.delete'),
+                        confirmButtonText: $.t('general.delete_confirm_btn'),
                         cancelButtonText: $.t('general.cancel'),
                         cancelButtonText: $.t('general.cancel'),
                         customClass: {
                         customClass: {
                             confirmButton: "btn btn-danger",
                             confirmButton: "btn btn-danger",
@@ -1361,7 +1365,7 @@ explicit grant from the SFTPGo Team ([email protected]).
         ModalAlert.fire({
         ModalAlert.fire({
             text: $.t('general.delete_confirm', {name: itemName}),
             text: $.t('general.delete_confirm', {name: itemName}),
             icon: "warning",
             icon: "warning",
-            confirmButtonText: $.t('general.delete'),
+            confirmButtonText: $.t('general.delete_confirm_btn'),
             cancelButtonText: $.t('general.cancel'),
             cancelButtonText: $.t('general.cancel'),
             customClass: {
             customClass: {
                 confirmButton: "btn btn-danger",
                 confirmButton: "btn btn-danger",
@@ -1420,7 +1424,7 @@ explicit grant from the SFTPGo Team ([email protected]).
         let oldName = getNameFromMeta(meta);
         let oldName = getNameFromMeta(meta);
         $('#rename_old_name').val(meta);
         $('#rename_old_name').val(meta);
         $('#rename_new_name').val(oldName);
         $('#rename_new_name').val(oldName);
-        $('#rename_title').text(`Rename "${oldName}"`);
+        $('#rename_title').text($.t('fs.rename.title', { name: oldName}));
         $('#modal_rename').modal('show');
         $('#modal_rename').modal('show');
     }
     }
 
 
@@ -1473,6 +1477,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
     function showCreateNewFolder(sender) {
     function showCreateNewFolder(sender) {
         if (sender == 0) {
         if (sender == 0) {
+            $('.new_folder_divider').removeClass("d-none");
             $('#file_manager_new_folder').removeClass("d-none");
             $('#file_manager_new_folder').removeClass("d-none");
             $('#errorMsg').addClass("d-none");
             $('#errorMsg').addClass("d-none");
             let el = $('#file_manager_new_folder_input');
             let el = $('#file_manager_new_folder_input');
@@ -1480,6 +1485,7 @@ explicit grant from the SFTPGo Team ([email protected]).
             el.focus();
             el.focus();
             return;
             return;
         }
         }
+        $('.dir_browser_folder_divider').removeClass("d-none");
         $('#dirsbrowser_new_folder').removeClass("d-none");
         $('#dirsbrowser_new_folder').removeClass("d-none");
         $('#errorModalMsg').addClass("d-none");
         $('#errorModalMsg').addClass("d-none");
         let el = $('#dirsbrowser_new_folder_input');
         let el = $('#dirsbrowser_new_folder_input');
@@ -1489,8 +1495,10 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
     function hideCreateNewFolder(sender) {
     function hideCreateNewFolder(sender) {
         if (sender == 0){
         if (sender == 0){
+            $('.new_folder_divider').addClass("d-none");
             $('#file_manager_new_folder').addClass("d-none");
             $('#file_manager_new_folder').addClass("d-none");
         } else {
         } else {
+            $('.dir_browser_folder_divider').addClass("d-none");
             $('#dirsbrowser_new_folder').addClass("d-none");
             $('#dirsbrowser_new_folder').addClass("d-none");
         }
         }
     }
     }
@@ -1808,7 +1816,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                         {{- if $files}}
                         {{- if $files}}
                         {{- $percentage := .QuotaUsage.GetQuotaFilesPercentage}}
                         {{- $percentage := .QuotaUsage.GetQuotaFilesPercentage}}
                         <div class="{{if .QuotaUsage.IsQuotaFilesLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
                         <div class="{{if .QuotaUsage.IsQuotaFilesLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
-                            <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.files_percentage" data-i18n-options='{ "val": "{{$files}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.files" data-i18n-options='{ "val": "{{$files}}" }'{{end}}></span>
+                            <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.files_percentage" data-i18n-options='{ "val": "{{$files}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.files" data-i18n-options='{ "num": "{{$files}}" }'{{end}}></span>
                         </div>
                         </div>
                         {{- end}}
                         {{- end}}
                     </div>
                     </div>
@@ -1866,8 +1874,8 @@ explicit grant from the SFTPGo Team ([email protected]).
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header border-0">
             <div class="modal-header border-0">
                 <h3 data-i18n="fs.upload.text" class="modal-title">Upload files</h3>
                 <h3 data-i18n="fs.upload.text" class="modal-title">Upload files</h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
             <div class="modal-body">
             <div class="modal-body">
@@ -1900,8 +1908,8 @@ explicit grant from the SFTPGo Team ([email protected]).
                 <h5 class="modal-title">
                 <h5 class="modal-title">
                     <span id="video_title"></span>
                     <span id="video_title"></span>
                 </h5>
                 </h5>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
 
 
@@ -1922,8 +1930,8 @@ explicit grant from the SFTPGo Team ([email protected]).
                 <h5 class="modal-title">
                 <h5 class="modal-title">
                     <span id="rename_title"></span>
                     <span id="rename_title"></span>
                 </h5>
                 </h5>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
 
 
@@ -1937,7 +1945,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
             <div class="modal-footer border-0">
             <div class="modal-footer border-0">
                 <button data-i18n="general.cancel" type="button" class="btn btn-secondary me-5" data-bs-dismiss="modal">Cancel</button>
                 <button data-i18n="general.cancel" type="button" class="btn btn-secondary me-5" data-bs-dismiss="modal">Cancel</button>
-                <button data-i18n="general.submit" id="id_do_rename_button" type="button" class="btn btn-primary" data-bs-dismiss="modal">Submit</button>
+                <button data-i18n="general.confirm" id="id_do_rename_button" type="button" class="btn btn-primary" data-bs-dismiss="modal">Submit</button>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>
@@ -1950,19 +1958,19 @@ explicit grant from the SFTPGo Team ([email protected]).
                 <h3 data-i18n="general.choose_target_folder" class="modal-title">
                 <h3 data-i18n="general.choose_target_folder" class="modal-title">
                     Choose target folder
                     Choose target folder
                 </h3>
                 </h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
 
 
             <div class="modal-body">
             <div class="modal-body">
                 <div id="errorModalMsg" class="d-none rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
                 <div id="errorModalMsg" class="d-none rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
-                    <i class="ki-duotone ki-information fs-3x text-warning me-5"><span class="path1"></span><span class="path2"></span><span class="path3"></span></i>
+                    <i class="ki-duotone ki-information-5 fs-3x text-warning me-5"><span class="path1"></span><span class="path2"></span><span class="path3"></span></i>
                     <div class="text-gray-700 fw-bold fs-5 d-flex flex-column pe-0 pe-sm-10">
                     <div class="text-gray-700 fw-bold fs-5 d-flex flex-column pe-0 pe-sm-10">
                         <span id="errorModalTxt"></span>
                         <span id="errorModalTxt"></span>
                     </div>
                     </div>
                     <button id="id_dismiss_error_modal_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
                     <button id="id_dismiss_error_modal_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
-                        <i class="ki-duotone ki-cross fs-2x text-primary"><span class="path1"></span><span class="path2"></span></i>
+                        <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                     </button>
                     </button>
                 </div>
                 </div>
                 <div class="row">
                 <div class="row">
@@ -1982,7 +1990,10 @@ explicit grant from the SFTPGo Team ([email protected]).
                         </button>
                         </button>
                     </div>
                     </div>
                 </div>
                 </div>
-                <div id="dirsbrowser_new_folder" class="d-flex align-items-center py-7 d-none">
+                <div class="dir_browser_folder_divider py-2 d-none">
+                    <hr>
+                </div>
+                <div id="dirsbrowser_new_folder" class="d-flex align-items-center d-none">
                     <span>
                     <span>
                         <i class="ki-duotone ki-folder fs-2x text-primary me-4">
                         <i class="ki-duotone ki-folder fs-2x text-primary me-4">
                             <span class="path1"></span>
                             <span class="path1"></span>
@@ -1999,13 +2010,12 @@ explicit grant from the SFTPGo Team ([email protected]).
                         </span>
                         </span>
                     </button>
                     </button>
                     <button class="btn btn-icon btn-light-danger" id="dirsbrowser_cancel_folder">
                     <button class="btn btn-icon btn-light-danger" id="dirsbrowser_cancel_folder">
-                        <i class="ki-duotone ki-cross fs-1">
-                            <span class="path1"></span>
-                            <span class="path2"></span>
-                        </i>
+                        <i class="ki-solid ki-cross fs-1"></i>
                     </button>
                     </button>
                 </div>
                 </div>
-
+                <div class="dir_browser_folder_divider py-2 d-none">
+                    <hr>
+                </div>
                 <table id="dirsbrowser_list" class="table align-middle table-row-dashed fs-6 gy-5">
                 <table id="dirsbrowser_list" class="table align-middle table-row-dashed fs-6 gy-5">
                     <thead>
                     <thead>
                         <tr class="text-start text-muted fw-bold fs-7 text-uppercase gs-0">
                         <tr class="text-start text-muted fw-bold fs-7 text-uppercase gs-0">

+ 21 - 3
templates/webclient/login.html

@@ -35,7 +35,22 @@ explicit grant from the SFTPGo Team ([email protected]).
 								<input data-i18n="[placeholder]login.username" class="form-control form-control-lg form-control-solid" type="text" name="username" placeholder="Username" autocomplete="on" spellcheck="false" required />
 								<input data-i18n="[placeholder]login.username" class="form-control form-control-lg form-control-solid" type="text" name="username" placeholder="Username" autocomplete="on" spellcheck="false" required />
 							</div>
 							</div>
 							<div class="fv-row mb-10">
 							<div class="fv-row mb-10">
-								<input data-i18n="[placeholder]login.password" class="form-control form-control-lg form-control-solid" type="password" name="password" placeholder="Password" autocomplete="current-password" spellcheck="false" required />
+								<div class="position-relative" data-password-control="container">
+									<input data-i18n="[placeholder]login.password" data-password-control="input" class="form-control form-control-lg form-control-solid" type="password" name="password" placeholder="Password" autocomplete="current-password" spellcheck="false" required />
+									<span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                    					<i class="ki-duotone ki-eye-slash fs-1">
+											<span class="path1"></span>
+											<span class="path2"></span>
+											<span class="path3"></span>
+											<span class="path4"></span>
+										</i>
+                    					<i class="ki-duotone ki-eye d-none fs-1">
+											<span class="path1"></span>
+											<span class="path2"></span>
+											<span class="path3"></span>
+										</i>
+									</span>
+								</div>
 								<div class="d-flex justify-content-end mt-2">
 								<div class="d-flex justify-content-end mt-2">
 									{{- if .ForgotPwdURL}}
 									{{- if .ForgotPwdURL}}
 									<a data-i18n="login.forgot_password" href="{{.ForgotPwdURL}}" class="link-primary fs-6 fw-bold">Forgot Password ?</a>
 									<a data-i18n="login.forgot_password" href="{{.ForgotPwdURL}}" class="link-primary fs-6 fw-bold">Forgot Password ?</a>
@@ -65,14 +80,17 @@ explicit grant from the SFTPGo Team ([email protected]).
 								{{- end}}
 								{{- end}}
 							</div>
 							</div>
 						</form>
 						</form>
-						<div class=" d-flex flex-stack pt-5 mt-5">
+						<hr>
+						<div class="d-flex flex-stack pt-5 mt-3">
 							<div class="me-10">
 							<div class="me-10">
 								<select id="languageSwitcher" name="language" class="form-select form-select-solid form-select-sm" data-control="i18n-select2" data-hide-search="true">
 								<select id="languageSwitcher" name="language" class="form-select form-select-solid form-select-sm" data-control="i18n-select2" data-hide-search="true">
 								</select>
 								</select>
 							</div>
 							</div>
 							<div class="d-flex fw-semibold text-primary">
 							<div class="d-flex fw-semibold text-primary">
 								{{- if .AltLoginURL}}
 								{{- if .AltLoginURL}}
-								<a href="{{.AltLoginURL}}" class="px-2">{{.AltLoginName}}</a>
+								<a href="{{.AltLoginURL}}" class="px-2">
+									<span data-i18n="login.link" data-i18n-options='{ "link": "{{.AltLoginName}}" }'></span>
+								</a>
 								{{- end}}
 								{{- end}}
 								{{- if and .Branding.DisclaimerName .Branding.DisclaimerPath}}
 								{{- if and .Branding.DisclaimerName .Branding.DisclaimerPath}}
 								<a href="{{.StaticURL}}{{.Branding.DisclaimerPath}}" target="_blank" class="px-2">
 								<a href="{{.StaticURL}}{{.Branding.DisclaimerPath}}" target="_blank" class="px-2">

+ 4 - 4
templates/webclient/message.html

@@ -40,19 +40,19 @@ explicit grant from the SFTPGo Team ([email protected]).
     {{- end}}
     {{- end}}
     <div class="card shadow-sm w-lg-600px">
     <div class="card shadow-sm w-lg-600px">
         <div class="card-header bg-light">
         <div class="card-header bg-light">
-            <h3 data-i18n="{{.Title}}" class="card-title text-primary"></h3>
+            <h3 data-i18n="{{.Title}}" class="card-title section-title"></h3>
         </div>
         </div>
         <div class="card-body">
         <div class="card-body">
         {{- if .Error}}
         {{- if .Error}}
             <div class="notice d-flex bg-light-warning rounded border-warning border border-dashed p-6">
             <div class="notice d-flex bg-light-warning rounded border-warning border border-dashed p-6">
-                <i class="ki-duotone ki-information fs-2tx text-warning me-4">
+                <i class="ki-duotone ki-information-5 fs-2tx text-warning me-4">
                     <span class="path1"></span>
                     <span class="path1"></span>
                     <span class="path2"></span>
                     <span class="path2"></span>
                     <span class="path3"></span>
                     <span class="path3"></span>
                 </i>
                 </i>
                 <div class="d-flex flex-stack flex-grow-1 ">
                 <div class="d-flex flex-stack flex-grow-1 ">
                     <div class=" fw-semibold">
                     <div class=" fw-semibold">
-                        <div class="fs-5 text-gray-800">
+                        <div class="fs-4 text-gray-800">
                             <span data-i18n="{{.Error.Message}}" {{if .Error.HasArgs}}data-i18n-options="{{.Error.Args}}"{{end}}></span>
                             <span data-i18n="{{.Error.Message}}" {{if .Error.HasArgs}}data-i18n-options="{{.Error.Args}}"{{end}}></span>
                         </div>
                         </div>
                     </div>
                     </div>
@@ -68,7 +68,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                 </i>
                 </i>
                 <div class="d-flex flex-stack flex-grow-1 ">
                 <div class="d-flex flex-stack flex-grow-1 ">
                     <div class=" fw-semibold">
                     <div class=" fw-semibold">
-                        <div class="fs-5 text-gray-800">
+                        <div class="fs-4 text-gray-800">
                             <span data-i18n="{{.Success}}"></span>
                             <span data-i18n="{{.Success}}"></span>
                         </div>
                         </div>
                     </div>
                     </div>

+ 153 - 76
templates/webclient/mfa.html

@@ -16,11 +16,42 @@ explicit grant from the SFTPGo Team ([email protected]).
 {{template "base" .}}
 {{template "base" .}}
 
 
 {{- define "page_body"}}
 {{- define "page_body"}}
+
+{{- if .TOTPConfig.Enabled}}
+<div class="notice d-flex bg-light-primary rounded border-primary border border-dashed p-6 mb-5">
+    <i class="ki-duotone ki-shield-tick fs-2tx text-primary me-4">
+        <span class="path1"></span>
+        <span class="path2"></span>
+    </i>
+    <div class="d-flex flex-stack flex-grow-1 flex-wrap flex-md-nowrap">
+        <div class="mb-3 mb-md-0 fw-semibold">
+            <h4 class="text-gray-900 fw-bold">
+                <span data-i18n="2fa.msg_enabled"></span> {{- if gt (len .TOTPConfigs) 1 }}
+                ({{$.TOTPConfig.ConfigName}}) {{- end}}
+            </h4>
+            <div class="fs-6 text-gray-800 pe-7">
+                <span data-i18n="2fa.msg_info"></span>
+            </div>
+        </div>
+        <button type="button" id="disable_btn" class="btn btn-danger ms-4 px-6 align-self-center text-nowrap">
+            <span data-i18n="general.disable" class="indicator-label">
+                Disable
+            </span>
+            <span data-i18n="general.wait" class="indicator-progress">
+                Please wait...
+                <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
+            </span>
+        </button>
+    </div>
+</div>
+{{- end}}
+
 <div class="card shadow-sm">
 <div class="card shadow-sm">
     <div class="card-header bg-light">
     <div class="card-header bg-light">
-        <h3 data-i18n="2fa.title" class="card-title text-primary">Two-factor authentication using Authenticator apps</h3>
+        <h3 data-i18n="2fa.title" class="card-title section-title">Two-factor authentication using Authenticator apps</h3>
     </div>
     </div>
     <div class="card-body">
     <div class="card-body">
+        {{- if not .TOTPConfig.Enabled}}
         <div class="notice d-flex bg-light-primary rounded border-primary border border-dashed p-6 mb-5">
         <div class="notice d-flex bg-light-primary rounded border-primary border border-dashed p-6 mb-5">
             <i class="ki-duotone ki-shield-tick fs-2tx text-primary me-4">
             <i class="ki-duotone ki-shield-tick fs-2tx text-primary me-4">
                 <span class="path1"></span>
                 <span class="path1"></span>
@@ -29,33 +60,15 @@ explicit grant from the SFTPGo Team ([email protected]).
             <div class="d-flex flex-stack flex-grow-1 flex-wrap flex-md-nowrap">
             <div class="d-flex flex-stack flex-grow-1 flex-wrap flex-md-nowrap">
                 <div class="mb-3 mb-md-0 fw-semibold">
                 <div class="mb-3 mb-md-0 fw-semibold">
                     <h4 class="text-gray-900 fw-bold">
                     <h4 class="text-gray-900 fw-bold">
-                        {{- if .TOTPConfig.Enabled}}
-                        <span data-i18n="2fa.msg_enabled">Two-factor authentication is enabled</span> {{- if gt (len .TOTPConfigs) 1 }} ({{$.TOTPConfig.ConfigName}}) {{- end}}
-                        {{- else}}
                         <span data-i18n="2fa.msg_disabled">Secure Your Account</span>
                         <span data-i18n="2fa.msg_disabled">Secure Your Account</span>
-                        {{- end}}
                     </h4>
                     </h4>
                     <div class="fs-6 text-gray-800 pe-7">
                     <div class="fs-6 text-gray-800 pe-7">
-                        <span data-i18n="2fa.msg_info">
-                            Two-factor authentication adds an extra layer of security to your account. To log in you'll need
-                            to provide an additional authentication code.
-                        </span>
+                        <span data-i18n="2fa.msg_info"></span>
                     </div>
                     </div>
                 </div>
                 </div>
-                {{- if .TOTPConfig.Enabled}}
-                <button type="button" id="disable_btn" class="btn btn-danger ms-4 px-6 align-self-center text-nowrap">
-                    <span data-i18n="general.disable" class="indicator-label">
-                        Disable
-                    </span>
-                    <span data-i18n="general.wait" class="indicator-progress">
-                        Please wait...
-                        <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
-                    </span>
-                </button>
-                {{- end}}
             </div>
             </div>
         </div>
         </div>
-
+        {{- end}}
         <div class="form-group row mt-10">
         <div class="form-group row mt-10">
             <label for="id_config" data-i18n="general.configuration" class="col-md-3 col-form-label">Configuration</label>
             <label for="id_config" data-i18n="general.configuration" class="col-md-3 col-form-label">Configuration</label>
             <div class="col-md-9">
             <div class="col-md-9">
@@ -108,38 +121,40 @@ explicit grant from the SFTPGo Team ([email protected]).
 </div>
 </div>
 
 
 {{- if .TOTPConfig.Enabled}}
 {{- if .TOTPConfig.Enabled}}
-<div class="notice d-flex bg-light-primary rounded border-primary border border-dashed p-6 my-10">
-    <i class="ki-duotone ki-shield-tick fs-2tx text-primary me-4">
-        <span class="path1"></span>
-        <span class="path2"></span>
-    </i>
-    <div class="d-flex flex-stack flex-grow-1 flex-wrap flex-md-nowrap">
-        <div class="mb-3 mb-md-0 fw-semibold">
-            <h4 data-i18n="2fa.recovery_codes" class="text-gray-900 fw-bold">
-                Recovery codes
-            </h4>
-            <div class="fs-6 text-gray-800">
-                <p data-i18n="2fa.recovery_codes_msg1">Recovery codes are a set of one time use codes that can be used in place of the authentication code to login to the web UI. You can use them if you lose access to your phone to login to your account and disable or regenerate two-factor configuration.</p>
-                <p data-i18n="2fa.recovery_codes_msg2">To keep your account secure, don't share or distribute your recovery codes. We recommend saving them with a secure password manager.</p>
-                <p data-i18n="2fa.recovery_codes_msg3">If you generate new recovery codes, you automatically invalidate old ones.</p>
-            </div>
-            <div class="d-flex justify-content-center mt-10">
-                <button type="button" id="generate_recovery_code_btn" class="btn btn-primary px-10 me-10">
-                    <span data-i18n="general.generate" class="indicator-label">
-                        Generate
-                    </span>
-                    <span data-i18n="general.wait" class="indicator-progress">
-                        Please wait...
-                        <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
-                    </span>
-                </button>
-                <button type="button" id="view_recovery_code_btn" class="btn btn-primary px-10">
-                    <span data-i18n="general.view" id="save_label" class="indicator-label">View</span>
-                    <span data-i18n="general.wait" class="indicator-progress">
-                        Please wait...
-                        <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
-                    </span>
-                </button>
+<div class="accordion shadow-sm my-10" id="id_accordion">
+    <div class="accordion-item">
+        <h2 class="accordion-header" id="accordion_rec_codes">
+            <button class="accordion-button section-title text-primary collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#accordion_rec_codes_body" aria-expanded="false" aria-controls="accordion_rec_codes_body">
+                <span data-i18n="2fa.recovery_codes">
+                    Recovery codes
+                </span>
+            </button>
+        </h2>
+        <div id="accordion_rec_codes_body" class="accordion-collapse collapse" aria-labelledby="accordion_rec_codes" data-bs-parent="#id_accordion">
+            <div class="accordion-body">
+                <div class="fs-5 text-gray-800">
+                    <p data-i18n="2fa.recovery_codes_msg1">Recovery codes are a set of one time use codes that can be used in place of the authentication code to login to the web UI. You can use them if you lose access to your phone to login to your account and disable or regenerate two-factor configuration.</p>
+                    <p data-i18n="2fa.recovery_codes_msg2">To keep your account secure, don't share or distribute your recovery codes. We recommend saving them with a secure password manager.</p>
+                    <p data-i18n="2fa.recovery_codes_msg3" class="fs-4 fw-bold">If you generate new recovery codes, you automatically invalidate old ones.</p>
+                </div>
+                <div class="d-flex justify-content-end mt-10">
+                    <button type="button" id="generate_recovery_code_btn" class="btn btn-light-primary px-10 me-10">
+                        <span data-i18n="2fa.recovery_codes_generate" class="indicator-label">
+                            Generate
+                        </span>
+                        <span data-i18n="general.wait" class="indicator-progress">
+                            Please wait...
+                            <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
+                        </span>
+                    </button>
+                    <button type="button" id="view_recovery_code_btn" class="btn btn-primary px-10">
+                        <span data-i18n="2fa.recovery_codes_view" id="save_label" class="indicator-label">View</span>
+                        <span data-i18n="general.wait" class="indicator-progress">
+                            Please wait...
+                            <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
+                        </span>
+                    </button>
+                </div>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>
@@ -165,8 +180,8 @@ explicit grant from the SFTPGo Team ([email protected]).
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
                 <h3 id="idRecoveryCodesTitle" class="modal-title"></h3>
                 <h3 id="idRecoveryCodesTitle" class="modal-title"></h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary ms-2" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
             <div class="modal-body">
             <div class="modal-body">
@@ -185,8 +200,8 @@ explicit grant from the SFTPGo Team ([email protected]).
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
                 <h3 data-i18n="2fa.info_title" class="modal-title">Learn about two-factor authentication</h3>
                 <h3 data-i18n="2fa.info_title" class="modal-title">Learn about two-factor authentication</h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary ms-2" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
             <div class="modal-body fw-semibold fs-6">
             <div class="modal-body fw-semibold fs-6">
@@ -210,8 +225,8 @@ explicit grant from the SFTPGo Team ([email protected]).
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
                 <h3 data-i18n="2fa.setup_title" class="modal-title">Set up two-factor authentication</h3>
                 <h3 data-i18n="2fa.setup_title" class="modal-title">Set up two-factor authentication</h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary ms-2" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
             <div class="modal-body scroll-y pt-10 pb-15 px-lg-17">
             <div class="modal-body scroll-y pt-10 pb-15 px-lg-17">
@@ -223,7 +238,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                 <div id="id_qr_code_container" class="pt-5 text-center">
                 <div id="id_qr_code_container" class="pt-5 text-center">
                 </div>
                 </div>
                 <div class="notice d-flex bg-light-warning rounded border-warning border border-dashed my-10 p-6">
                 <div class="notice d-flex bg-light-warning rounded border-warning border border-dashed my-10 p-6">
-                    <i class="ki-duotone ki-information fs-2tx text-warning me-4">
+                    <i class="ki-duotone ki-information-5 fs-2tx text-warning me-4">
                         <span class="path1"></span>
                         <span class="path1"></span>
                         <span class="path2"></span>
                         <span class="path2"></span>
                         <span class="path3"></span>
                         <span class="path3"></span>
@@ -239,12 +254,12 @@ explicit grant from the SFTPGo Team ([email protected]).
                 </div>
                 </div>
 
 
                 <div id="errorModalMsg" class="d-none rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
                 <div id="errorModalMsg" class="d-none rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
-                    <i class="ki-duotone ki-information fs-3x text-warning me-5"><span class="path1"></span><span class="path2"></span><span class="path3"></span></i>
+                    <i class="ki-duotone ki-information-5 fs-3x text-warning me-5"><span class="path1"></span><span class="path2"></span><span class="path3"></span></i>
                     <div class="text-gray-700 fw-bold fs-5 d-flex flex-column pe-0 pe-sm-10">
                     <div class="text-gray-700 fw-bold fs-5 d-flex flex-column pe-0 pe-sm-10">
                         <span id="errorModalTxt"></span>
                         <span id="errorModalTxt"></span>
                     </div>
                     </div>
                     <button id="id_dismiss_error_modal_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
                     <button id="id_dismiss_error_modal_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
-                        <i class="ki-duotone ki-cross fs-2x text-primary"><span class="path1"></span><span class="path2"></span></i>
+                        <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                     </button>
                     </button>
                 </div>
                 </div>
 
 
@@ -332,7 +347,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                     icon: "warning",
                     icon: "warning",
                     confirmButtonText: $.t('general.ok'),
                     confirmButtonText: $.t('general.ok'),
                     customClass: {
                     customClass: {
-                        confirmButton: "btn btn-danger"
+                        confirmButton: "btn btn-primary"
                     }
                     }
                 });
                 });
             });
             });
@@ -374,13 +389,24 @@ explicit grant from the SFTPGo Team ([email protected]).
                     icon: "warning",
                     icon: "warning",
                     confirmButtonText: $.t('general.ok'),
                     confirmButtonText: $.t('general.ok'),
                     customClass: {
                     customClass: {
-                        confirmButton: "btn btn-danger"
+                        confirmButton: "btn btn-primary"
                     }
                     }
                 });
                 });
             });
             });
     }
     }
 
 
     function disableConfig() {
     function disableConfig() {
+        if (requiredProtocols.length > 0){
+            ModalAlert.fire({
+                text: $.t('2fa.required_protocols', {val: requiredProtocols.join(', ')}),
+                icon: "warning",
+                confirmButtonText: $.t('general.ok'),
+                customClass: {
+                    confirmButton: "btn btn-primary"
+                }
+            });
+            return;
+        }
         ModalAlert.fire({
         ModalAlert.fire({
             text: $.t('2fa.disable_question'),
             text: $.t('2fa.disable_question'),
             icon: "warning",
             icon: "warning",
@@ -469,7 +495,14 @@ explicit grant from the SFTPGo Team ([email protected]).
         }
         }
 
 
         if ($('#id_protocols').find('option:selected').length == 0){
         if ($('#id_protocols').find('option:selected').length == 0){
-            showToast('2fa.no_protocol');
+            ModalAlert.fire({
+                text: $.t('2fa.no_protocol'),
+                icon: "warning",
+                confirmButtonText: $.t('general.ok'),
+                customClass: {
+                    confirmButton: "btn btn-primary"
+                }
+            });
             return;
             return;
         }
         }
 
 
@@ -492,7 +525,14 @@ explicit grant from the SFTPGo Team ([email protected]).
                 el.removeAttribute('data-kt-indicator');
                 el.removeAttribute('data-kt-indicator');
                 el.disabled = false;
                 el.disabled = false;
                 if (!response.data.secret) {
                 if (!response.data.secret) {
-                    showToast(errorMessage);
+                    ModalAlert.fire({
+                        text: $.t(errorMessage),
+                        icon: "warning",
+                        confirmButtonText: $.t('general.ok'),
+                        customClass: {
+                            confirmButton: "btn btn-primary"
+                        }
+                    });
                     return;
                     return;
                 }
                 }
                 $('#id_secret').text(response.data.secret);
                 $('#id_secret').text(response.data.secret);
@@ -509,7 +549,14 @@ explicit grant from the SFTPGo Team ([email protected]).
             }).catch(function (error){
             }).catch(function (error){
                 el.removeAttribute('data-kt-indicator');
                 el.removeAttribute('data-kt-indicator');
                 el.disabled = false;
                 el.disabled = false;
-                showToast(errorMessage);
+                ModalAlert.fire({
+                    text: $.t(errorMessage),
+                    icon: "warning",
+                    confirmButtonText: $.t('general.ok'),
+                    customClass: {
+                        confirmButton: "btn btn-primary"
+                    }
+                });
             });
             });
     }
     }
 
 
@@ -522,16 +569,32 @@ explicit grant from the SFTPGo Team ([email protected]).
         }
         }
         let errorMessage = '2fa.save_err';
         let errorMessage = '2fa.save_err';
         let protocolsArray = [];
         let protocolsArray = [];
-        $('#id_protocols').find('option:selected').each(function(){
-            protocolsArray.push($(this).val());
-        });
-        if (protocolsArray.length == 0){
-            showToast('2fa.no_protocol');
-            return;
+        if (!disabled) {
+            $('#id_protocols').find('option:selected').each(function(){
+                protocolsArray.push($(this).val());
+            });
+            if (protocolsArray.length == 0){
+                ModalAlert.fire({
+                    text: $.t('2fa.no_protocol'),
+                    icon: "warning",
+                    confirmButtonText: $.t('general.ok'),
+                    customClass: {
+                        confirmButton: "btn btn-primary"
+                    }
+                });
+                return;
+            }
         }
         }
         for (let i = 0; i < requiredProtocols.length > 0; i++){
         for (let i = 0; i < requiredProtocols.length > 0; i++){
             if (!protocolsArray.includes(requiredProtocols[i])){
             if (!protocolsArray.includes(requiredProtocols[i])){
-                showToast('2fa.required_protocols', {val: requiredProtocols.join(', ')});
+                ModalAlert.fire({
+                    text: $.t('2fa.required_protocols', {val: requiredProtocols.join(', ')}),
+                    icon: "warning",
+                    confirmButtonText: $.t('general.ok'),
+                    customClass: {
+                        confirmButton: "btn btn-primary"
+                    }
+                });
                 return;
                 return;
             }
             }
         }
         }
@@ -565,7 +628,14 @@ explicit grant from the SFTPGo Team ([email protected]).
                 el.removeAttribute('data-kt-indicator');
                 el.removeAttribute('data-kt-indicator');
                 el.disabled = false;
                 el.disabled = false;
                 if (!response.data.message) {
                 if (!response.data.message) {
-                    showToast(errorMessage);
+                    ModalAlert.fire({
+                        text: $.t(errorMessage),
+                        icon: "warning",
+                        confirmButtonText: $.t('general.ok'),
+                        customClass: {
+                            confirmButton: "btn btn-primary"
+                        }
+                    });
                     return;
                     return;
                 }
                 }
                 ModalAlert.fire({
                 ModalAlert.fire({
@@ -583,7 +653,14 @@ explicit grant from the SFTPGo Team ([email protected]).
             }).catch(function (error) {
             }).catch(function (error) {
                 el.removeAttribute('data-kt-indicator');
                 el.removeAttribute('data-kt-indicator');
                 el.disabled = false;
                 el.disabled = false;
-                showToast(errorMessage);
+                ModalAlert.fire({
+                    text: $.t(errorMessage),
+                    icon: "warning",
+                    confirmButtonText: $.t('general.ok'),
+                    customClass: {
+                        confirmButton: "btn btn-primary"
+                    }
+                });
             });
             });
 
 
     }
     }

+ 2 - 2
templates/webclient/profile.html

@@ -18,7 +18,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 {{- define "page_body"}}
 {{- define "page_body"}}
 <div class="card shadow-sm">
 <div class="card shadow-sm">
     <div class="card-header bg-light">
     <div class="card-header bg-light">
-        <h3 data-i18n="general.my_profile" class="card-title text-primary">My profile</h3>
+        <h3 data-i18n="general.my_profile" class="card-title section-title">My profile</h3>
     </div>
     </div>
     <div class="card-body">
     <div class="card-body">
         {{- template "errmsg" .Error}}
         {{- template "errmsg" .Error}}
@@ -54,7 +54,7 @@ explicit grant from the SFTPGo Team ([email protected]).
             {{- if .LoggedUser.CanManagePublicKeys}}
             {{- if .LoggedUser.CanManagePublicKeys}}
             <div class="card card-rounded mt-10">
             <div class="card card-rounded mt-10">
                 <div class="card-header bg-light">
                 <div class="card-header bg-light">
-                    <h3 data-i18n="general.pub_keys" class="card-title">Public keys</h3>
+                    <h3 data-i18n="general.pub_keys" class="card-title section-title-inner">Public keys</h3>
                 </div>
                 </div>
                 <div class="card-body">
                 <div class="card-body">
                     <div id="public_keys">
                     <div id="public_keys">

+ 34 - 2
templates/webclient/reset-password.html

@@ -46,10 +46,42 @@ explicit grant from the SFTPGo Team ([email protected]).
         <input data-i18n="[placeholder]login.confirm_code" class="form-control form-control-lg form-control-solid" type="text" placeholder="Confirmation code" name="code" spellcheck="false" required />
         <input data-i18n="[placeholder]login.confirm_code" class="form-control form-control-lg form-control-solid" type="text" placeholder="Confirmation code" name="code" spellcheck="false" required />
     </div>
     </div>
     <div class="fv-row mb-10">
     <div class="fv-row mb-10">
-        <input data-i18n="[placeholder]general.new_password" class="form-control form-control-lg form-control-solid" type="password" name="password" placeholder="New Password" autocomplete="new-password" spellcheck="false" required />
+        <div class="position-relative" data-password-control="container">
+            <input data-i18n="[placeholder]general.new_password" data-password-control="input" class="form-control form-control-lg form-control-solid"
+                type="password" name="password" placeholder="New Password" autocomplete="new-password" spellcheck="false" required />
+            <span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                <i class="ki-duotone ki-eye-slash fs-1">
+                    <span class="path1"></span>
+                    <span class="path2"></span>
+                    <span class="path3"></span>
+                    <span class="path4"></span>
+                </i>
+                <i class="ki-duotone ki-eye d-none fs-1">
+                    <span class="path1"></span>
+                    <span class="path2"></span>
+                    <span class="path3"></span>
+                </i>
+            </span>
+        </div>
     </div>
     </div>
     <div class="fv-row mb-10">
     <div class="fv-row mb-10">
-        <input data-i18n="[placeholder]general.confirm_password" class="form-control form-control-lg form-control-solid" type="password" name="confirm_password" placeholder="Confirm Password" autocomplete="new-password" spellcheck="false" required />
+        <div class="position-relative" data-password-control="container">
+            <input data-i18n="[placeholder]general.confirm_password" data-password-control="input" class="form-control form-control-lg form-control-solid"
+                type="password" name="confirm_password" placeholder="Confirm Password" autocomplete="new-password" spellcheck="false" required />
+            <span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                <i class="ki-duotone ki-eye-slash fs-1">
+                    <span class="path1"></span>
+                    <span class="path2"></span>
+                    <span class="path3"></span>
+                    <span class="path4"></span>
+                </i>
+                <i class="ki-duotone ki-eye d-none fs-1">
+                    <span class="path1"></span>
+                    <span class="path2"></span>
+                    <span class="path3"></span>
+                </i>
+            </span>
+        </div>
     </div>
     </div>
     <div class="text-center">
     <div class="text-center">
         <input type="hidden" name="_form_token" value="{{.CSRFToken}}">
         <input type="hidden" name="_form_token" value="{{.CSRFToken}}">

+ 4 - 7
templates/webclient/share.html

@@ -18,7 +18,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 {{- define "page_body"}}
 {{- define "page_body"}}
 <div class="card shadow-sm">
 <div class="card shadow-sm">
     <div class="card-header bg-light">
     <div class="card-header bg-light">
-        <h3 {{if .IsAdd}}data-i18n="title.add_share"{{else}}data-i18n="title.update_share"{{end}} class="card-title text-primary"></h3>
+        <h3 {{if .IsAdd}}data-i18n="title.add_share"{{else}}data-i18n="title.update_share"{{end}} class="card-title section-title"></h3>
     </div>
     </div>
     <div class="card-body">
     <div class="card-body">
         {{- template "errmsg" .Error}}
         {{- template "errmsg" .Error}}
@@ -47,7 +47,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
             <div class="card card-rounded mt-10">
             <div class="card card-rounded mt-10">
                 <div class="card-header bg-light">
                 <div class="card-header bg-light">
-                    <h3 data-i18n="share.paths" class="card-title">Paths</h3>
+                    <h3 data-i18n="share.paths" class="card-title section-title-inner">Paths</h3>
                 </div>
                 </div>
                 <div class="card-body">
                 <div class="card-body">
                     <div id="paths">
                     <div id="paths">
@@ -127,10 +127,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                 <div class="col-md-9 d-flex">
                 <div class="col-md-9 d-flex">
                     <input data-i18n="[placeholder]share.expiration_help" id="id_expiration" class="form-control" placeholder="Pick an expiration date" />
                     <input data-i18n="[placeholder]share.expiration_help" id="id_expiration" class="form-control" placeholder="Pick an expiration date" />
                     <button class="btn btn-icon btn-light-danger ms-2 d-none" id="id_expiration_clear">
                     <button class="btn btn-icon btn-light-danger ms-2 d-none" id="id_expiration_clear">
-                        <i class="ki-duotone ki-cross fs-1">
-                            <span class="path1"></span>
-                            <span class="path2"></span>
-                        </i>
+                        <i class="ki-solid ki-cross fs-1"></i>
                     </button>
                     </button>
                 </div>
                 </div>
             </div>
             </div>
@@ -203,7 +200,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                 },
                 },
                 defaultHour: 23,
                 defaultHour: 23,
                 defaultMinute: 59,
                 defaultMinute: 59,
-                locale: i18next.language,
+                locale: i18next.resolvedLanguage,
                 onChange: function(selectedDates, dateStr, instance) {
                 onChange: function(selectedDates, dateStr, instance) {
                     if (selectedDates.length > 0){
                     if (selectedDates.length > 0){
                         $('#id_expiration_clear').removeClass("d-none");
                         $('#id_expiration_clear').removeClass("d-none");

+ 8 - 2
templates/webclient/sharedownload.html

@@ -27,11 +27,17 @@ explicit grant from the SFTPGo Team ([email protected]).
     </div>
     </div>
     <div class="card shadow-sm w-lg-600px">
     <div class="card shadow-sm w-lg-600px">
         <div class="card-header bg-light">
         <div class="card-header bg-light">
-            <h3 data-i18n="fs.download_ready" class="card-title text-primary">Your download is ready</h3>
+            <h3 data-i18n="fs.download_ready" class="card-title section-title">Your download is ready</h3>
         </div>
         </div>
         <div class="card-body">
         <div class="card-body">
             <div>
             <div>
-                <a data-i18n="fs.download" href="{{.DownloadLink}}" class="btn btn-primary btn-user-custom btn-block">Download</a>
+                <a href="{{.DownloadLink}}" class="btn btn-primary btn-block">
+                    <i class="ki-duotone ki-folder-down fs-2">
+                        <span class="path1"></span>
+                        <span class="path2"></span>
+                    </i>
+                    <span data-i18n="fs.download">Download</span>
+                </a>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>

+ 18 - 1
templates/webclient/sharelogin.html

@@ -31,7 +31,23 @@ explicit grant from the SFTPGo Team ([email protected]).
     </div>
     </div>
     {{- template "errmsg" .Error}}
     {{- template "errmsg" .Error}}
     <div class="fv-row mb-10">
     <div class="fv-row mb-10">
-        <input data-i18n="[placeholder]login.password" class="form-control form-control-lg form-control-solid" type="password" name="share_password" placeholder="Password" spellcheck="false" required />
+        <div class="position-relative" data-password-control="container">
+            <input data-i18n="[placeholder]login.password" data-password-control="input" class="form-control form-control-lg form-control-solid"
+                type="password" name="share_password" placeholder="Password" spellcheck="false" required />
+            <span class="btn btn-sm btn-icon position-absolute translate-middle top-50 end-0 me-n2" data-password-control="visibility">
+                <i class="ki-duotone ki-eye-slash fs-1">
+                    <span class="path1"></span>
+                    <span class="path2"></span>
+                    <span class="path3"></span>
+                    <span class="path4"></span>
+                </i>
+                <i class="ki-duotone ki-eye d-none fs-1">
+                    <span class="path1"></span>
+                    <span class="path2"></span>
+                    <span class="path3"></span>
+                </i>
+            </span>
+        </div>
     </div>
     </div>
     <div class="text-center">
     <div class="text-center">
         <input type="hidden" name="_form_token" value="{{.CSRFToken}}">
         <input type="hidden" name="_form_token" value="{{.CSRFToken}}">
@@ -44,6 +60,7 @@ explicit grant from the SFTPGo Team ([email protected]).
         </button>
         </button>
     </div>
     </div>
 </form>
 </form>
+<hr>
 <div class=" d-flex flex-stack pt-5 mt-5">
 <div class=" d-flex flex-stack pt-5 mt-5">
     <div class="me-10">
     <div class="me-10">
         <select id="languageSwitcher" name="language" class="form-select form-select-solid form-select-sm" data-control="i18n-select2" data-hide-search="true">
         <select id="languageSwitcher" name="language" class="form-select form-select-solid form-select-sm" data-control="i18n-select2" data-hide-search="true">

+ 101 - 16
templates/webclient/shares.html

@@ -24,7 +24,7 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
 <div class="card shadow-sm">
 <div class="card shadow-sm">
     <div class="card-header bg-light">
     <div class="card-header bg-light">
-        <h3 data-i18n="share.view_manage" class="card-title text-primary">View and manage shares</h3>
+        <h3 data-i18n="share.view_manage" class="card-title section-title">View and manage shares</h3>
     </div>
     </div>
     <div id="card_body" class="card-body">
     <div id="card_body" class="card-body">
         <div id="loader" class="align-items-center text-center my-10">
         <div id="loader" class="align-items-center text-center my-10">
@@ -39,7 +39,7 @@ explicit grant from the SFTPGo Team ([email protected]).
                         <span class="path2"></span>
                         <span class="path2"></span>
                     </i>
                     </i>
                     <input name="search" data-i18n="[placeholder]general.search" type="text" data-share-table-filter="search"
                     <input name="search" data-i18n="[placeholder]general.search" type="text" data-share-table-filter="search"
-                        class="form-control rounded-1 w-200px ps-15" placeholder="Search" />
+                        class="form-control rounded-1 w-250px ps-15 me-5" placeholder="Search" />
                 </div>
                 </div>
 
 
                 <div class="d-flex justify-content-end" data-share-table-toolbar="base">
                 <div class="d-flex justify-content-end" data-share-table-toolbar="base">
@@ -71,31 +71,95 @@ explicit grant from the SFTPGo Team ([email protected]).
     <div class="modal-dialog" role="document">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header border-0">
             <div class="modal-header border-0">
-                <h3 class="modal-title">
+                <h3 data-i18n="share.access_links_title" class="modal-title">
                     Share access links
                     Share access links
                 </h3>
                 </h3>
-                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
-                    <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
+                <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
+                    <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
                 </div>
                 </div>
             </div>
             </div>
             <div class="modal-body fs-5">
             <div class="modal-body fs-5">
                 <div id="readShare">
                 <div id="readShare">
-                    <p>You can download the shared contents, as single zip file, using this <a id="readLink" href="#" target="_blank">link</a>.</p>
-                    <p>If the share consists of a single directory you can browse and download files using this <a id="readBrowseLink" href="#" target="_blank">page</a>.</p>
-                    <p>If the share consists of a single file you can download it uncompressed using this <a id="readUncompressedLink" href="#" target="_blank">link</a>.</p>
+                    <div class="mb-5">
+                        <h4 data-i18n="share.link_single_title">Single zip file</h4>
+                        <p data-i18n="share.link_single_desc">You can download shared content as a single zip file</p>
+                        <div class="d-flex">
+                            <button id="readLinkCopy" type="button" class="btn btn-flex btn-light-primary btn-clipboard-copy me-3">
+                                <i class="ki-duotone ki-fasten fs-2">
+                                    <span class="path1"></span>
+                                    <span class="path2"></span>
+                                </i>
+                                <span data-i18n="general.copy_link">Copy link</span>
+                            </button>
+                            <a id="readLink" href="#" target="_blank" type="button" class="btn btn-flex btn-primary">
+                                <i class="ki-duotone ki-folder-down fs-2">
+                                    <span class="path1"></span>
+                                    <span class="path2"></span>
+                                </i>
+                                <span data-i18n="fs.download">Download</span>
+                            </a>
+                        </div>
+                    </div>
+                    <hr>
+                    <div class="mb-5">
+                        <h4 data-i18n="share.link_dir_title">Single directory</h4>
+                        <p data-i18n="share.link_dir_desc">If the share consists of a single directory you can browse and download files</p>
+                        <button id="readBrowseLinkCopy" data-clipboard-target="#readBrowseLink" type="button" class="btn btn-flex btn-light-primary btn-clipboard-copy me-3">
+                            <i class="ki-duotone ki-fasten fs-2">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                            </i>
+                            <span data-i18n="general.copy_link">Copy link</span>
+                        </button>
+                        <a id="readBrowseLink" href="#" target="_blank" type="button" class="btn btn-flex btn-primary">
+                            <i class="ki-duotone ki-arrow-up-right fs-2">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                            </i>
+                            <span data-i18n="share.go">Go to share</span>
+                        </a>
+                    </div>
+                    <hr>
+                    <div>
+                        <h4 data-i18n="share.link_uncompressed_title">Uncompressed file</h4>
+                        <p data-i18n="share.link_uncompressed_desc">If the share consists of a single file you can download it uncompressed</p>
+                        <button id="readUncompressedLinkCopy"  data-clipboard-target="#readUncompressedLink" type="button" class="btn btn-flex btn-light-primary btn-clipboard-copy me-3">
+                            <i class="ki-duotone ki-fasten fs-2">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                            </i>
+                            <span data-i18n="general.copy_link">Copy link</span>
+                        </button>
+                        <a id="readUncompressedLink" href="#" target="_blank" type="button" class="btn btn-flex btn-primary">
+                            <i class="ki-duotone ki-folder-down fs-2">
+                                <span class="path1"></span>
+                                <span class="path2"></span>
+                            </i>
+                            <span data-i18n="fs.download">Download</span>
+                        </a>
+                    </div>
                 </div>
                 </div>
                 <div id="writeShare">
                 <div id="writeShare">
-                    <p>You can upload one or more files to the shared directory using this <a id="writePageLink" href="#" target="_blank">page</a></p>
+                    <p data-i18n="share.upload_desc">You can upload one or more files to the shared directory</p>
+                    <button id="writePageLinkCopy"  data-clipboard-target="#writePageLink" type="button" class="btn btn-flex btn-light-primary btn-clipboard-copy me-3">
+                        <i class="ki-duotone ki-fasten fs-2">
+                            <span class="path1"></span>
+                            <span class="path2"></span>
+                        </i>
+                        <span data-i18n="general.copy_link">Copy link</span>
+                    </button>
+                    <a id="writePageLink" href="#" target="_blank" type="button" class="btn btn-flex btn-primary">
+                        <i class="ki-duotone ki-folder-up fs-2">
+                            <span class="path1"></span>
+                            <span class="path2"></span>
+                        </i>
+                        <span data-i18n="fs.upload.text">Upload</span>
+                    </a>
                 </div>
                 </div>
-                <div id="expiredShare">
+                <div data-i18n="share.expired_desc" id="expiredShare">
                     This share is no longer accessible because it has expired
                     This share is no longer accessible because it has expired
                 </div>
                 </div>
             </div>
             </div>
-            <div class="modal-footer border-0">
-                <button class="btn btn-primary" type="button" data-bs-dismiss="modal">
-                    OK
-                </button>
-            </div>
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
@@ -109,7 +173,7 @@ explicit grant from the SFTPGo Team ([email protected]).
         ModalAlert.fire({
         ModalAlert.fire({
             text: $.t('general.delete_confirm_generic'),
             text: $.t('general.delete_confirm_generic'),
             icon: "warning",
             icon: "warning",
-            confirmButtonText: $.t('general.delete'),
+            confirmButtonText: $.t('general.delete_confirm_btn'),
             cancelButtonText: $.t('general.cancel'),
             cancelButtonText: $.t('general.cancel'),
             customClass: {
             customClass: {
                 confirmButton: "btn btn-danger",
                 confirmButton: "btn btn-danger",
@@ -170,16 +234,20 @@ explicit grant from the SFTPGo Team ([email protected]).
                 $('#readShare').show();
                 $('#readShare').show();
                 $('#readLink').attr("href", shareURL + "/download");
                 $('#readLink').attr("href", shareURL + "/download");
                 $('#readLink').attr("title", shareURL + "/download");
                 $('#readLink').attr("title", shareURL + "/download");
+                $('#readLinkCopy').attr("data-clipboard-text",getCurrentURI()+shareURL + "/download");
                 $('#readUncompressedLink').attr("href", shareURL + "/download?compress=false");
                 $('#readUncompressedLink').attr("href", shareURL + "/download?compress=false");
                 $('#readUncompressedLink').attr("title", shareURL + "/download?compress=false");
                 $('#readUncompressedLink').attr("title", shareURL + "/download?compress=false");
+                $('#readUncompressedLinkCopy').attr("data-clipboard-text",getCurrentURI()+shareURL + "/download?compress=false");
                 $('#readBrowseLink').attr("href", shareURL + "/browse");
                 $('#readBrowseLink').attr("href", shareURL + "/browse");
                 $('#readBrowseLink').attr("title", shareURL + "/browse");
                 $('#readBrowseLink').attr("title", shareURL + "/browse");
+                $('#readBrowseLinkCopy').attr("data-clipboard-text",getCurrentURI()+shareURL + "/browse");
             } else {
             } else {
                 $('#expiredShare').hide();
                 $('#expiredShare').hide();
                 $('#writeShare').show();
                 $('#writeShare').show();
                 $('#readShare').hide();
                 $('#readShare').hide();
                 $('#writePageLink').attr("href", shareURL + "/upload");
                 $('#writePageLink').attr("href", shareURL + "/upload");
                 $('#writePageLink').attr("title", shareURL + "/upload");
                 $('#writePageLink').attr("title", shareURL + "/upload");
+                $('#writePageLinkCopy').attr("data-clipboard-text",getCurrentURI()+shareURL + "/upload");
             }
             }
         }
         }
         $('#link_modal').modal('show');
         $('#link_modal').modal('show');
@@ -386,6 +454,23 @@ explicit grant from the SFTPGo Team ([email protected]).
 
 
     $(document).on("i18nshow", function(){
     $(document).on("i18nshow", function(){
         sharesDatatable.init();
         sharesDatatable.init();
+
+        var clipboard = new ClipboardJS('.btn-clipboard-copy',{
+            container: document.getElementById('link_modal')
+        });
+
+        clipboard.on('success', function (e) {
+            e.trigger.querySelectorAll('span').forEach(spanEl => {
+                if (spanEl.getAttribute('data-i18n')){
+                    setI18NData($(spanEl),"general.copied");
+                    setTimeout(function(){
+                        setI18NData($(spanEl),"general.copy_link");
+                    }, 3000)
+                }
+            });
+            e.clearSelection();
+        });
+
     });
     });
 </script>
 </script>
 {{end}}
 {{end}}

+ 1 - 1
templates/webclient/shareupload.html

@@ -27,7 +27,7 @@ explicit grant from the SFTPGo Team ([email protected]).
     </div>
     </div>
     <div class="card shadow-sm w-lg-600px">
     <div class="card shadow-sm w-lg-600px">
         <div class="card-header bg-light">
         <div class="card-header bg-light">
-            <h3 data-i18n="title.upload_to_share" class="card-title text-primary">Upload one or more files to share</h3>
+            <h3 data-i18n="title.upload_to_share" class="card-title section-title">Upload one or more files to share</h3>
         </div>
         </div>
         <div class="card-body">
         <div class="card-body">
             {{- template "errmsg" ""}}
             {{- template "errmsg" ""}}

Some files were not shown because too many files changed in this diff