From bf94f8b87c6ee683f97d11206ac832ddc02d6be3 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Sun, 14 Jan 2024 09:09:42 +0100 Subject: [PATCH] WIP new WebAdmin: group page Signed-off-by: Nicola Murino --- README.md | 2 + go.mod | 15 +- go.sum | 34 +- internal/httpd/web.go | 8 + internal/httpd/webadmin.go | 45 +- internal/util/i18n.go | 2 + static/locales/en/translation.json | 10 +- static/locales/it/translation.json | 10 +- templates/webadmin/fsconfig.html | 354 +++++++++ templates/webadmin/group.html | 1097 ++++++++++------------------ templates/webadmin/user.html | 358 +-------- 11 files changed, 815 insertions(+), 1120 deletions(-) diff --git a/README.md b/README.md index f32abf72..5d3cea29 100644 --- a/README.md +++ b/README.md @@ -370,6 +370,8 @@ Thank you to [ysura](https://www.ysura.com/) for granting us stable access to a Thank you to [KeenThemes](https://keenthemes.com/) for granting us a custom license to use their amazing [Mega Bundle](https://keenthemes.com/products/templates-mega-bundle) for SFTPGo UI. +Thank you to [Incode](https://www.incode.it/) for helping us to improve the UI/UX. + ## License GNU AGPL-3.0-only diff --git a/go.mod b/go.mod index 447b7a39..1fc0f0d5 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.1 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/cockroachdb/cockroach-go/v2 v2.3.5 @@ -36,7 +36,7 @@ require ( github.com/hashicorp/go-hclog v1.6.2 github.com/hashicorp/go-plugin v1.6.0 github.com/hashicorp/go-retryablehttp v0.7.5 - github.com/jackc/pgx/v5 v5.5.1 + github.com/jackc/pgx/v5 v5.5.2 github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126 github.com/klauspost/compress v1.17.4 github.com/lestrrat-go/jwx/v2 v2.0.19 @@ -74,12 +74,12 @@ require ( golang.org/x/sys v0.16.0 golang.org/x/term v0.16.0 golang.org/x/time v0.5.0 - google.golang.org/api v0.155.0 + google.golang.org/api v0.156.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) require ( - cloud.google.com/go v0.111.0 // indirect + cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect @@ -136,7 +136,6 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.57 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -145,7 +144,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common v0.46.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -165,11 +164,11 @@ require ( go.opentelemetry.io/otel/metric v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.17.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240108191215-35c7eff3a6b1 // indirect diff --git a/go.sum b/go.sum index a78035e3..f9877eb2 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= @@ -65,8 +65,8 @@ github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6 h1:JWy+uLKZQR/9 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6/go.mod h1:T2NcfuIuXWcuwVwg3rBIW6h1cfzCdrzSn4Hs0KltND8= github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflYDHFoZAu/sKYkwU= github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.1 h1:Sn3MAV9YeACCULaxNWWYFH1a6G4wYFwBn3/TA5MwE2Q= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.1/go.mod h1:qutL00aW8GSo2D0I6UEOqMvRS3ZyuBrOC1BLe5D2jPc= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2 h1:A5sGOT/mukuU+4At1vkSIWAN8tPwPCoYZBp7aruR540= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2/go.mod h1:qutL00aW8GSo2D0I6UEOqMvRS3ZyuBrOC1BLe5D2jPc= github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 h1:dGrs+Q/WzhsiUKh82SfTVN66QzyulXuMDTV/G8ZxOac= github.com/aws/aws-sdk-go-v2/service/sso v1.18.6/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 h1:Yf2MIo9x+0tyv76GljxzqA3WtC5mw7NmazD2chwjxE4= @@ -235,8 +235,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI= -github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA= +github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -288,8 +288,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mhale/smtpd v0.8.1 h1:O02u8O3eYAGxZCGf4E98WjyB+rA3DVFZtchEialjX4s= github.com/mhale/smtpd v0.8.1/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= @@ -330,8 +328,8 @@ github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlk github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -419,8 +417,8 @@ go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= @@ -430,8 +428,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus= gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE= -golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -516,16 +514,16 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= -google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= +google.golang.org/api v0.156.0 h1:yloYcGbBtVYjLKQe4enCunxvwn3s2w/XPrrhVf6MsvQ= +google.golang.org/api v0.156.0/go.mod h1:bUSmn4KFO0Q+69zo9CNIDp4Psi6BqM0np0CbzKRSiSY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= diff --git a/internal/httpd/web.go b/internal/httpd/web.go index 859acd7f..0b06c2ac 100644 --- a/internal/httpd/web.go +++ b/internal/httpd/web.go @@ -152,6 +152,14 @@ func getI18NErrorString(err error, fallback string) string { return fallback } +func getI18nError(err error) *util.I18nError { + var errI18n *util.I18nError + if err != nil { + errI18n = util.NewI18nError(err, util.I18nError500Message) + } + return errI18n +} + func handlePingRequest(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) render.PlainText(w, r, "PONG") diff --git a/internal/httpd/webadmin.go b/internal/httpd/webadmin.go index 9ef67d13..aed7e5b0 100644 --- a/internal/httpd/webadmin.go +++ b/internal/httpd/webadmin.go @@ -316,7 +316,7 @@ type folderPage struct { type groupPage struct { basePage Group *dataprovider.Group - Error string + Error *util.I18nError Mode genericPageMode ValidPerms []string ValidLoginMethods []string @@ -447,10 +447,9 @@ func loadAdminTemplates(templatesPath string) { filepath.Join(templatesPath, templateAdminDir, templateGroups), } groupPaths := []string{ - filepath.Join(templatesPath, templateCommonDir, templateCommonCSS), + filepath.Join(templatesPath, templateCommonDir, templateCommonBase), filepath.Join(templatesPath, templateAdminDir, templateBase), filepath.Join(templatesPath, templateAdminDir, templateFsConfig), - filepath.Join(templatesPath, templateAdminDir, templateSharedComponents), filepath.Join(templatesPath, templateAdminDir, templateGroup), } eventRulesPaths := []string{ @@ -993,14 +992,10 @@ func (s *httpdServer) renderUserPage(w http.ResponseWriter, r *http.Request, use if errGroups != nil { return } - var errI18n *util.I18nError - if err != nil { - errI18n = util.NewI18nError(err, util.I18nError500Message) - } data := userPage{ basePage: basePage, Mode: mode, - Error: errI18n, + Error: getI18nError(err), User: user, ValidPerms: dataprovider.ValidPerms, ValidLoginMethods: dataprovider.ValidLoginMethods, @@ -1067,10 +1062,10 @@ func (s *httpdServer) renderRolePage(w http.ResponseWriter, r *http.Request, rol } func (s *httpdServer) renderGroupPage(w http.ResponseWriter, r *http.Request, group dataprovider.Group, - mode genericPageMode, error string, + mode genericPageMode, err error, ) { - folders, err := s.getWebVirtualFolders(w, r, defaultQueryLimit, true) - if err != nil { + folders, errFolders := s.getWebVirtualFolders(w, r, defaultQueryLimit, true) + if errFolders != nil { return } group.SetEmptySecretsIfNil() @@ -1078,10 +1073,10 @@ func (s *httpdServer) renderGroupPage(w http.ResponseWriter, r *http.Request, gr var title, currentURL string switch mode { case genericPageModeAdd: - title = "Add a new group" + title = util.I18nAddGroupTitle currentURL = webGroupPath case genericPageModeUpdate: - title = "Update group" + title = util.I18nUpdateGroupTitle currentURL = fmt.Sprintf("%v/%v", webGroupPath, url.PathEscape(group.Name)) } group.UserSettings.FsConfig.RedactedSecret = redactedSecret @@ -1089,7 +1084,7 @@ func (s *httpdServer) renderGroupPage(w http.ResponseWriter, r *http.Request, gr data := groupPage{ basePage: s.getBasePageData(title, currentURL, r), - Error: error, + Error: getI18nError(err), Group: &group, Mode: mode, ValidPerms: dataprovider.ValidPerms, @@ -1977,7 +1972,7 @@ func getQuotaLimits(r *http.Request) (int64, int, error) { return quotaSize, quotaFiles, nil } -func updateUserFormFields(r *http.Request) { +func updateRepeaterFormFields(r *http.Request) { for k := range r.Form { if hasPrefixAndSuffix(k, "public_keys[", "][public_key]") { r.Form.Add("public_keys", r.Form.Get(k)) @@ -2029,7 +2024,9 @@ func getUserFromPostFields(r *http.Request) (dataprovider.User, error) { return user, util.NewI18nError(err, util.I18nErrorInvalidForm) } defer r.MultipartForm.RemoveAll() //nolint:errcheck - updateUserFormFields(r) + + updateRepeaterFormFields(r) + uid, err := strconv.Atoi(r.Form.Get("uid")) if err != nil { return user, fmt.Errorf("invalid uid: %w", err) @@ -2118,10 +2115,12 @@ func getGroupFromPostFields(r *http.Request) (dataprovider.Group, error) { group := dataprovider.Group{} err := r.ParseMultipartForm(maxRequestSize) if err != nil { - return group, err + return group, util.NewI18nError(err, util.I18nErrorInvalidForm) } defer r.MultipartForm.RemoveAll() //nolint:errcheck + updateRepeaterFormFields(r) + maxSessions, err := strconv.Atoi(r.Form.Get("max_sessions")) if err != nil { return group, fmt.Errorf("invalid max sessions: %w", err) @@ -3536,7 +3535,7 @@ func (s *httpdServer) handleWebGetGroups(w http.ResponseWriter, r *http.Request) func (s *httpdServer) handleWebAddGroupGet(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) - s.renderGroupPage(w, r, dataprovider.Group{}, genericPageModeAdd, "") + s.renderGroupPage(w, r, dataprovider.Group{}, genericPageModeAdd, nil) } func (s *httpdServer) handleWebAddGroupPost(w http.ResponseWriter, r *http.Request) { @@ -3548,7 +3547,7 @@ func (s *httpdServer) handleWebAddGroupPost(w http.ResponseWriter, r *http.Reque } group, err := getGroupFromPostFields(r) if err != nil { - s.renderGroupPage(w, r, group, genericPageModeAdd, err.Error()) + s.renderGroupPage(w, r, group, genericPageModeAdd, err) return } ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr) @@ -3558,7 +3557,7 @@ func (s *httpdServer) handleWebAddGroupPost(w http.ResponseWriter, r *http.Reque } err = dataprovider.AddGroup(&group, claims.Username, ipAddr, claims.Role) if err != nil { - s.renderGroupPage(w, r, group, genericPageModeAdd, err.Error()) + s.renderGroupPage(w, r, group, genericPageModeAdd, err) return } http.Redirect(w, r, webGroupsPath, http.StatusSeeOther) @@ -3569,7 +3568,7 @@ func (s *httpdServer) handleWebUpdateGroupGet(w http.ResponseWriter, r *http.Req name := getURLParam(r, "name") group, err := dataprovider.GroupExists(name) if err == nil { - s.renderGroupPage(w, r, group, genericPageModeUpdate, "") + s.renderGroupPage(w, r, group, genericPageModeUpdate, nil) } else if errors.Is(err, util.ErrNotFound) { s.renderNotFoundPage(w, r, err) } else { @@ -3595,7 +3594,7 @@ func (s *httpdServer) handleWebUpdateGroupPost(w http.ResponseWriter, r *http.Re } updatedGroup, err := getGroupFromPostFields(r) if err != nil { - s.renderGroupPage(w, r, group, genericPageModeUpdate, err.Error()) + s.renderGroupPage(w, r, group, genericPageModeUpdate, err) return } ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr) @@ -3616,7 +3615,7 @@ func (s *httpdServer) handleWebUpdateGroupPost(w http.ResponseWriter, r *http.Re err = dataprovider.UpdateGroup(&updatedGroup, group.Users, claims.Username, ipAddr, claims.Role) if err != nil { - s.renderGroupPage(w, r, updatedGroup, genericPageModeUpdate, err.Error()) + s.renderGroupPage(w, r, updatedGroup, genericPageModeUpdate, err) return } http.Redirect(w, r, webGroupsPath, http.StatusSeeOther) diff --git a/internal/util/i18n.go b/internal/util/i18n.go index 1c2e1a73..a25eb54f 100644 --- a/internal/util/i18n.go +++ b/internal/util/i18n.go @@ -180,6 +180,8 @@ const ( I18nErrorEndpointInvalid = "storage.endpoint_invalid" I18nErrorEndpointRequired = "storage.endpoint_required" I18nErrorFsUsernameRequired = "storage.username_required" + I18nAddGroupTitle = "title.add_group" + I18nUpdateGroupTitle = "title.update_group" ) // NewI18nError returns a I18nError wrappring the provided error diff --git a/static/locales/en/translation.json b/static/locales/en/translation.json index 15ed24c3..828ff53b 100644 --- a/static/locales/en/translation.json +++ b/static/locales/en/translation.json @@ -47,7 +47,9 @@ "status": "Status", "add_user": "Add user", "update_user": "Update user", - "template_user": "User template" + "template_user": "User template", + "add_group": "Add group", + "update_group": "Update group" }, "setup": { "desc": "To start using SFTPGo you need to create an administrator user", @@ -462,7 +464,9 @@ "disconnect_help": "This way you force the user to login again, if connected, and so to use the new configuration", "submit_generate": "Generate and save users", "submit_export": "Generate and export users", - "invalid_quota_size": "Invalid quota size" + "invalid_quota_size": "Invalid quota size", + "expires_in": "Expires in", + "expires_in_help": "Account expiration as number of days from the creation. 0 means no expiration" }, "group": { "view_manage": "View and manage groups", @@ -621,7 +625,7 @@ "is_anonymous_help": "Anonymous users are supported for FTP and WebDAV protocols and have read-only access", "disable_fs_checks": "Disable filesystem checks", "disable_fs_checks_help": "Disable checks for existence and automatic creation of home directory and virtual folders", - "api_key_auth_help": "Allow to impersonate this user, in REST API, with an API key", + "api_key_auth_help": "Allow to impersonate the user, in REST API, with an API key", "external_auth_cache_time": "External auth cache time", "external_auth_cache_time_help": "Cache time, in seconds, for users authenticated using an external auth hook. 0 means no cache" } diff --git a/static/locales/it/translation.json b/static/locales/it/translation.json index f014d4a7..5dd97326 100644 --- a/static/locales/it/translation.json +++ b/static/locales/it/translation.json @@ -47,7 +47,9 @@ "status": "Stato", "add_user": "Aggiungi utente", "update_user": "Aggiorna utente", - "template_user": "Modello utente" + "template_user": "Modello utente", + "add_group": "Aggiungi gruppo", + "update_group": "Aggiorna gruppo" }, "setup": { "desc": "Per iniziare a utilizzare SFTPGo devi creare un utente amministratore", @@ -462,7 +464,9 @@ "disconnect_help": "In questo modo si obbliga l'utente a effettuare nuovamente il login, se connesso, e quindi ad utilizzare la nuova configurazione", "submit_generate": "Genera e salva utenti", "submit_export": "Genera ed esporta utenti", - "invalid_quota_size": "Quota (dimensione) non valida" + "invalid_quota_size": "Quota (dimensione) non valida", + "expires_in": "Scadenza", + "expires_in_help": "Scadenza dell'account espressa in numero di giorni dalla creazione. 0 significa nessuna scadenza" }, "group": { "view_manage": "Visualizza e gestisci gruppi", @@ -621,7 +625,7 @@ "is_anonymous_help": "Gli utenti anonimi sono supportati per i protocolli FTP e WebDAV e hanno accesso di sola lettura", "disable_fs_checks": "Disabilita i controlli del filesystem", "disable_fs_checks_help": "Disabilita i controlli sull'esistenza e la creazione automatica della directory home e delle cartelle virtuali", - "api_key_auth_help": "Permetti di impersonare questo utente nelle API REST utilizzando una chiave API", + "api_key_auth_help": "Permetti di impersonare l'utente nelle API REST utilizzando una chiave API", "external_auth_cache_time": "Cache per autenticazione esterna", "external_auth_cache_time_help": "Tempo di memorizzazione nella cache, in secondi, per gli utenti autenticati utilizzando un hook di autenticazione esterno. 0 significa nessuna cache" } diff --git a/templates/webadmin/fsconfig.html b/templates/webadmin/fsconfig.html index 9fc862fd..67f20368 100644 --- a/templates/webadmin/fsconfig.html +++ b/templates/webadmin/fsconfig.html @@ -508,4 +508,358 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). +{{- end}} + +{{- define "user_group_perms"}} +
+
+

Per-directory name patterns restrictions

+
+
+
+

+
+
+ {{- range $idx, $pattern := .GetFlatFilePatterns -}} +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ {{- else}} +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ {{- end}} +
+
+ + +
+
+
+{{- end}} + +{{- define "user_group_quota"}} +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+

Per-source bandwidth speed limits

+
+
+
+
+
+ {{- range $idx, $bwLimit := .Filters.BandwidthLimits -}} +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+ {{- else}} +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+ {{- end}} +
+
+ + +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+{{- end}} + +{{- define "user_group_advanced"}} +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+
+ + +
+
+
+{{- end}} + +{{- define "user_group_profile"}} +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
{{- end}} \ No newline at end of file diff --git a/templates/webadmin/group.html b/templates/webadmin/group.html index b45a347b..09b00580 100644 --- a/templates/webadmin/group.html +++ b/templates/webadmin/group.html @@ -1,752 +1,415 @@ {{template "base" .}} -{{define "title"}}{{.Title}}{{end}} - -{{define "extra_css"}} - -{{end}} - -{{define "page_body"}} - -
-
-
{{.Title}}
+{{- define "page_body"}} +
+
+

- {{if .Error}} - - {{end}} -
-
- The %username% placeholder will be replaced with the username of the associated users. -
-
+ {{- template "errmsg" .Error}}
+
- -
- -
-
-
- -
- - - Optional description - + +
+
- {{template "fshtml" .FsWrapper}} - {{if .VirtualFolders}} -
-
- Virtual folders +
+ +
+ +
+
+ + {{- template "fshtml" .FsWrapper}} + {{- if .VirtualFolders}} +
+
+

Virtual folders

-
Quota size -1 means included within user quota, 0 unlimited. Don't set -1 for shared folders. You can use MB/GB/TB suffix. With no suffix we assume bytes
-
-
- {{range $idx, $val := .Group.VirtualFolders}} -
-
- +
+

+ Quota size/files -1 means included within user quota, 0 unlimited. Don't set -1 for shared folders. You can use MB/GB/TB suffix. Without suffix we assume bytes +

+
+
+ {{- range $idx, $val := .Group.VirtualFolders}} +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
-
- -
-
- - - Quota size - -
-
- - - Quota files - -
-
- + {{- else}} +
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+ {{- end}}
- {{else}} -
-
- -
-
- -
-
- - - Quota size - -
-
- - - Quota files - -
-
- -
-
- {{end}} +
+ +
+
+
+ {{- end}} -
- + +
+
+ +
+
+

Per-directory permissions

+
+
+
+

+
+
+ {{- range $idx, $dirPerms := .Group.GetPermissions -}} +
+
+
+ +
+
+ +
+ +
+
+ {{- else}} +
+
+
+ +
+
+ +
+ +
+
+ {{- end}} +
+
+ + +
+
+
+ + {{- template "user_group_perms" .Group.UserSettings.Filters}} + +
+ +
+ +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+ +
+

+ +

+
+
+ {{- template "user_group_quota" .Group.UserSettings}} +
+ +
+

+ +

+
+
+ +
+ +
+ +
+
+
+ + {{- template "user_group_profile" .Group.UserSettings.Filters}} + + {{- template "user_group_advanced" .Group.UserSettings.Filters}} + +
+ +
+ +
+
+
+ +
+
+
+
- {{end}} -
-
-
-

- -

-
-
-
-
-
- Per-directory permissions. Wildcards are supported in paths, for example "/incoming/*" matches any directory within "/incoming" -
-
-
-
- {{range $idx, $dirPerms := .Group.GetPermissions -}} -
-
- -
-
- -
-
- -
-
- {{else}} -
-
- -
-
- -
-
- -
-
- {{end}} -
-
- -
- -
-
-
- -
-
- Per-directory pattern restrictions -
-
-
Comma separated denied or allowed files/directories, based on shell patterns.
-

Match is case insensitive, set you patterns as lowercase. Denied entries are visible in directory listing by default, you can hide them by setting the "Hidden" policy, but please be aware that this may cause performance issues for large directories. Setting a denied pattern as "*" and allowed pattern/s for the same directory you can create denied except rules, but note that if you allow a directory, everything in it will be allowed unless more specific patterns/permissions are defined.

-
-
- {{range $idx, $pattern := .Group.UserSettings.Filters.GetFlatFilePatterns -}} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- {{else}} -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- {{end}} -
-
- -
- -
-
-
- -
- -
- - - Maximun number of concurrent sessions. 0 means no limit - -
-
- -
- -
- -
-
- -
- -
- - - "password" is valid for all supported protocols, "password-over-SSH" only for SSH/SFTP/SCP - -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- - - Comma separated IP/Mask in CIDR format, example: "192.168.1.0/24,10.8.0.100/32" - -
-
- -
- -
- - - Comma separated IP/Mask in CIDR format, example: "192.168.1.0/24,10.8.0.100/32" - -
-
- -
-
-
-
-
-

- -

-
-
-
- -
- -
- - - 0 means no limit. You can use MB/GB/TB suffix - -
-
- -
- - - 0 means no limit - -
-
- -
- -
- - - Maximum upload size for a single file. 0 means no limit. You can use MB/GB/TB suffix - -
-
- -
- -
- - - 0 means no limit - -
-
- -
- - - 0 means no limit - -
-
- -
-
- Per-source bandwidth speed limits -
-
-
-
- {{range $idx, $bwLimit := .Group.UserSettings.Filters.BandwidthLimits -}} -
-
- - - Comma separated IP/Mask in CIDR format, example: "192.168.1.0/24,10.8.0.100/32" - -
-
-
- - - UL (KB/s). 0 means no limit - -
-
- - - DL (KB/s). 0 means no limit - -
-
- -
- -
-
- {{else}} -
-
- - - Comma separated IP/Mask in CIDR format, example: "192.168.1.0/24,10.8.0.100/32" - -
-
-
- - - UL (KB/s). 0 means no limit - -
-
- - - DL (KB/s). 0 means no limit - -
-
-
- -
-
- {{end}} -
-
- -
- -
-
-
- -
- -
- - - Maximum data transfer allowed for uploads. 0 means no limit - -
-
- -
- - - Maximum data transfer allowed for downloads. 0 means no limit - -
-
- -
- -
- - - Maximum data transfer allowed for uploads + downloads. Replace the individual limits. 0 means no limit - -
-
- -
-
-
- -
-
-

- -

-
-
-
- -
- -
- - - Alternate start directory to use instead of "/". Supported for SFTP/FTP/HTTP - -
-
- -
- -
- - - Defines the TLS certificate field to use as username. Ignored if mutual TLS is disabled - -
-
- -
- -
- - - Ignored if TLS is globally required for all FTP users - -
-
- -
- -
- - - Account expiration as number of days from the creation. 0 means no expiration - -
-
- -
- -
- - - Values in the 50-70 range are suggested for common use cases. 0 means disabled, any password will be accepted. Applied when users change their password - -
-
- -
- -
- - - Password expiration as number of days. 0 means no expiration - -
-
- -
- -
- - - Default expiration for newly created shares as number of days - -
-
- -
- -
- - - Maximum allowed expiration, as number of days, when a user creates or updates a share - -
-
- -
- -
- -
-
- -
-
- - - - Anonymous users are supported for FTP and WebDAV protocols and have read-only access - -
-
- -
-
- - - - Disable checks for existence and automatic creation of home directory and virtual folders - -
-
- -
-
- - - - Allow to impersonate the associated users, in REST API, with an API key - -
-
- -
- -
- - - Cache time, in seconds, for users authenticated using an external auth hook. 0 means no cache - -
-
- -
-
-
-
- -
- +
+ +
-{{end}} +{{- end}} +{{- define "extra_js"}} + + - - -{{template "fsjs"}} -{{template "shared_user_group" .}} -{{end}} \ No newline at end of file +{{- end}} \ No newline at end of file diff --git a/templates/webadmin/user.html b/templates/webadmin/user.html index 6af49410..ac50acb8 100644 --- a/templates/webadmin/user.html +++ b/templates/webadmin/user.html @@ -400,37 +400,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
-
- -
- -
-
-
- -
- -
- -
-
-
- -
- -
- -
-
-
- -
- -
- -
-
-
+ {{- template "user_group_profile" .User.Filters}}
@@ -549,98 +519,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
-
-
-

Per-directory name patterns restrictions

-
-
-
-

-
-
- {{- range $idx, $pattern := .User.Filters.GetFlatFilePatterns -}} -
-
-
- -
-
- -
-
- -
-
- -
- -
-
- {{- else}} -
-
-
- -
-
- -
-
- -
-
- -
- -
-
- {{- end}} -
-
- - -
-
-
+ {{- template "user_group_perms" .User.Filters}}
@@ -665,7 +544,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
+ rows="3">{{.User.GetAllowedIPAsString}}
@@ -735,143 +614,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
-
- -
- -
-
-
- -
- -
-
-
- -
- -
- -
-
-
- -
- -
- -
-
-
- -
- -
-
-
- -
-
-

Per-source bandwidth speed limits

-
-
-
-
-
- {{- range $idx, $bwLimit := .User.Filters.BandwidthLimits -}} -
-
-
- -
-
-
- -
-
-
- -
-
- -
-
- {{- else}} -
-
-
- -
-
-
- -
-
-
- -
-
- -
-
- {{- end}} -
-
- - -
-
-
- -
- -
- -
-
-
- -
- -
-
-
- -
- -
- -
-
-
+ {{template "user_group_quota" .User}}
@@ -886,50 +629,13 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
-
- -
- -
-
-
+ {{template "user_group_advanced" .User.Filters}} -
- +
+
- -
-
-
- -
- -
- -
-
-
- -
- -
- + +
@@ -945,50 +651,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
-
- -
-
- - -
-
-
- -
- -
-
- - -
-
-
- -
- -
-
- - -
-
-
- -
- -
- -
-
-
-