mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-19 23:53:49 +08:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
7be026dd1f | |||
3e7938e5f6 | |||
30789138e2 | |||
9610ce5b8c | |||
a39a311d2f | |||
08e41ab762 | |||
85ca318e2f | |||
9032865e60 | |||
5692522ee0 | |||
cb1882e589 | |||
41d9422687 | |||
3297db688b | |||
cc82d292f0 | |||
f2e3037bc5 | |||
d986a4a9e0 | |||
2df3878c15 | |||
24ab8880cc | |||
f26b4853c5 | |||
d78e8e9776 | |||
d61f9a1856 |
@ -249,6 +249,10 @@ func (c *ApiController) Signup() {
|
|||||||
user.Groups = []string{invitation.SignupGroup}
|
user.Groups = []string{invitation.SignupGroup}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.DefaultGroup != "" && user.Groups == nil {
|
||||||
|
user.Groups = []string{application.DefaultGroup}
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := object.AddUser(user)
|
affected, err := object.AddUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
|
@ -598,6 +598,9 @@ func (c *ApiController) Login() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if provider == nil {
|
||||||
|
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s does not exist"), authForm.Provider))
|
||||||
|
}
|
||||||
|
|
||||||
providerItem := application.GetProviderItem(provider.Name)
|
providerItem := application.GetProviderItem(provider.Name)
|
||||||
if !providerItem.IsProviderVisible() {
|
if !providerItem.IsProviderVisible() {
|
||||||
@ -986,6 +989,18 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authForm.Language != "" {
|
||||||
|
user := c.getCurrentUser()
|
||||||
|
if user != nil {
|
||||||
|
user.Language = authForm.Language
|
||||||
|
_, err = object.UpdateUser(user.GetId(), user, []string{"language"}, user.IsAdmin)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = resp
|
c.Data["json"] = resp
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ type AuthForm struct {
|
|||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"`
|
||||||
Affiliation string `json:"affiliation"`
|
Affiliation string `json:"affiliation"`
|
||||||
IdCard string `json:"idCard"`
|
IdCard string `json:"idCard"`
|
||||||
|
Language string `json:"language"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
InvitationCode string `json:"invitationCode"`
|
InvitationCode string `json:"invitationCode"`
|
||||||
|
|
||||||
|
171
go.mod
171
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/casdoor/casdoor
|
module github.com/casdoor/casdoor
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.3
|
github.com/Masterminds/squirrel v1.5.3
|
||||||
@ -18,7 +18,6 @@ require (
|
|||||||
github.com/casvisor/casvisor-go-sdk v1.4.0
|
github.com/casvisor/casvisor-go-sdk v1.4.0
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/denisenkom/go-mssqldb v0.9.0
|
github.com/denisenkom/go-mssqldb v0.9.0
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
|
|
||||||
github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
|
github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
|
||||||
github.com/fogleman/gg v1.3.0
|
github.com/fogleman/gg v1.3.0
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5
|
github.com/go-asn1-ber/asn1-ber v1.5.5
|
||||||
@ -46,7 +45,6 @@ require (
|
|||||||
github.com/russellhaering/gosaml2 v0.9.0
|
github.com/russellhaering/gosaml2 v0.9.0
|
||||||
github.com/russellhaering/goxmldsig v1.2.0
|
github.com/russellhaering/goxmldsig v1.2.0
|
||||||
github.com/sendgrid/sendgrid-go v3.14.0+incompatible
|
github.com/sendgrid/sendgrid-go v3.14.0+incompatible
|
||||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
|
||||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
@ -54,20 +52,179 @@ require (
|
|||||||
github.com/stripe/stripe-go/v74 v74.29.0
|
github.com/stripe/stripe-go/v74 v74.29.0
|
||||||
github.com/tealeg/xlsx v1.0.5
|
github.com/tealeg/xlsx v1.0.5
|
||||||
github.com/thanhpk/randstr v1.0.4
|
github.com/thanhpk/randstr v1.0.4
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
|
||||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
|
||||||
github.com/xorm-io/builder v0.3.13
|
github.com/xorm-io/builder v0.3.13
|
||||||
github.com/xorm-io/core v0.7.4
|
github.com/xorm-io/core v0.7.4
|
||||||
github.com/xorm-io/xorm v1.1.6
|
github.com/xorm-io/xorm v1.1.6
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
|
||||||
golang.org/x/crypto v0.32.0
|
golang.org/x/crypto v0.32.0
|
||||||
golang.org/x/net v0.34.0
|
golang.org/x/net v0.34.0
|
||||||
golang.org/x/oauth2 v0.17.0
|
golang.org/x/oauth2 v0.17.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.21.0
|
||||||
google.golang.org/api v0.150.0
|
google.golang.org/api v0.150.0
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
||||||
maunium.net/go/mautrix v0.16.0
|
maunium.net/go/mautrix v0.16.0
|
||||||
modernc.org/sqlite v1.18.2
|
modernc.org/sqlite v1.18.2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.110.8 // indirect
|
||||||
|
cloud.google.com/go/compute v1.23.1 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
|
cloud.google.com/go/iam v1.1.3 // indirect
|
||||||
|
cloud.google.com/go/storage v1.35.1 // indirect
|
||||||
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
|
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||||
|
github.com/Azure/azure-storage-blob-go v0.15.0 // indirect
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
|
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||||
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||||
|
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20221121042443-a3fd332d56d9 // indirect
|
||||||
|
github.com/SherClockHolmes/webpush-go v1.2.0 // indirect
|
||||||
|
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545 // indirect
|
||||||
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible // indirect
|
||||||
|
github.com/apistd/uni-go-sdk v0.0.2 // indirect
|
||||||
|
github.com/atc0005/go-teams-notify/v2 v2.6.1 // indirect
|
||||||
|
github.com/baidubce/bce-sdk-go v0.9.156 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19 // indirect
|
||||||
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
|
github.com/bwmarrin/discordgo v0.27.1 // indirect
|
||||||
|
github.com/casdoor/casdoor-go-sdk v0.50.0 // indirect
|
||||||
|
github.com/casdoor/go-reddit/v2 v2.1.0 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.3.3 // indirect
|
||||||
|
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb // indirect
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||||
|
github.com/dghubble/oauth1 v0.7.2 // indirect
|
||||||
|
github.com/dghubble/sling v1.4.0 // indirect
|
||||||
|
github.com/di-wu/parser v0.2.2 // indirect
|
||||||
|
github.com/di-wu/xsd-datetime v1.0.0 // indirect
|
||||||
|
github.com/drswork/go-twitter v0.0.0-20221107160839-dea1b6ed53d7 // indirect
|
||||||
|
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
|
||||||
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
|
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||||
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
|
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||||
|
github.com/go-lark/lark v1.9.0 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-webauthn/revoke v0.1.6 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||||
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/go-tpm v0.3.3 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/gregdel/pushover v1.2.1 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
|
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||||
|
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||||
|
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||||
|
github.com/line/line-bot-sdk-go v7.8.0+incompatible // indirect
|
||||||
|
github.com/markbates/going v1.0.0 // indirect
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
|
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
|
github.com/mileusna/viber v1.0.1 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c // indirect
|
||||||
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
|
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect
|
||||||
|
github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7 // indirect
|
||||||
|
github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d // indirect
|
||||||
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/common v0.30.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
|
github.com/qiniu/go-sdk/v7 v7.12.1 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
|
github.com/rs/zerolog v1.30.0 // indirect
|
||||||
|
github.com/scim2/filter-parser/v2 v2.2.0 // indirect
|
||||||
|
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||||
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
|
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
|
||||||
|
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
|
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||||
|
github.com/slack-go/slack v0.12.3 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
|
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.744 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.744 // indirect
|
||||||
|
github.com/tidwall/gjson v1.16.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
|
github.com/tidwall/sjson v1.2.5 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||||
|
github.com/twilio/twilio-go v1.13.0 // indirect
|
||||||
|
github.com/ucloud/ucloud-sdk-go v0.22.5 // indirect
|
||||||
|
github.com/utahta/go-linenotify v0.5.0 // indirect
|
||||||
|
github.com/volcengine/volc-sdk-golang v1.0.117 // indirect
|
||||||
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 // indirect
|
||||||
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.7.0 // indirect
|
||||||
|
go.uber.org/zap v1.19.1 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
|
||||||
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||||
|
google.golang.org/grpc v1.59.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.32.0 // indirect
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
lukechampine.com/uint128 v1.1.1 // indirect
|
||||||
|
maunium.net/go/maulogger/v2 v2.4.1 // indirect
|
||||||
|
modernc.org/cc/v3 v3.37.0 // indirect
|
||||||
|
modernc.org/ccgo/v3 v3.16.9 // indirect
|
||||||
|
modernc.org/libc v1.18.0 // indirect
|
||||||
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
|
modernc.org/memory v1.3.0 // indirect
|
||||||
|
modernc.org/opt v0.1.1 // indirect
|
||||||
|
modernc.org/strutil v1.1.3 // indirect
|
||||||
|
modernc.org/token v1.0.1 // indirect
|
||||||
|
)
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Uživatelské jméno nemůže být emailová adresa",
|
"Username cannot be an email address": "Uživatelské jméno nemůže být emailová adresa",
|
||||||
"Username cannot contain white spaces": "Uživatelské jméno nemůže obsahovat mezery",
|
"Username cannot contain white spaces": "Uživatelské jméno nemůže obsahovat mezery",
|
||||||
"Username cannot start with a digit": "Uživatelské jméno nemůže začínat číslicí",
|
"Username cannot start with a digit": "Uživatelské jméno nemůže začínat číslicí",
|
||||||
"Username is too long (maximum is 39 characters).": "Uživatelské jméno je příliš dlouhé (maximálně 39 znaků).",
|
"Username is too long (maximum is 255 characters).": "Uživatelské jméno je příliš dlouhé (maximálně 255 znaků).",
|
||||||
"Username must have at least 2 characters": "Uživatelské jméno musí mít alespoň 2 znaky",
|
"Username must have at least 2 characters": "Uživatelské jméno musí mít alespoň 2 znaky",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali jste špatné heslo nebo kód příliš mnohokrát, prosím počkejte %d minut a zkuste to znovu",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali jste špatné heslo nebo kód příliš mnohokrát, prosím počkejte %d minut a zkuste to znovu",
|
||||||
"Your region is not allow to signup by phone": "Vaše oblast neumožňuje registraci pomocí telefonu",
|
"Your region is not allow to signup by phone": "Vaše oblast neumožňuje registraci pomocí telefonu",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein",
|
"Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein",
|
||||||
"Username cannot contain white spaces": "Benutzername darf keine Leerzeichen enthalten",
|
"Username cannot contain white spaces": "Benutzername darf keine Leerzeichen enthalten",
|
||||||
"Username cannot start with a digit": "Benutzername darf nicht mit einer Ziffer beginnen",
|
"Username cannot start with a digit": "Benutzername darf nicht mit einer Ziffer beginnen",
|
||||||
"Username is too long (maximum is 39 characters).": "Benutzername ist zu lang (das Maximum beträgt 39 Zeichen).",
|
"Username is too long (maximum is 255 characters).": "Benutzername ist zu lang (das Maximum beträgt 255 Zeichen).",
|
||||||
"Username must have at least 2 characters": "Benutzername muss mindestens 2 Zeichen lang sein",
|
"Username must have at least 2 characters": "Benutzername muss mindestens 2 Zeichen lang sein",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Sie haben zu oft das falsche Passwort oder den falschen Code eingegeben. Bitte warten Sie %d Minuten und versuchen Sie es erneut",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Sie haben zu oft das falsche Passwort oder den falschen Code eingegeben. Bitte warten Sie %d Minuten und versuchen Sie es erneut",
|
||||||
"Your region is not allow to signup by phone": "Ihre Region ist nicht berechtigt, sich telefonisch anzumelden",
|
"Your region is not allow to signup by phone": "Ihre Region ist nicht berechtigt, sich telefonisch anzumelden",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico",
|
"Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico",
|
||||||
"Username cannot contain white spaces": "Nombre de usuario no puede contener espacios en blanco",
|
"Username cannot contain white spaces": "Nombre de usuario no puede contener espacios en blanco",
|
||||||
"Username cannot start with a digit": "El nombre de usuario no puede empezar con un dígito",
|
"Username cannot start with a digit": "El nombre de usuario no puede empezar con un dígito",
|
||||||
"Username is too long (maximum is 39 characters).": "El nombre de usuario es demasiado largo (el máximo es de 39 caracteres).",
|
"Username is too long (maximum is 255 characters).": "El nombre de usuario es demasiado largo (el máximo es de 255 caracteres).",
|
||||||
"Username must have at least 2 characters": "Nombre de usuario debe tener al menos 2 caracteres",
|
"Username must have at least 2 characters": "Nombre de usuario debe tener al menos 2 caracteres",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Has ingresado la contraseña o código incorrecto demasiadas veces, por favor espera %d minutos e intenta de nuevo",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Has ingresado la contraseña o código incorrecto demasiadas veces, por favor espera %d minutos e intenta de nuevo",
|
||||||
"Your region is not allow to signup by phone": "Tu región no está permitida para registrarse por teléfono",
|
"Your region is not allow to signup by phone": "Tu región no está permitida para registrarse por teléfono",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "نام کاربری نمیتواند یک آدرس ایمیل باشد",
|
"Username cannot be an email address": "نام کاربری نمیتواند یک آدرس ایمیل باشد",
|
||||||
"Username cannot contain white spaces": "نام کاربری نمیتواند حاوی فاصله باشد",
|
"Username cannot contain white spaces": "نام کاربری نمیتواند حاوی فاصله باشد",
|
||||||
"Username cannot start with a digit": "نام کاربری نمیتواند با یک رقم شروع شود",
|
"Username cannot start with a digit": "نام کاربری نمیتواند با یک رقم شروع شود",
|
||||||
"Username is too long (maximum is 39 characters).": "نام کاربری بیش از حد طولانی است (حداکثر ۳۹ کاراکتر).",
|
"Username is too long (maximum is 255 characters).": "نام کاربری بیش از حد طولانی است (حداکثر ۳۹ کاراکتر).",
|
||||||
"Username must have at least 2 characters": "نام کاربری باید حداقل ۲ کاراکتر داشته باشد",
|
"Username must have at least 2 characters": "نام کاربری باید حداقل ۲ کاراکتر داشته باشد",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "شما رمز عبور یا کد اشتباه را بیش از حد وارد کردهاید، لطفاً %d دقیقه صبر کنید و دوباره تلاش کنید",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "شما رمز عبور یا کد اشتباه را بیش از حد وارد کردهاید، لطفاً %d دقیقه صبر کنید و دوباره تلاش کنید",
|
||||||
"Your region is not allow to signup by phone": "منطقه شما اجازه ثبتنام با تلفن را ندارد",
|
"Your region is not allow to signup by phone": "منطقه شما اجازه ثبتنام با تلفن را ندارد",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail",
|
"Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail",
|
||||||
"Username cannot contain white spaces": "Nom d'utilisateur ne peut pas contenir d'espaces blancs",
|
"Username cannot contain white spaces": "Nom d'utilisateur ne peut pas contenir d'espaces blancs",
|
||||||
"Username cannot start with a digit": "Nom d'utilisateur ne peut pas commencer par un chiffre",
|
"Username cannot start with a digit": "Nom d'utilisateur ne peut pas commencer par un chiffre",
|
||||||
"Username is too long (maximum is 39 characters).": "Nom d'utilisateur est trop long (maximum de 39 caractères).",
|
"Username is too long (maximum is 255 characters).": "Nom d'utilisateur est trop long (maximum de 255 caractères).",
|
||||||
"Username must have at least 2 characters": "Le nom d'utilisateur doit comporter au moins 2 caractères",
|
"Username must have at least 2 characters": "Le nom d'utilisateur doit comporter au moins 2 caractères",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Vous avez entré le mauvais mot de passe ou code plusieurs fois, veuillez attendre %d minutes et réessayer",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Vous avez entré le mauvais mot de passe ou code plusieurs fois, veuillez attendre %d minutes et réessayer",
|
||||||
"Your region is not allow to signup by phone": "Votre région n'est pas autorisée à s'inscrire par téléphone",
|
"Your region is not allow to signup by phone": "Votre région n'est pas autorisée à s'inscrire par téléphone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username tidak bisa menjadi alamat email",
|
"Username cannot be an email address": "Username tidak bisa menjadi alamat email",
|
||||||
"Username cannot contain white spaces": "Username tidak boleh mengandung spasi",
|
"Username cannot contain white spaces": "Username tidak boleh mengandung spasi",
|
||||||
"Username cannot start with a digit": "Username tidak dapat dimulai dengan angka",
|
"Username cannot start with a digit": "Username tidak dapat dimulai dengan angka",
|
||||||
"Username is too long (maximum is 39 characters).": "Nama pengguna terlalu panjang (maksimum 39 karakter).",
|
"Username is too long (maximum is 255 characters).": "Nama pengguna terlalu panjang (maksimum 255 karakter).",
|
||||||
"Username must have at least 2 characters": "Nama pengguna harus memiliki setidaknya 2 karakter",
|
"Username must have at least 2 characters": "Nama pengguna harus memiliki setidaknya 2 karakter",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Anda telah memasukkan kata sandi atau kode yang salah terlalu banyak kali, mohon tunggu selama %d menit dan coba lagi",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Anda telah memasukkan kata sandi atau kode yang salah terlalu banyak kali, mohon tunggu selama %d menit dan coba lagi",
|
||||||
"Your region is not allow to signup by phone": "Wilayah Anda tidak diizinkan untuk mendaftar melalui telepon",
|
"Your region is not allow to signup by phone": "Wilayah Anda tidak diizinkan untuk mendaftar melalui telepon",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません",
|
"Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません",
|
||||||
"Username cannot contain white spaces": "ユーザ名にはスペースを含めることはできません",
|
"Username cannot contain white spaces": "ユーザ名にはスペースを含めることはできません",
|
||||||
"Username cannot start with a digit": "ユーザー名は数字で始めることはできません",
|
"Username cannot start with a digit": "ユーザー名は数字で始めることはできません",
|
||||||
"Username is too long (maximum is 39 characters).": "ユーザー名が長すぎます(最大39文字)。",
|
"Username is too long (maximum is 255 characters).": "ユーザー名が長すぎます(最大255文字)。",
|
||||||
"Username must have at least 2 characters": "ユーザー名は少なくとも2文字必要です",
|
"Username must have at least 2 characters": "ユーザー名は少なくとも2文字必要です",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "あなたは間違ったパスワードまたはコードを何度も入力しました。%d 分間待ってから再度お試しください",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "あなたは間違ったパスワードまたはコードを何度も入力しました。%d 分間待ってから再度お試しください",
|
||||||
"Your region is not allow to signup by phone": "あなたの地域は電話でサインアップすることができません",
|
"Your region is not allow to signup by phone": "あなたの地域は電話でサインアップすることができません",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다",
|
"Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다",
|
||||||
"Username cannot contain white spaces": "사용자 이름에는 공백이 포함될 수 없습니다",
|
"Username cannot contain white spaces": "사용자 이름에는 공백이 포함될 수 없습니다",
|
||||||
"Username cannot start with a digit": "사용자 이름은 숫자로 시작할 수 없습니다",
|
"Username cannot start with a digit": "사용자 이름은 숫자로 시작할 수 없습니다",
|
||||||
"Username is too long (maximum is 39 characters).": "사용자 이름이 너무 깁니다 (최대 39자).",
|
"Username is too long (maximum is 255 characters).": "사용자 이름이 너무 깁니다 (최대 255자).",
|
||||||
"Username must have at least 2 characters": "사용자 이름은 적어도 2개의 문자가 있어야 합니다",
|
"Username must have at least 2 characters": "사용자 이름은 적어도 2개의 문자가 있어야 합니다",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "올바르지 않은 비밀번호나 코드를 여러 번 입력했습니다. %d분 동안 기다리신 후 다시 시도해주세요",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "올바르지 않은 비밀번호나 코드를 여러 번 입력했습니다. %d분 동안 기다리신 후 다시 시도해주세요",
|
||||||
"Your region is not allow to signup by phone": "당신의 지역은 전화로 가입할 수 없습니다",
|
"Your region is not allow to signup by phone": "당신의 지역은 전화로 가입할 수 없습니다",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "O nome de usuário não pode começar com um dígito",
|
"Username cannot start with a digit": "O nome de usuário não pode começar com um dígito",
|
||||||
"Username is too long (maximum is 39 characters).": "Nome de usuário é muito longo (máximo é 39 caracteres).",
|
"Username is too long (maximum is 255 characters).": "Nome de usuário é muito longo (máximo é 255 caracteres).",
|
||||||
"Username must have at least 2 characters": "Nome de usuário deve ter pelo menos 2 caracteres",
|
"Username must have at least 2 characters": "Nome de usuário deve ter pelo menos 2 caracteres",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
|
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
|
||||||
"Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
|
"Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
|
||||||
"Username cannot start with a digit": "Имя пользователя не может начинаться с цифры",
|
"Username cannot start with a digit": "Имя пользователя не может начинаться с цифры",
|
||||||
"Username is too long (maximum is 39 characters).": "Имя пользователя слишком длинное (максимальная длина - 39 символов).",
|
"Username is too long (maximum is 255 characters).": "Имя пользователя слишком длинное (максимальная длина - 255 символов).",
|
||||||
"Username must have at least 2 characters": "Имя пользователя должно содержать не менее 2 символов",
|
"Username must have at least 2 characters": "Имя пользователя должно содержать не менее 2 символов",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Вы ввели неправильный пароль или код слишком много раз, пожалуйста, подождите %d минут и попробуйте снова",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Вы ввели неправильный пароль или код слишком много раз, пожалуйста, подождите %d минут и попробуйте снова",
|
||||||
"Your region is not allow to signup by phone": "Ваш регион не разрешает регистрацию по телефону",
|
"Your region is not allow to signup by phone": "Ваш регион не разрешает регистрацию по телефону",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Používateľské meno nemôže byť e-mailová adresa",
|
"Username cannot be an email address": "Používateľské meno nemôže byť e-mailová adresa",
|
||||||
"Username cannot contain white spaces": "Používateľské meno nemôže obsahovať medzery",
|
"Username cannot contain white spaces": "Používateľské meno nemôže obsahovať medzery",
|
||||||
"Username cannot start with a digit": "Používateľské meno nemôže začínať číslicou",
|
"Username cannot start with a digit": "Používateľské meno nemôže začínať číslicou",
|
||||||
"Username is too long (maximum is 39 characters).": "Používateľské meno je príliš dlhé (maximum je 39 znakov).",
|
"Username is too long (maximum is 255 characters).": "Používateľské meno je príliš dlhé (maximum je 255 znakov).",
|
||||||
"Username must have at least 2 characters": "Používateľské meno musí mať aspoň 2 znaky",
|
"Username must have at least 2 characters": "Používateľské meno musí mať aspoň 2 znaky",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali ste nesprávne heslo alebo kód príliš veľa krát, prosím, počkajte %d minút a skúste to znova",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali ste nesprávne heslo alebo kód príliš veľa krát, prosím, počkajte %d minút a skúste to znova",
|
||||||
"Your region is not allow to signup by phone": "Váš región neumožňuje registráciu cez telefón",
|
"Your region is not allow to signup by phone": "Váš región neumožňuje registráciu cez telefón",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Kullanıcı adı bir e-mail adresi olamaz",
|
"Username cannot be an email address": "Kullanıcı adı bir e-mail adresi olamaz",
|
||||||
"Username cannot contain white spaces": "Kullanıcı adı boşluk karakteri içeremez",
|
"Username cannot contain white spaces": "Kullanıcı adı boşluk karakteri içeremez",
|
||||||
"Username cannot start with a digit": "Kullanıcı adı rakamla başlayamaz",
|
"Username cannot start with a digit": "Kullanıcı adı rakamla başlayamaz",
|
||||||
"Username is too long (maximum is 39 characters).": "Kullanıcı adı çok uzun (en fazla 39 karakter olmalı).",
|
"Username is too long (maximum is 255 characters).": "Kullanıcı adı çok uzun (en fazla 255 karakter olmalı).",
|
||||||
"Username must have at least 2 characters": "Kullanıcı adı en az iki karakterden oluşmalı",
|
"Username must have at least 2 characters": "Kullanıcı adı en az iki karakterden oluşmalı",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Çok fazla hatalı şifre denemesi yaptınız. %d dakika kadar bekleyip yeniden giriş yapmayı deneyebilirsiniz.",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Çok fazla hatalı şifre denemesi yaptınız. %d dakika kadar bekleyip yeniden giriş yapmayı deneyebilirsiniz.",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Username cannot be an email address",
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
"Username is too long (maximum is 255 characters).": "Username is too long (maximum is 255 characters).",
|
||||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "Tên người dùng không thể là địa chỉ email",
|
"Username cannot be an email address": "Tên người dùng không thể là địa chỉ email",
|
||||||
"Username cannot contain white spaces": "Tên người dùng không thể chứa khoảng trắng",
|
"Username cannot contain white spaces": "Tên người dùng không thể chứa khoảng trắng",
|
||||||
"Username cannot start with a digit": "Tên người dùng không thể bắt đầu bằng chữ số",
|
"Username cannot start with a digit": "Tên người dùng không thể bắt đầu bằng chữ số",
|
||||||
"Username is too long (maximum is 39 characters).": "Tên đăng nhập quá dài (tối đa là 39 ký tự).",
|
"Username is too long (maximum is 255 characters).": "Tên đăng nhập quá dài (tối đa là 255 ký tự).",
|
||||||
"Username must have at least 2 characters": "Tên đăng nhập phải có ít nhất 2 ký tự",
|
"Username must have at least 2 characters": "Tên đăng nhập phải có ít nhất 2 ký tự",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Bạn đã nhập sai mật khẩu hoặc mã quá nhiều lần, vui lòng đợi %d phút và thử lại",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Bạn đã nhập sai mật khẩu hoặc mã quá nhiều lần, vui lòng đợi %d phút và thử lại",
|
||||||
"Your region is not allow to signup by phone": "Vùng của bạn không được phép đăng ký bằng điện thoại",
|
"Your region is not allow to signup by phone": "Vùng của bạn không được phép đăng ký bằng điện thoại",
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
"Username cannot be an email address": "用户名不可以是邮箱地址",
|
"Username cannot be an email address": "用户名不可以是邮箱地址",
|
||||||
"Username cannot contain white spaces": "用户名禁止包含空格",
|
"Username cannot contain white spaces": "用户名禁止包含空格",
|
||||||
"Username cannot start with a digit": "用户名禁止使用数字开头",
|
"Username cannot start with a digit": "用户名禁止使用数字开头",
|
||||||
"Username is too long (maximum is 39 characters).": "用户名过长(最大允许长度为39个字符)",
|
"Username is too long (maximum is 255 characters).": "用户名过长(最大允许长度为255个字符)",
|
||||||
"Username must have at least 2 characters": "用户名至少要有2个字符",
|
"Username must have at least 2 characters": "用户名至少要有2个字符",
|
||||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试",
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试",
|
||||||
"Your region is not allow to signup by phone": "所在地区不支持手机号注册",
|
"Your region is not allow to signup by phone": "所在地区不支持手机号注册",
|
||||||
|
@ -434,7 +434,7 @@
|
|||||||
"isTopGroup": true,
|
"isTopGroup": true,
|
||||||
"title": "",
|
"title": "",
|
||||||
"key": "",
|
"key": "",
|
||||||
"children": "",
|
"children": [],
|
||||||
"isEnabled": true
|
"isEnabled": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -185,12 +185,9 @@ func buildUserFilterCondition(filter interface{}) (builder.Cond, error) {
|
|||||||
attr := string(f.AttributeDesc())
|
attr := string(f.AttributeDesc())
|
||||||
|
|
||||||
if attr == ldapMemberOfAttr {
|
if attr == ldapMemberOfAttr {
|
||||||
groupId := string(f.AssertionValue())
|
|
||||||
users, err := object.GetGroupUsers(groupId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var names []string
|
var names []string
|
||||||
|
groupId := string(f.AssertionValue())
|
||||||
|
users := object.GetGroupUsersWithoutError(groupId)
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
names = append(names, user.Name)
|
names = append(names, user.Name)
|
||||||
}
|
}
|
||||||
@ -249,7 +246,7 @@ func buildSafeCondition(filter interface{}) builder.Cond {
|
|||||||
condition, err := buildUserFilterCondition(filter)
|
condition, err := buildUserFilterCondition(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("err = %v", err.Error())
|
log.Printf("err = %v", err.Error())
|
||||||
return nil
|
return builder.And(builder.Expr("1 != 1"))
|
||||||
}
|
}
|
||||||
return condition
|
return condition
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ type Application struct {
|
|||||||
Description string `xorm:"varchar(100)" json:"description"`
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||||
|
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
|
||||||
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
|
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
|
||||||
EnablePassword bool `json:"enablePassword"`
|
EnablePassword bool `json:"enablePassword"`
|
||||||
EnableSignUp bool `json:"enableSignUp"`
|
EnableSignUp bool `json:"enableSignUp"`
|
||||||
@ -97,29 +98,30 @@ type Application struct {
|
|||||||
IsShared bool `json:"isShared"`
|
IsShared bool `json:"isShared"`
|
||||||
IpRestriction string `json:"ipRestriction"`
|
IpRestriction string `json:"ipRestriction"`
|
||||||
|
|
||||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||||
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
||||||
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
||||||
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
|
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
|
||||||
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
||||||
ExpireInHours int `json:"expireInHours"`
|
ExpireInHours int `json:"expireInHours"`
|
||||||
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
||||||
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
|
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
|
||||||
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
||||||
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
|
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
|
||||||
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
|
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
|
||||||
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
|
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
|
||||||
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
|
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
|
||||||
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
|
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
|
||||||
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
|
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
|
||||||
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
||||||
FooterHtml string `xorm:"mediumtext" json:"footerHtml"`
|
FooterHtml string `xorm:"mediumtext" json:"footerHtml"`
|
||||||
FormCss string `xorm:"text" json:"formCss"`
|
FormCss string `xorm:"text" json:"formCss"`
|
||||||
FormCssMobile string `xorm:"text" json:"formCssMobile"`
|
FormCssMobile string `xorm:"text" json:"formCssMobile"`
|
||||||
FormOffset int `json:"formOffset"`
|
FormOffset int `json:"formOffset"`
|
||||||
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
||||||
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
||||||
|
FormBackgroundUrlMobile string `xorm:"varchar(200)" json:"formBackgroundUrlMobile"`
|
||||||
|
|
||||||
FailedSigninLimit int `json:"failedSigninLimit"`
|
FailedSigninLimit int `json:"failedSigninLimit"`
|
||||||
FailedSigninFrozenTime int `json:"failedSigninFrozenTime"`
|
FailedSigninFrozenTime int `json:"failedSigninFrozenTime"`
|
||||||
|
@ -146,7 +146,12 @@ func getCertByName(name string) (*Cert, error) {
|
|||||||
|
|
||||||
func GetCert(id string) (*Cert, error) {
|
func GetCert(id string) (*Cert, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
return getCert(owner, name)
|
cert, err := getCert(owner, name)
|
||||||
|
if cert == nil && owner != "admin" {
|
||||||
|
return getCert("admin", name)
|
||||||
|
} else {
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateCert(id string, cert *Cert) (bool, error) {
|
func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||||
|
@ -517,8 +517,8 @@ func CheckLoginPermission(userId string, application *Application) (bool, error)
|
|||||||
func CheckUsername(username string, lang string) string {
|
func CheckUsername(username string, lang string) string {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return i18n.Translate(lang, "check:Empty username.")
|
return i18n.Translate(lang, "check:Empty username.")
|
||||||
} else if len(username) > 39 {
|
} else if len(username) > 255 {
|
||||||
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
|
return i18n.Translate(lang, "check:Username is too long (maximum is 255 characters).")
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
|
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
|
||||||
@ -533,8 +533,8 @@ func CheckUsername(username string, lang string) string {
|
|||||||
func CheckUsernameWithEmail(username string, lang string) string {
|
func CheckUsernameWithEmail(username string, lang string) string {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return i18n.Translate(lang, "check:Empty username.")
|
return i18n.Translate(lang, "check:Empty username.")
|
||||||
} else if len(username) > 39 {
|
} else if len(username) > 255 {
|
||||||
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
|
return i18n.Translate(lang, "check:Username is too long (maximum is 255 characters).")
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
|
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
|
||||||
|
@ -83,19 +83,23 @@ func GetPaginationGroups(owner string, offset, limit int, field, value, sortFiel
|
|||||||
func GetGroupsHaveChildrenMap(groups []*Group) (map[string]*Group, error) {
|
func GetGroupsHaveChildrenMap(groups []*Group) (map[string]*Group, error) {
|
||||||
groupsHaveChildren := []*Group{}
|
groupsHaveChildren := []*Group{}
|
||||||
resultMap := make(map[string]*Group)
|
resultMap := make(map[string]*Group)
|
||||||
|
groupMap := map[string]*Group{}
|
||||||
|
|
||||||
groupIds := []string{}
|
groupIds := []string{}
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
|
groupMap[group.Name] = group
|
||||||
groupIds = append(groupIds, group.Name)
|
groupIds = append(groupIds, group.Name)
|
||||||
groupIds = append(groupIds, group.ParentId)
|
if !group.IsTopGroup {
|
||||||
|
groupIds = append(groupIds, group.ParentId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ormer.Engine.Cols("owner", "name", "parent_id", "display_name").Distinct("parent_id").In("parent_id", groupIds).Find(&groupsHaveChildren)
|
err := ormer.Engine.Cols("owner", "name", "parent_id", "display_name").Distinct("parent_id").In("parent_id", groupIds).Find(&groupsHaveChildren)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, group := range groups {
|
for _, group := range groupsHaveChildren {
|
||||||
resultMap[group.Name] = group
|
resultMap[group.ParentId] = groupMap[group.ParentId]
|
||||||
}
|
}
|
||||||
return resultMap, nil
|
return resultMap, nil
|
||||||
}
|
}
|
||||||
@ -302,7 +306,10 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
|
|||||||
|
|
||||||
func GetGroupUsers(groupId string) ([]*User, error) {
|
func GetGroupUsers(groupId string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
owner, _, err := util.GetOwnerAndNameFromIdWithError(groupId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -314,6 +321,11 @@ func GetGroupUsers(groupId string) ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGroupUsersWithoutError(groupId string) []*User {
|
||||||
|
users, _ := GetGroupUsers(groupId)
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
func ExtendGroupWithUsers(group *Group) error {
|
func ExtendGroupWithUsers(group *Group) error {
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -77,6 +77,7 @@ func getOriginFromHostInternal(host string) (string, string) {
|
|||||||
return origin, origin
|
return origin, origin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDev := conf.GetConfigString("runmode") == "dev"
|
||||||
// "door.casdoor.com"
|
// "door.casdoor.com"
|
||||||
protocol := "https://"
|
protocol := "https://"
|
||||||
if !strings.Contains(host, ".") {
|
if !strings.Contains(host, ".") {
|
||||||
@ -87,7 +88,7 @@ func getOriginFromHostInternal(host string) (string, string) {
|
|||||||
protocol = "http://"
|
protocol = "http://"
|
||||||
}
|
}
|
||||||
|
|
||||||
if host == "localhost:8000" {
|
if host == "localhost:8000" && isDev {
|
||||||
return fmt.Sprintf("%s%s", protocol, "localhost:7001"), fmt.Sprintf("%s%s", protocol, "localhost:8000")
|
return fmt.Sprintf("%s%s", protocol, "localhost:7001"), fmt.Sprintf("%s%s", protocol, "localhost:8000")
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%s%s", protocol, host), fmt.Sprintf("%s%s", protocol, host)
|
return fmt.Sprintf("%s%s", protocol, host), fmt.Sprintf("%s%s", protocol, host)
|
||||||
|
@ -48,7 +48,7 @@ func InitUserManager() {
|
|||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(255) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100) index" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100) index" json:"createdTime"`
|
||||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||||
DeletedTime string `xorm:"varchar(100)" json:"deletedTime"`
|
DeletedTime string `xorm:"varchar(100)" json:"deletedTime"`
|
||||||
|
@ -38,6 +38,7 @@ type Webhook struct {
|
|||||||
ContentType string `xorm:"varchar(100)" json:"contentType"`
|
ContentType string `xorm:"varchar(100)" json:"contentType"`
|
||||||
Headers []*Header `xorm:"mediumtext" json:"headers"`
|
Headers []*Header `xorm:"mediumtext" json:"headers"`
|
||||||
Events []string `xorm:"varchar(1000)" json:"events"`
|
Events []string `xorm:"varchar(1000)" json:"events"`
|
||||||
|
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
||||||
IsUserExtended bool `json:"isUserExtended"`
|
IsUserExtended bool `json:"isUserExtended"`
|
||||||
SingleOrgOnly bool `json:"singleOrgOnly"`
|
SingleOrgOnly bool `json:"singleOrgOnly"`
|
||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
|
@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -25,17 +26,43 @@ import (
|
|||||||
|
|
||||||
func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *User) (int, string, error) {
|
func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *User) (int, string, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
userMap := make(map[string]interface{})
|
||||||
|
var body io.Reader
|
||||||
|
|
||||||
type RecordEx struct {
|
if webhook.TokenFields != nil && len(webhook.TokenFields) > 0 && extendedUser != nil {
|
||||||
casvisorsdk.Record
|
userValue := reflect.ValueOf(extendedUser).Elem()
|
||||||
ExtendedUser *User `xorm:"-" json:"extendedUser"`
|
|
||||||
}
|
|
||||||
recordEx := &RecordEx{
|
|
||||||
Record: *record,
|
|
||||||
ExtendedUser: extendedUser,
|
|
||||||
}
|
|
||||||
|
|
||||||
body := strings.NewReader(util.StructToJson(recordEx))
|
for _, field := range webhook.TokenFields {
|
||||||
|
userField := userValue.FieldByName(field)
|
||||||
|
if userField.IsValid() {
|
||||||
|
newfield := util.SnakeToCamel(util.CamelToSnakeCase(field))
|
||||||
|
userMap[newfield] = userField.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecordEx struct {
|
||||||
|
casvisorsdk.Record
|
||||||
|
ExtendedUser map[string]interface{} `json:"extendedUser"`
|
||||||
|
}
|
||||||
|
|
||||||
|
recordEx := &RecordEx{
|
||||||
|
Record: *record,
|
||||||
|
ExtendedUser: userMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
body = strings.NewReader(util.StructToJson(recordEx))
|
||||||
|
} else {
|
||||||
|
type RecordEx struct {
|
||||||
|
casvisorsdk.Record
|
||||||
|
ExtendedUser *User `xorm:"-" json:"extendedUser"`
|
||||||
|
}
|
||||||
|
recordEx := &RecordEx{
|
||||||
|
Record: *record,
|
||||||
|
ExtendedUser: extendedUser,
|
||||||
|
}
|
||||||
|
|
||||||
|
body = strings.NewReader(util.StructToJson(recordEx))
|
||||||
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(webhook.Method, webhook.Url, body)
|
req, err := http.NewRequest(webhook.Method, webhook.Url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -106,11 +106,14 @@ func getOrganizationThemeCookieFromUrlPath(ctx *context.Context, urlPath string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
organizationThemeCookie := &OrganizationThemeCookie{
|
organizationThemeCookie := &OrganizationThemeCookie{
|
||||||
application.ThemeData,
|
ThemeData: application.ThemeData,
|
||||||
application.Logo,
|
LogoUrl: application.Logo,
|
||||||
application.FooterHtml,
|
FooterHtml: application.FooterHtml,
|
||||||
organization.Favicon,
|
}
|
||||||
organization.DisplayName,
|
|
||||||
|
if organization != nil {
|
||||||
|
organizationThemeCookie.Favicon = organization.Favicon
|
||||||
|
organizationThemeCookie.DisplayName = organization.DisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
return organizationThemeCookie, nil
|
return organizationThemeCookie, nil
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/cssinjs": "^1.10.1",
|
"@ant-design/cssinjs": "^1.23.0",
|
||||||
"@ant-design/icons": "^4.7.0",
|
"@ant-design/icons": "^5.6.1",
|
||||||
"@craco/craco": "^6.4.5",
|
"@craco/craco": "^6.4.5",
|
||||||
"@crowdin/cli": "^3.7.10",
|
"@crowdin/cli": "^3.7.10",
|
||||||
"@ctrl/tinycolor": "^3.5.0",
|
"@ctrl/tinycolor": "^3.5.0",
|
||||||
@ -23,8 +23,8 @@
|
|||||||
"@web3-onboard/sequence": "^2.0.8",
|
"@web3-onboard/sequence": "^2.0.8",
|
||||||
"@web3-onboard/taho": "^2.0.5",
|
"@web3-onboard/taho": "^2.0.5",
|
||||||
"@web3-onboard/trust": "^2.0.4",
|
"@web3-onboard/trust": "^2.0.4",
|
||||||
"antd": "5.2.3",
|
"antd": "5.24.1",
|
||||||
"antd-token-previewer": "^1.1.0-22",
|
"antd-token-previewer": "^2.0.8",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"copy-to-clipboard": "^3.3.1",
|
"copy-to-clipboard": "^3.3.1",
|
||||||
|
@ -327,7 +327,7 @@ class App extends Component {
|
|||||||
isAiAssistantOpen: false,
|
isAiAssistantOpen: false,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
visible={this.state.isAiAssistantOpen}
|
open={this.state.isAiAssistantOpen}
|
||||||
>
|
>
|
||||||
<iframe id="iframeHelper" title={"iframeHelper"} src={`${Conf.AiAssistantUrl}/?isRaw=1`} width="100%" height="100%" scrolling="no" frameBorder="no" />
|
<iframe id="iframeHelper" title={"iframeHelper"} src={`${Conf.AiAssistantUrl}/?isRaw=1`} width="100%" height="100%" scrolling="no" frameBorder="no" />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
@ -58,6 +58,16 @@ img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.org-select {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
transform: translateY(50%);
|
||||||
|
margin: 0 10px !important;
|
||||||
|
float: right;
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
.rightDropDown {
|
.rightDropDown {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Card, Col, ConfigProvider, Input, InputNumber, Popover, Radio, Result, Row, Select, Switch, Upload} from "antd";
|
import {Button, Card, Col, ConfigProvider, Input, InputNumber, Popover, Radio, Result, Row, Select, Space, Switch, Upload} from "antd";
|
||||||
import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
|
import {CopyOutlined, HolderOutlined, LinkOutlined, UploadOutlined, UsergroupAddOutlined} from "@ant-design/icons";
|
||||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
import * as CertBackend from "./backend/CertBackend";
|
import * as CertBackend from "./backend/CertBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
@ -36,6 +36,7 @@ import ThemeEditor from "./common/theme/ThemeEditor";
|
|||||||
|
|
||||||
import SigninTable from "./table/SigninTable";
|
import SigninTable from "./table/SigninTable";
|
||||||
import Editor from "./common/Editor";
|
import Editor from "./common/Editor";
|
||||||
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
@ -116,6 +117,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
this.getApplication();
|
this.getApplication();
|
||||||
this.getOrganizations();
|
this.getOrganizations();
|
||||||
|
this.getGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
getApplication() {
|
getApplication() {
|
||||||
@ -167,6 +169,17 @@ class ApplicationEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGroups() {
|
||||||
|
GroupBackend.getGroups(this.state.organizationName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
groups: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getCerts(application) {
|
getCerts(application) {
|
||||||
let owner = application.organization;
|
let owner = application.organization;
|
||||||
if (application.isShared) {
|
if (application.isShared) {
|
||||||
@ -469,6 +482,31 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("ldap:Default group"), i18next.t("ldap:Default group - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Select virtual={false} style={{width: "100%"}} value={this.state.application.defaultGroup ?? []} onChange={(value => {
|
||||||
|
this.updateApplicationField("defaultGroup", value);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Option key={""} value={""}>
|
||||||
|
<Space>
|
||||||
|
{i18next.t("general:Default")}
|
||||||
|
</Space>
|
||||||
|
</Option>
|
||||||
|
{
|
||||||
|
this.state.groups?.map((group) => <Option key={group.name} value={`${group.owner}/${group.name}`}>
|
||||||
|
<Space>
|
||||||
|
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||||
|
{group.displayName}
|
||||||
|
</Space>
|
||||||
|
</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
{Setting.getLabel(i18next.t("application:Enable signup"), i18next.t("application:Enable signup - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Enable signup"), i18next.t("application:Enable signup - Tooltip"))} :
|
||||||
@ -804,6 +842,33 @@ class ApplicationEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("application:Background URL Mobile"), i18next.t("application:Background URL Mobile - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} style={(Setting.isMobile()) ? {maxWidth: "100%"} : {}}>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input prefix={<LinkOutlined />} value={this.state.application.formBackgroundUrlMobile} onChange={e => {
|
||||||
|
this.updateApplicationField("formBackgroundUrlMobile", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{i18next.t("general:Preview")}:
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<a target="_blank" rel="noreferrer" href={this.state.application.formBackgroundUrlMobile}>
|
||||||
|
<img src={this.state.application.formBackgroundUrlMobile} alt={this.state.application.formBackgroundUrlMobile} height={90} style={{marginBottom: "20px"}} />
|
||||||
|
</a>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("application:Custom CSS"), i18next.t("application:Custom CSS - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Custom CSS"), i18next.t("application:Custom CSS - Tooltip"))} :
|
||||||
|
@ -121,10 +121,12 @@ class BaseListPage extends React.Component {
|
|||||||
record[dataIndex]
|
record[dataIndex]
|
||||||
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
|
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
|
||||||
: "",
|
: "",
|
||||||
onFilterDropdownOpenChange: visible => {
|
filterDropdownProps: {
|
||||||
if (visible) {
|
onOpenChange: visible => {
|
||||||
setTimeout(() => this.searchInput.select(), 100);
|
if (visible) {
|
||||||
}
|
setTimeout(() => this.searchInput.select(), 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
const highlightContent = this.state.searchedColumn === dataIndex ? (
|
const highlightContent = this.state.searchedColumn === dataIndex ? (
|
||||||
@ -173,7 +175,7 @@ class BaseListPage extends React.Component {
|
|||||||
const steps = TourConfig.getSteps();
|
const steps = TourConfig.getSteps();
|
||||||
steps.map((item, index) => {
|
steps.map((item, index) => {
|
||||||
if (!index) {
|
if (!index) {
|
||||||
item.target = () => document.querySelector("table");
|
item.target = () => document.querySelector(".ant-table");
|
||||||
} else {
|
} else {
|
||||||
item.target = () => document.getElementById(item.id) || null;
|
item.target = () => document.getElementById(item.id) || null;
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,6 @@ import {Tabs} from "antd";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Editor from "./common/Editor";
|
import Editor from "./common/Editor";
|
||||||
|
|
||||||
const {TabPane} = Tabs;
|
|
||||||
|
|
||||||
const CasbinEditor = ({model, onModelTextChange}) => {
|
const CasbinEditor = ({model, onModelTextChange}) => {
|
||||||
const [activeKey, setActiveKey] = useState("advanced");
|
const [activeKey, setActiveKey] = useState("advanced");
|
||||||
const iframeRef = useRef(null);
|
const iframeRef = useRef(null);
|
||||||
@ -66,10 +64,15 @@ const CasbinEditor = ({model, onModelTextChange}) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column"}}>
|
<div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column"}}>
|
||||||
<Tabs activeKey={activeKey} onChange={handleTabChange} style={{flex: "0 0 auto", marginTop: "-10px"}}>
|
<Tabs
|
||||||
<TabPane tab={i18next.t("model:Basic Editor")} key="basic" />
|
activeKey={activeKey}
|
||||||
<TabPane tab={i18next.t("model:Advanced Editor")} key="advanced" />
|
onChange={handleTabChange}
|
||||||
</Tabs>
|
style={{flex: "0 0 auto", marginTop: "-10px"}}
|
||||||
|
items={[
|
||||||
|
{key: "basic", label: i18next.t("model:Basic Editor")},
|
||||||
|
{key: "advanced", label: i18next.t("model:Advanced Editor")},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<div style={{flex: "1 1 auto", overflow: "hidden"}}>
|
<div style={{flex: "1 1 auto", overflow: "hidden"}}>
|
||||||
{activeKey === "advanced" ? (
|
{activeKey === "advanced" ? (
|
||||||
<IframeEditor
|
<IframeEditor
|
||||||
|
@ -109,7 +109,7 @@ class EntryPage extends React.Component {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CustomHead headerHtml={this.state.application?.headerHtml} />
|
<CustomHead headerHtml={this.state.application?.headerHtml} />
|
||||||
<div className={`${isDarkMode ? "loginBackgroundDark" : "loginBackground"}`}
|
<div className={`${isDarkMode ? "loginBackgroundDark" : "loginBackground"}`}
|
||||||
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
|
style={{backgroundImage: Setting.inIframe() ? null : (Setting.isMobile() ? `url(${this.state.application?.formBackgroundUrlMobile})` : `url(${this.state.application?.formBackgroundUrl})`)}}>
|
||||||
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
|
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
|
||||||
style={{width: "100%", margin: "0 auto", position: "absolute"}} />
|
style={{width: "100%", margin: "0 auto", position: "absolute"}} />
|
||||||
<Switch>
|
<Switch>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import {Button, Table} from "antd";
|
import {Button, Table, Tooltip} from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import * as GroupBackend from "./backend/GroupBackend";
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
@ -202,12 +202,16 @@ class GroupListPage extends BaseListPage {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/groups/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/groups/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<PopconfirmModal
|
{
|
||||||
disabled={record.haveChildren}
|
record.haveChildren ? <Tooltip placement="topLeft" title={i18next.t("group:You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page")}>
|
||||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
<Button disabled type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||||
onConfirm={() => this.deleteGroup(index)}
|
</Tooltip> :
|
||||||
>
|
<PopconfirmModal
|
||||||
</PopconfirmModal>
|
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||||
|
onConfirm={() => this.deleteGroup(index)}
|
||||||
|
>
|
||||||
|
</PopconfirmModal>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -206,11 +206,11 @@ function ManagementPage(props) {
|
|||||||
<OrganizationSelect
|
<OrganizationSelect
|
||||||
initValue={Setting.getOrganization()}
|
initValue={Setting.getOrganization()}
|
||||||
withAll={true}
|
withAll={true}
|
||||||
style={{marginRight: "20px", width: "180px", display: !Setting.isMobile() ? "flex" : "none"}}
|
className="org-select"
|
||||||
|
style={{display: Setting.isMobile() ? "none" : "flex"}}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
Setting.setOrganization(value);
|
Setting.setOrganization(value);
|
||||||
}}
|
}}
|
||||||
className="select-box"
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -459,7 +459,7 @@ function ManagementPage(props) {
|
|||||||
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}} >
|
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}} >
|
||||||
{props.requiredEnableMfa || (Setting.isMobile() ?
|
{props.requiredEnableMfa || (Setting.isMobile() ?
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Drawer title={i18next.t("general:Close")} placement="left" visible={menuVisible} onClose={onClose}>
|
<Drawer title={i18next.t("general:Close")} placement="left" open={menuVisible} onClose={onClose}>
|
||||||
<Menu
|
<Menu
|
||||||
items={getMenuItems()}
|
items={getMenuItems()}
|
||||||
mode={"inline"}
|
mode={"inline"}
|
||||||
|
@ -113,8 +113,8 @@ class PermissionListPage extends BaseListPage {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Upload {...props}>
|
<Upload {...props}>
|
||||||
<Button id="upload-button" type="primary" size="small">
|
<Button icon={<UploadOutlined />} id="upload-button" type="primary" size="small">
|
||||||
<UploadOutlined /> {i18next.t("user:Upload (.xlsx)")}
|
{i18next.t("user:Upload (.xlsx)")}
|
||||||
</Button></Upload>
|
</Button></Upload>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ class RoleListPage extends BaseListPage {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Upload {...props}>
|
<Upload {...props}>
|
||||||
<Button type="primary" size="small">
|
<Button icon={<UploadOutlined />} type="primary" size="small">
|
||||||
<UploadOutlined /> {i18next.t("user:Upload (.xlsx)")}
|
{i18next.t("user:Upload (.xlsx)")}
|
||||||
</Button>
|
</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
);
|
);
|
||||||
|
@ -188,8 +188,8 @@ class UserListPage extends BaseListPage {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Upload {...props}>
|
<Upload {...props}>
|
||||||
<Button id="upload-button" type="primary" size="small">
|
<Button icon={<UploadOutlined />} id="upload-button" type="primary" size="small">
|
||||||
<UploadOutlined /> {i18next.t("user:Upload (.xlsx)")}
|
{i18next.t("user:Upload (.xlsx)")}
|
||||||
</Button>
|
</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
);
|
);
|
||||||
|
@ -174,7 +174,16 @@ class WebhookEditPage extends React.Component {
|
|||||||
renderWebhook() {
|
renderWebhook() {
|
||||||
const preview = Setting.deepCopy(previewTemplate);
|
const preview = Setting.deepCopy(previewTemplate);
|
||||||
if (this.state.webhook.isUserExtended) {
|
if (this.state.webhook.isUserExtended) {
|
||||||
preview["extendedUser"] = userTemplate;
|
if (this.state.webhook.tokenFields && this.state.webhook.tokenFields.length !== 0) {
|
||||||
|
const extendedUser = {};
|
||||||
|
this.state.webhook.tokenFields.forEach(field => {
|
||||||
|
const fieldTrans = field.replace(field[0], field[0].toLowerCase());
|
||||||
|
extendedUser[fieldTrans] = userTemplate[fieldTrans];
|
||||||
|
});
|
||||||
|
preview["extendedUser"] = extendedUser;
|
||||||
|
} else {
|
||||||
|
preview["extendedUser"] = userTemplate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const previewText = JSON.stringify(preview, null, 2);
|
const previewText = JSON.stringify(preview, null, 2);
|
||||||
|
|
||||||
@ -295,6 +304,18 @@ class WebhookEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("application:Extended user fields"), i18next.t("application:Extended user fields - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} mode="tags" showSearch style={{width: "100%"}} value={this.state.webhook.tokenFields} onChange={(value => {this.updateWebhookField("tokenFields", value);})}>
|
||||||
|
{
|
||||||
|
Setting.getUserCommonFields().map((item, index) => <Option key={index} value={item}>{item}</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :
|
||||||
|
@ -471,9 +471,12 @@ class ForgetPage extends React.Component {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CustomGithubCorner />
|
<CustomGithubCorner />
|
||||||
<div className="forget-content" style={{padding: Setting.isMobile() ? "0" : null, boxShadow: Setting.isMobile() ? "none" : null}}>
|
<div className="forget-content" style={{padding: Setting.isMobile() ? "0" : null, boxShadow: Setting.isMobile() ? "none" : null}}>
|
||||||
<Button type="text" style={{position: "relative", left: Setting.isMobile() ? "10px" : "-90px", top: 0}} size={"large"} onClick={() => {this.stepBack();}}>
|
<Button type="text"
|
||||||
<ArrowLeftOutlined style={{fontSize: "24px"}} />
|
style={{position: "relative", left: Setting.isMobile() ? "10px" : "-90px", top: 0}}
|
||||||
</Button>
|
icon={<ArrowLeftOutlined style={{fontSize: "24px"}} />}
|
||||||
|
size={"large"}
|
||||||
|
onClick={() => {this.stepBack();}}
|
||||||
|
/>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} style={{justifyContent: "center"}}>
|
<Col span={24} style={{justifyContent: "center"}}>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -61,6 +61,7 @@ class LoginPage extends React.Component {
|
|||||||
isTermsOfUseVisible: false,
|
isTermsOfUseVisible: false,
|
||||||
termsOfUseContent: "",
|
termsOfUseContent: "",
|
||||||
orgChoiceMode: new URLSearchParams(props.location?.search).get("orgChoiceMode") ?? null,
|
orgChoiceMode: new URLSearchParams(props.location?.search).get("orgChoiceMode") ?? null,
|
||||||
|
userLang: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
|
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
|
||||||
@ -415,6 +416,7 @@ class LoginPage extends React.Component {
|
|||||||
|
|
||||||
login(values) {
|
login(values) {
|
||||||
// here we are supposed to determine whether Casdoor is working as an OAuth server or CAS server
|
// here we are supposed to determine whether Casdoor is working as an OAuth server or CAS server
|
||||||
|
values["language"] = this.state.userLang ?? "";
|
||||||
if (this.state.type === "cas") {
|
if (this.state.type === "cas") {
|
||||||
// CAS
|
// CAS
|
||||||
const casParams = Util.getCasParameters();
|
const casParams = Util.getCasParameters();
|
||||||
@ -530,9 +532,11 @@ class LoginPage extends React.Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resultItemKey = `${application.organization}_${application.name}_${signinItem.name}`;
|
||||||
|
|
||||||
if (signinItem.name === "Logo") {
|
if (signinItem.name === "Logo") {
|
||||||
return (
|
return (
|
||||||
<div className="login-logo-box">
|
<div key={resultItemKey} className="login-logo-box">
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
{
|
{
|
||||||
Setting.renderHelmet(application)
|
Setting.renderHelmet(application)
|
||||||
@ -544,7 +548,7 @@ class LoginPage extends React.Component {
|
|||||||
);
|
);
|
||||||
} else if (signinItem.name === "Back button") {
|
} else if (signinItem.name === "Back button") {
|
||||||
return (
|
return (
|
||||||
<div className="back-button">
|
<div key={resultItemKey} className="back-button">
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
{
|
{
|
||||||
this.renderBackButton()
|
this.renderBackButton()
|
||||||
@ -562,14 +566,14 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="login-languages">
|
<div key={resultItemKey} className="login-languages">
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
<LanguageSelect languages={application.organizationObj.languages} />
|
<LanguageSelect languages={application.organizationObj.languages} onClick={key => {this.setState({userLang: key});}} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (signinItem.name === "Signin methods") {
|
} else if (signinItem.name === "Signin methods") {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div key={resultItemKey}>
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
{this.renderMethodChoiceBox()}
|
{this.renderMethodChoiceBox()}
|
||||||
</div>
|
</div>
|
||||||
@ -577,7 +581,7 @@ class LoginPage extends React.Component {
|
|||||||
;
|
;
|
||||||
} else if (signinItem.name === "Username") {
|
} else if (signinItem.name === "Username") {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div key={resultItemKey}>
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="username"
|
name="username"
|
||||||
@ -654,14 +658,14 @@ class LoginPage extends React.Component {
|
|||||||
);
|
);
|
||||||
} else if (signinItem.name === "Password") {
|
} else if (signinItem.name === "Password") {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div key={resultItemKey}>
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
{this.renderPasswordOrCodeInput(signinItem)}
|
{this.renderPasswordOrCodeInput(signinItem)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (signinItem.name === "Forgot password?") {
|
} else if (signinItem.name === "Forgot password?") {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div key={resultItemKey}>
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
<div className="login-forget-password">
|
<div className="login-forget-password">
|
||||||
<Form.Item name="autoSignin" valuePropName="checked" noStyle>
|
<Form.Item name="autoSignin" valuePropName="checked" noStyle>
|
||||||
@ -679,7 +683,7 @@ class LoginPage extends React.Component {
|
|||||||
return AgreementModal.isAgreementRequired(application) ? AgreementModal.renderAgreementFormItem(application, true, {}, this) : null;
|
return AgreementModal.isAgreementRequired(application) ? AgreementModal.renderAgreementFormItem(application, true, {}, this) : null;
|
||||||
} else if (signinItem.name === "Login button") {
|
} else if (signinItem.name === "Login button") {
|
||||||
return (
|
return (
|
||||||
<Form.Item className="login-button-box">
|
<Form.Item key={resultItemKey} className="login-button-box">
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -723,13 +727,13 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div key={resultItemKey}>
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
{
|
{
|
||||||
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map((providerItem, id) => {
|
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map((providerItem, id) => {
|
||||||
return (
|
return (
|
||||||
<span key ={id} onClick={(e) => {
|
<span key={id} onClick={(e) => {
|
||||||
const agreementChecked = this.form.current.getFieldValue("agreement");
|
const agreementChecked = this.form.current.getFieldValue("agreement");
|
||||||
|
|
||||||
if (agreementChecked !== undefined && typeof agreementChecked === "boolean" && !agreementChecked) {
|
if (agreementChecked !== undefined && typeof agreementChecked === "boolean" && !agreementChecked) {
|
||||||
@ -752,11 +756,11 @@ class LoginPage extends React.Component {
|
|||||||
);
|
);
|
||||||
} else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) {
|
} else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) {
|
||||||
return (
|
return (
|
||||||
<div dangerouslySetInnerHTML={{__html: signinItem.customCss}} />
|
<div key={resultItemKey} dangerouslySetInnerHTML={{__html: signinItem.customCss}} />
|
||||||
);
|
);
|
||||||
} else if (signinItem.name === "Signup link") {
|
} else if (signinItem.name === "Signup link") {
|
||||||
return (
|
return (
|
||||||
<div style={{width: "100%"}} className="login-signup-link">
|
<div key={resultItemKey} style={{width: "100%"}} className="login-signup-link">
|
||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
{this.renderFooter(application, signinItem)}
|
{this.renderFooter(application, signinItem)}
|
||||||
</div>
|
</div>
|
||||||
@ -803,7 +807,6 @@ class LoginPage extends React.Component {
|
|||||||
<Form
|
<Form
|
||||||
name="normal_login"
|
name="normal_login"
|
||||||
initialValues={{
|
initialValues={{
|
||||||
|
|
||||||
organization: application.organization,
|
organization: application.organization,
|
||||||
application: application.name,
|
application: application.name,
|
||||||
autoSignin: true,
|
autoSignin: true,
|
||||||
|
@ -420,7 +420,7 @@ export function getAuthUrl(application, provider, method, code) {
|
|||||||
} else if (provider.type === "AzureADB2C") {
|
} else if (provider.type === "AzureADB2C") {
|
||||||
return `https://${provider.domain}.b2clogin.com/${provider.domain}.onmicrosoft.com/${provider.appId}/oauth2/v2.0/authorize?client_id=${provider.clientId}&nonce=defaultNonce&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}&prompt=login`;
|
return `https://${provider.domain}.b2clogin.com/${provider.domain}.onmicrosoft.com/${provider.appId}/oauth2/v2.0/authorize?client_id=${provider.clientId}&nonce=defaultNonce&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}&prompt=login`;
|
||||||
} else if (provider.type === "DingTalk") {
|
} else if (provider.type === "DingTalk") {
|
||||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&prompt=consent&state=${state}`;
|
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&prompt=login%20consent&state=${state}`;
|
||||||
} else if (provider.type === "WeChat") {
|
} else if (provider.type === "WeChat") {
|
||||||
if (navigator.userAgent.includes("MicroMessenger")) {
|
if (navigator.userAgent.includes("MicroMessenger")) {
|
||||||
return `${authInfo[provider.type].mpEndpoint}?appid=${provider.clientId2}&redirect_uri=${redirectUri}&state=${state}&scope=${authInfo[provider.type].mpScope}&response_type=code#wechat_redirect`;
|
return `${authInfo[provider.type].mpEndpoint}?appid=${provider.clientId2}&redirect_uri=${redirectUri}&state=${state}&scope=${authInfo[provider.type].mpScope}&response_type=code#wechat_redirect`;
|
||||||
|
@ -167,25 +167,35 @@ const Dashboard = (props) => {
|
|||||||
};
|
};
|
||||||
myChart.setOption(option);
|
myChart.setOption(option);
|
||||||
|
|
||||||
|
const cardStyles = {
|
||||||
|
body: {
|
||||||
|
width: Setting.isMobile() ? "340px" : "100%",
|
||||||
|
height: Setting.isMobile() ? "100px" : "150px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row id="statistic" gutter={80} justify={"center"}>
|
<Row id="statistic" gutter={80} justify={"center"}>
|
||||||
<Col span={50} style={{marginBottom: "10px"}}>
|
<Col span={50} style={{marginBottom: "10px"}}>
|
||||||
<Card bordered={false} bodyStyle={{width: Setting.isMobile() ? "340px" : "100%", height: Setting.isMobile() ? "100px" : "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
|
<Card variant="borderless" styles={cardStyles}>
|
||||||
<Statistic title={i18next.t("home:Total users")} fontSize="100px" value={dashboardData.userCounts[30]} valueStyle={{fontSize: "30px"}} style={{width: "200px", paddingLeft: "10px"}} />
|
<Statistic title={i18next.t("home:Total users")} fontSize="100px" value={dashboardData.userCounts[30]} valueStyle={{fontSize: "30px"}} style={{width: "200px", paddingLeft: "10px"}} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={50} style={{marginBottom: "10px"}}>
|
<Col span={50} style={{marginBottom: "10px"}}>
|
||||||
<Card bordered={false} bodyStyle={{width: Setting.isMobile() ? "340px" : "100%", height: Setting.isMobile() ? "100px" : "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
|
<Card variant="borderless" styles={cardStyles}>
|
||||||
<Statistic title={i18next.t("home:New users today")} fontSize="100px" value={dashboardData.userCounts[30] - dashboardData.userCounts[30 - 1]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
|
<Statistic title={i18next.t("home:New users today")} fontSize="100px" value={dashboardData.userCounts[30] - dashboardData.userCounts[30 - 1]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={50} style={{marginBottom: "10px"}}>
|
<Col span={50} style={{marginBottom: "10px"}}>
|
||||||
<Card bordered={false} bodyStyle={{width: Setting.isMobile() ? "340px" : "100%", height: Setting.isMobile() ? "100px" : "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
|
<Card variant="borderless" styles={cardStyles}>
|
||||||
<Statistic title={i18next.t("home:New users past 7 days")} value={dashboardData.userCounts[30] - dashboardData.userCounts[30 - 7]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
|
<Statistic title={i18next.t("home:New users past 7 days")} value={dashboardData.userCounts[30] - dashboardData.userCounts[30 - 7]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={50} style={{marginBottom: "10px"}}>
|
<Col span={50} style={{marginBottom: "10px"}}>
|
||||||
<Card bordered={false} bodyStyle={{width: Setting.isMobile() ? "340px" : "100%", height: Setting.isMobile() ? "100px" : "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
|
<Card variant="borderless" styles={cardStyles}>
|
||||||
<Statistic title={i18next.t("home:New users past 30 days")} value={dashboardData.userCounts[30] - dashboardData.userCounts[30 - 30]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
|
<Statistic title={i18next.t("home:New users past 30 days")} value={dashboardData.userCounts[30] - dashboardData.userCounts[30 - 30]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -31,7 +31,7 @@ const GridCards = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
Setting.isMobile() ? (
|
Setting.isMobile() ? (
|
||||||
<Card bodyStyle={{padding: 0}}>
|
<Card styles={{body: {padding: 0}}}>
|
||||||
{items.map(item => <SingleCard key={item.link} logo={item.logo} link={item.link} title={item.name} desc={item.description} isSingle={items.length === 1} />)}
|
{items.map(item => <SingleCard key={item.link} logo={item.logo} link={item.link} title={item.name} desc={item.description} isSingle={items.length === 1} />)}
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
|
@ -14,11 +14,12 @@
|
|||||||
|
|
||||||
import * as faceapi from "face-api.js";
|
import * as faceapi from "face-api.js";
|
||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
import {Button, Modal, Progress, Spin, message} from "antd";
|
import {Button, Modal, Progress, Space, Spin, message} from "antd";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import Dragger from "antd/es/upload/Dragger";
|
||||||
|
|
||||||
const FaceRecognitionModal = (props) => {
|
const FaceRecognitionModal = (props) => {
|
||||||
const {visible, onOk, onCancel} = props;
|
const {visible, onOk, onCancel, withImage} = props;
|
||||||
const [modelsLoaded, setModelsLoaded] = React.useState(false);
|
const [modelsLoaded, setModelsLoaded] = React.useState(false);
|
||||||
const [isCameraCaptured, setIsCameraCaptured] = useState(false);
|
const [isCameraCaptured, setIsCameraCaptured] = useState(false);
|
||||||
|
|
||||||
@ -28,6 +29,10 @@ const FaceRecognitionModal = (props) => {
|
|||||||
const mediaStreamRef = React.useRef(null);
|
const mediaStreamRef = React.useRef(null);
|
||||||
const [percent, setPercent] = useState(0);
|
const [percent, setPercent] = useState(0);
|
||||||
|
|
||||||
|
const [files, setFiles] = useState([]);
|
||||||
|
const [currentFaceId, setCurrentFaceId] = React.useState();
|
||||||
|
const [currentFaceIndex, setCurrentFaceIndex] = React.useState();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const loadModels = async() => {
|
const loadModels = async() => {
|
||||||
// const MODEL_URL = process.env.PUBLIC_URL + "/models";
|
// const MODEL_URL = process.env.PUBLIC_URL + "/models";
|
||||||
@ -50,6 +55,9 @@ const FaceRecognitionModal = (props) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (withImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (visible) {
|
if (visible) {
|
||||||
setPercent(0);
|
setPercent(0);
|
||||||
if (modelsLoaded) {
|
if (modelsLoaded) {
|
||||||
@ -75,6 +83,9 @@ const FaceRecognitionModal = (props) => {
|
|||||||
}, [visible, modelsLoaded]);
|
}, [visible, modelsLoaded]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (withImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isCameraCaptured) {
|
if (isCameraCaptured) {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@ -98,6 +109,9 @@ const FaceRecognitionModal = (props) => {
|
|||||||
}, [isCameraCaptured]);
|
}, [isCameraCaptured]);
|
||||||
|
|
||||||
const handleStreamVideo = () => {
|
const handleStreamVideo = () => {
|
||||||
|
if (withImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let goodCount = 0;
|
let goodCount = 0;
|
||||||
if (!detection.current) {
|
if (!detection.current) {
|
||||||
@ -148,73 +162,163 @@ const FaceRecognitionModal = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const getBase64 = (file) => {
|
||||||
<div>
|
return new Promise((resolve, reject) => {
|
||||||
<Modal
|
const reader = new FileReader();
|
||||||
closable={false}
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => resolve(reader.result);
|
||||||
|
reader.onerror = (error) => reject(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!withImage) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Modal
|
||||||
|
closable={false}
|
||||||
|
maskClosable={false}
|
||||||
|
destroyOnClose={true}
|
||||||
|
open={visible && isCameraCaptured}
|
||||||
|
title={i18next.t("login:Face Recognition")}
|
||||||
|
width={350}
|
||||||
|
footer={[
|
||||||
|
<Button key="back" onClick={onCancel}>
|
||||||
|
Cancel
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Progress percent={percent} />
|
||||||
|
<div style={{
|
||||||
|
marginTop: "20px",
|
||||||
|
marginBottom: "50px",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignContent: "center",
|
||||||
|
position: "relative",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
modelsLoaded ?
|
||||||
|
<div style={{display: "flex", justifyContent: "center", alignContent: "center"}}>
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
onPlay={handleStreamVideo}
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
height: "220px",
|
||||||
|
verticalAlign: "middle",
|
||||||
|
width: "220px",
|
||||||
|
objectFit: "cover",
|
||||||
|
}}
|
||||||
|
></video>
|
||||||
|
<div style={{
|
||||||
|
position: "absolute",
|
||||||
|
width: "240px",
|
||||||
|
height: "240px",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
}}>
|
||||||
|
<svg width="240" height="240" fill="none">
|
||||||
|
<circle
|
||||||
|
strokeDasharray="700"
|
||||||
|
strokeDashoffset={700 - 6.9115 * percent}
|
||||||
|
strokeWidth="4"
|
||||||
|
cx="120"
|
||||||
|
cy="120"
|
||||||
|
r="110"
|
||||||
|
stroke="#5734d3"
|
||||||
|
transform="rotate(-90, 120, 120)"
|
||||||
|
strokeLinecap="round"
|
||||||
|
style={{transition: "all .2s linear"}}
|
||||||
|
></circle>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<canvas ref={canvasRef} style={{position: "absolute"}} />
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
<Spin tip={i18next.t("login:Loading")} size="large"
|
||||||
|
style={{display: "flex", justifyContent: "center", alignContent: "center"}}>
|
||||||
|
<div className="content" />
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div>
|
||||||
|
<Modal closable={false}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
destroyOnClose={true}
|
destroyOnClose={true}
|
||||||
open={visible && isCameraCaptured}
|
open={visible}
|
||||||
title={i18next.t("login:Face Recognition")}
|
title={i18next.t("login:Face Recognition")}
|
||||||
width={350}
|
width={350}
|
||||||
footer={[
|
footer={[
|
||||||
<Button key="back" onClick={onCancel}>
|
<Button key="ok" type={"primary"} disabled={!currentFaceId || currentFaceId?.length === 0} onClick={() => {
|
||||||
Cancel
|
onOk(Array.from(currentFaceId.descriptor));
|
||||||
|
}}>
|
||||||
|
Ok
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
<Button key="back" onClick={onCancel}>
|
||||||
>
|
Cancel
|
||||||
<Progress percent={percent} />
|
</Button>,
|
||||||
<div style={{marginTop: "20px", marginBottom: "50px", justifyContent: "center", alignContent: "center", position: "relative", flexDirection: "column"}}>
|
]}>
|
||||||
|
<Space direction={"vertical"} style={{width: "100%"}}>
|
||||||
|
<Dragger
|
||||||
|
multiple={true}
|
||||||
|
defaultFileList={files}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
beforeUpload={(file) => {
|
||||||
|
getBase64(file).then(res => {
|
||||||
|
file.base64 = res;
|
||||||
|
files.push(file);
|
||||||
|
});
|
||||||
|
setCurrentFaceId([]);
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
onRemove={(file) => {
|
||||||
|
const index = files.indexOf(file);
|
||||||
|
const newFileList = files.slice();
|
||||||
|
newFileList.splice(index, 1);
|
||||||
|
setFiles(newFileList);
|
||||||
|
setCurrentFaceId([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>{i18next.t("general:Click to Upload")}</p>
|
||||||
|
</Dragger >
|
||||||
{
|
{
|
||||||
modelsLoaded ?
|
modelsLoaded ? <Button style={{width: "100%"}} onClick={async() => {
|
||||||
<div style={{display: "flex", justifyContent: "center", alignContent: "center"}}>
|
let maxScore = 0;
|
||||||
<video
|
for (const file of files) {
|
||||||
ref={videoRef}
|
const fileIndex = files.indexOf(file);
|
||||||
onPlay={handleStreamVideo}
|
const img = new Image();
|
||||||
style={{
|
img.src = file.base64;
|
||||||
borderRadius: "50%",
|
const faceIds = await faceapi.detectAllFaces(img, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceDescriptors();
|
||||||
height: "220px",
|
if (faceIds[0]?.detection.score > 0.9 && faceIds[0]?.detection.score > maxScore) {
|
||||||
verticalAlign: "middle",
|
maxScore = faceIds[0]?.detection.score;
|
||||||
width: "220px",
|
setCurrentFaceId(faceIds[0]);
|
||||||
objectFit: "cover",
|
setCurrentFaceIndex(fileIndex);
|
||||||
}}
|
}
|
||||||
></video>
|
}
|
||||||
<div style={{
|
if (maxScore < 0.9) {
|
||||||
position: "absolute",
|
message.error(i18next.t("login:Face recognition failed"));
|
||||||
width: "240px",
|
}
|
||||||
height: "240px",
|
}}> {i18next.t("application:Generate faceId")}</Button> : null
|
||||||
top: "50%",
|
|
||||||
left: "50%",
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
}}>
|
|
||||||
<svg width="240" height="240" fill="none">
|
|
||||||
<circle
|
|
||||||
strokeDasharray="700"
|
|
||||||
strokeDashoffset={700 - 6.9115 * percent}
|
|
||||||
strokeWidth="4"
|
|
||||||
cx="120"
|
|
||||||
cy="120"
|
|
||||||
r="110"
|
|
||||||
stroke="#5734d3"
|
|
||||||
transform="rotate(-90, 120, 120)"
|
|
||||||
strokeLinecap="round"
|
|
||||||
style={{transition: "all .2s linear"}}
|
|
||||||
></circle>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<canvas ref={canvasRef} style={{position: "absolute"}} />
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<div>
|
|
||||||
<Spin tip={i18next.t("login:Loading")} size="large" style={{display: "flex", justifyContent: "center", alignContent: "center"}}>
|
|
||||||
<div className="content" />
|
|
||||||
</Spin>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</div>
|
</Space>
|
||||||
|
{
|
||||||
|
currentFaceId && currentFaceId.length !== 0 ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<div>{i18next.t("application:Select")}:{files[currentFaceIndex]?.name}</div>
|
||||||
|
<div><img src={files[currentFaceIndex]?.base64} alt="selected" style={{width: "100%"}} /></div>
|
||||||
|
</React.Fragment>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>;
|
||||||
);
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FaceRecognitionModal;
|
export default FaceRecognitionModal;
|
||||||
|
@ -30,6 +30,7 @@ class LanguageSelect extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
languages: props.languages ?? Setting.Countries.map(item => item.key),
|
languages: props.languages ?? Setting.Countries.map(item => item.key),
|
||||||
|
onClick: props.onClick,
|
||||||
};
|
};
|
||||||
|
|
||||||
Setting.Countries.forEach((country) => {
|
Setting.Countries.forEach((country) => {
|
||||||
@ -50,6 +51,9 @@ class LanguageSelect extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const languageItems = this.getOrganizationLanguages(this.state.languages);
|
const languageItems = this.getOrganizationLanguages(this.state.languages);
|
||||||
const onClick = (e) => {
|
const onClick = (e) => {
|
||||||
|
if (typeof this.state.onClick === "function") {
|
||||||
|
this.state.onClick(e.key);
|
||||||
|
}
|
||||||
Setting.setLanguage(e.key);
|
Setting.setLanguage(e.key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ function OrganizationSelect(props) {
|
|||||||
<Select
|
<Select
|
||||||
options={getOrganizationItems()}
|
options={getOrganizationItems()}
|
||||||
virtual={false}
|
virtual={false}
|
||||||
|
popupMatchSelectWidth={false}
|
||||||
placeholder={i18next.t("login:Please select an organization")}
|
placeholder={i18next.t("login:Please select an organization")}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
|
@ -147,7 +147,7 @@ class PricingPage extends React.Component {
|
|||||||
|
|
||||||
if (Setting.isMobile()) {
|
if (Setting.isMobile()) {
|
||||||
return (
|
return (
|
||||||
<Card style={{border: "none"}} bodyStyle={{padding: 0}}>
|
<Card style={{border: "none"}} styles={{body: {padding: 0}}}>
|
||||||
{
|
{
|
||||||
this.state.plans.map(item => {
|
this.state.plans.map(item => {
|
||||||
return item.period === this.state.selectedPeriod ? (
|
return item.period === this.state.selectedPeriod ? (
|
||||||
|
@ -97,12 +97,16 @@ class FaceIdTable extends React.Component {
|
|||||||
title={() => (
|
title={() => (
|
||||||
<div>
|
<div>
|
||||||
{i18next.t("user:Face IDs")}
|
{i18next.t("user:Face IDs")}
|
||||||
<Button disabled={this.props.table?.length >= 5} style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.setState({openFaceRecognitionModal: true})}>
|
<Button disabled={this.props.table?.length >= 5} style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.setState({openFaceRecognitionModal: true, withImage: false})}>
|
||||||
{i18next.t("general:Add Face Id")}
|
{i18next.t("general:Add Face Id")}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button disabled={this.props.table?.length >= 5} style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.setState({openFaceRecognitionModal: true, withImage: true})}>
|
||||||
|
{i18next.t("general:Add Face Id with image")}
|
||||||
|
</Button>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<FaceRecognitionModal
|
<FaceRecognitionModal
|
||||||
visible={this.state.openFaceRecognitionModal}
|
visible={this.state.openFaceRecognitionModal}
|
||||||
|
withImage={this.state.withImage}
|
||||||
onOk={(faceIdData) => {
|
onOk={(faceIdData) => {
|
||||||
this.addFaceId(table, faceIdData);
|
this.addFaceId(table, faceIdData);
|
||||||
this.setState({openFaceRecognitionModal: false});
|
this.setState({openFaceRecognitionModal: false});
|
||||||
|
1438
web/yarn.lock
1438
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user