mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-04 20:10:35 +08:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ee5c3f3f39 | ||
![]() |
714f69be7b | ||
![]() |
0d12972e92 | ||
![]() |
78b62c28ab | ||
![]() |
5c26335fd6 | ||
![]() |
7edaeafea5 | ||
![]() |
336f3f7a7b | ||
![]() |
47dc3715f9 | ||
![]() |
7503e05a4a | ||
![]() |
b89cf1de07 | ||
![]() |
be87078c25 | ||
![]() |
faf352acc5 | ||
![]() |
0db61dd658 | ||
![]() |
ebe8ad8669 | ||
![]() |
2e01f0d10e | ||
![]() |
754fa1e745 | ||
![]() |
8b9e0ba96b | ||
![]() |
b0656aca36 | ||
![]() |
623b4fee17 |
@@ -64,6 +64,7 @@ COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
|
|||||||
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
|
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
|
||||||
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
|
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
|
||||||
COPY --from=FRONT /web/build ./web/build
|
COPY --from=FRONT /web/build ./web/build
|
||||||
|
RUN mkdir tempFiles
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/bash"]
|
ENTRYPOINT ["/bin/bash"]
|
||||||
CMD ["/docker-entrypoint.sh"]
|
CMD ["/docker-entrypoint.sh"]
|
||||||
|
@@ -20,5 +20,5 @@ staticBaseUrl = "https://cdn.casbin.org"
|
|||||||
isDemoMode = false
|
isDemoMode = false
|
||||||
batchSize = 100
|
batchSize = 100
|
||||||
ldapServerPort = 389
|
ldapServerPort = 389
|
||||||
languages = en,zh,es,fr,de,id,ja,ko,ru,vn
|
languages = en,zh,es,fr,de,id,ja,ko,ru,vi
|
||||||
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
||||||
|
@@ -137,7 +137,7 @@ func (c *ApiController) Signup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var checkPhone string
|
var checkPhone string
|
||||||
if application.IsSignupItemVisible("Phone") && form.Phone != "" {
|
if application.IsSignupItemVisible("Phone") && application.GetSignupItemRule("Phone") != "No verification" && form.Phone != "" {
|
||||||
checkPhone, _ = util.GetE164Number(form.Phone, form.CountryCode)
|
checkPhone, _ = util.GetE164Number(form.Phone, form.CountryCode)
|
||||||
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
|
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
|
||||||
if checkResult.Code != object.VerificationSuccess {
|
if checkResult.Code != object.VerificationSuccess {
|
||||||
|
@@ -339,7 +339,7 @@ func (c *ApiController) Login() {
|
|||||||
userInfo := &idp.UserInfo{}
|
userInfo := &idp.UserInfo{}
|
||||||
if provider.Category == "SAML" {
|
if provider.Category == "SAML" {
|
||||||
// SAML
|
// SAML
|
||||||
userInfo.Id, err = object.ParseSamlResponse(form.SamlResponse, provider.Type)
|
userInfo.Id, err = object.ParseSamlResponse(form.SamlResponse, provider, c.Ctx.Request.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -546,7 +546,7 @@ func (c *ApiController) Login() {
|
|||||||
func (c *ApiController) GetSamlLogin() {
|
func (c *ApiController) GetSamlLogin() {
|
||||||
providerId := c.Input().Get("id")
|
providerId := c.Input().Get("id")
|
||||||
relayState := c.Input().Get("relayState")
|
relayState := c.Input().Get("relayState")
|
||||||
authURL, method, err := object.GenerateSamlLoginUrl(providerId, relayState, c.GetAcceptLanguage())
|
authURL, method, err := object.GenerateSamlRequest(providerId, relayState, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
}
|
}
|
||||||
|
@@ -37,8 +37,17 @@ func (c *ApiController) GetMessages() {
|
|||||||
value := c.Input().Get("value")
|
value := c.Input().Get("value")
|
||||||
sortField := c.Input().Get("sortField")
|
sortField := c.Input().Get("sortField")
|
||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
chat := c.Input().Get("chat")
|
||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
c.Data["json"] = object.GetMaskedMessages(object.GetMessages(owner))
|
var messages []*object.Message
|
||||||
|
if chat == "" {
|
||||||
|
messages = object.GetMessages(owner)
|
||||||
|
} else {
|
||||||
|
messages = object.GetChatMessages(chat)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetMaskedMessages(messages)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
|
2
go.mod
2
go.mod
@@ -17,6 +17,7 @@ require (
|
|||||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
||||||
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/fogleman/gg v1.3.0
|
||||||
github.com/forestmgy/ldapserver v1.1.0
|
github.com/forestmgy/ldapserver v1.1.0
|
||||||
github.com/go-git/go-git/v5 v5.6.0
|
github.com/go-git/go-git/v5 v5.6.0
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0
|
github.com/go-ldap/ldap/v3 v3.3.0
|
||||||
@@ -25,6 +26,7 @@ require (
|
|||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
github.com/go-webauthn/webauthn v0.6.0
|
github.com/go-webauthn/webauthn v0.6.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
|
5
go.sum
5
go.sum
@@ -173,6 +173,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/forestmgy/ldapserver v1.1.0 h1:gvil4nuLhqPEL8SugCkFhRyA0/lIvRdwZSqlrw63ll4=
|
github.com/forestmgy/ldapserver v1.1.0 h1:gvil4nuLhqPEL8SugCkFhRyA0/lIvRdwZSqlrw63ll4=
|
||||||
github.com/forestmgy/ldapserver v1.1.0/go.mod h1:1RZ8lox1QSY7rmbjdmy+sYQXY4Lp7SpGzpdE3+j3IyM=
|
github.com/forestmgy/ldapserver v1.1.0/go.mod h1:1RZ8lox1QSY7rmbjdmy+sYQXY4Lp7SpGzpdE3+j3IyM=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||||
@@ -239,6 +241,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW
|
|||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@@ -677,6 +681,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
@@ -31,7 +31,7 @@ func TestGenerateI18nFrontend(t *testing.T) {
|
|||||||
applyToOtherLanguage("frontend", "ja", data)
|
applyToOtherLanguage("frontend", "ja", data)
|
||||||
applyToOtherLanguage("frontend", "ko", data)
|
applyToOtherLanguage("frontend", "ko", data)
|
||||||
applyToOtherLanguage("frontend", "ru", data)
|
applyToOtherLanguage("frontend", "ru", data)
|
||||||
applyToOtherLanguage("frontend", "vn", data)
|
applyToOtherLanguage("frontend", "vi", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateI18nBackend(t *testing.T) {
|
func TestGenerateI18nBackend(t *testing.T) {
|
||||||
@@ -46,5 +46,5 @@ func TestGenerateI18nBackend(t *testing.T) {
|
|||||||
applyToOtherLanguage("backend", "ja", data)
|
applyToOtherLanguage("backend", "ja", data)
|
||||||
applyToOtherLanguage("backend", "ko", data)
|
applyToOtherLanguage("backend", "ko", data)
|
||||||
applyToOtherLanguage("backend", "ru", data)
|
applyToOtherLanguage("backend", "ru", data)
|
||||||
applyToOtherLanguage("backend", "vn", data)
|
applyToOtherLanguage("backend", "vi", data)
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
"defaultAvatar": "",
|
"defaultAvatar": "",
|
||||||
"defaultApplication": "",
|
"defaultApplication": "",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vn"],
|
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi"],
|
||||||
"masterPassword": "",
|
"masterPassword": "",
|
||||||
"initScore": 2000,
|
"initScore": 2000,
|
||||||
"enableSoftDeletion": false,
|
"enableSoftDeletion": false,
|
||||||
|
@@ -80,3 +80,21 @@ func DownloadAndUpload(url string, fullFilePath string, lang string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPermanentAvatarUrlFromBuffer(organization string, username string, fileBuffer *bytes.Buffer, ext string, upload bool) string {
|
||||||
|
if defaultStorageProvider == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fullFilePath := fmt.Sprintf("/avatar/%s/%s%s", organization, username, ext)
|
||||||
|
uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false)
|
||||||
|
|
||||||
|
if upload {
|
||||||
|
_, _, err := UploadFileSafe(defaultStorageProvider, fullFilePath, fileBuffer, "en")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uploadedFileUrl
|
||||||
|
}
|
||||||
|
@@ -16,6 +16,7 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
@@ -37,3 +38,22 @@ func TestSyncPermanentAvatars(t *testing.T) {
|
|||||||
fmt.Printf("[%d/%d]: Update user: [%s]'s permanent avatar: %s\n", i, len(users), user.GetId(), user.PermanentAvatar)
|
fmt.Printf("[%d/%d]: Update user: [%s]'s permanent avatar: %s\n", i, len(users), user.GetId(), user.PermanentAvatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateAvatars(t *testing.T) {
|
||||||
|
InitConfig()
|
||||||
|
InitDefaultStorageProvider()
|
||||||
|
proxy.InitHttpClient()
|
||||||
|
|
||||||
|
users := GetUsers("casdoor")
|
||||||
|
for _, user := range users {
|
||||||
|
if strings.HasPrefix(user.Avatar, "http") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
updated := user.refreshAvatar()
|
||||||
|
if updated {
|
||||||
|
user.PermanentAvatar = "*"
|
||||||
|
UpdateUser(user.GetId(), user, []string{"avatar"}, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
167
object/avatar_util.go
Normal file
167
object/avatar_util.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasGravatar(client *http.Client, email string) (bool, error) {
|
||||||
|
// Clean and lowercase the email
|
||||||
|
email = strings.TrimSpace(strings.ToLower(email))
|
||||||
|
|
||||||
|
// Generate MD5 hash of the email
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, email)
|
||||||
|
hashedEmail := fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
|
||||||
|
// Create Gravatar URL with d=404 parameter
|
||||||
|
gravatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s?d=404", hashedEmail)
|
||||||
|
|
||||||
|
// Send a request to Gravatar
|
||||||
|
req, err := http.NewRequest("GET", gravatarURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Check if the user has a custom Gravatar image
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return true, nil
|
||||||
|
} else if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
return false, fmt.Errorf("failed to fetch gravatar image: %s", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGravatarFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
||||||
|
// Clean and lowercase the email
|
||||||
|
email = strings.TrimSpace(strings.ToLower(email))
|
||||||
|
|
||||||
|
// Generate MD5 hash of the email
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, email)
|
||||||
|
hashedEmail := fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
|
||||||
|
// Create Gravatar URL
|
||||||
|
gravatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s", hashedEmail)
|
||||||
|
|
||||||
|
// Download the image
|
||||||
|
req, err := http.NewRequest("GET", gravatarURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, "", fmt.Errorf("failed to download gravatar image: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the content type and determine the file extension
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
fileExtension := ""
|
||||||
|
switch contentType {
|
||||||
|
case "image/jpeg":
|
||||||
|
fileExtension = ".jpg"
|
||||||
|
case "image/png":
|
||||||
|
fileExtension = ".png"
|
||||||
|
case "image/gif":
|
||||||
|
fileExtension = ".gif"
|
||||||
|
default:
|
||||||
|
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the image to a bytes.Buffer
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
_, err = io.Copy(buffer, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, fileExtension, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getColor(data []byte) color.RGBA {
|
||||||
|
r := int(data[0]) % 256
|
||||||
|
g := int(data[1]) % 256
|
||||||
|
b := int(data[2]) % 256
|
||||||
|
return color.RGBA{uint8(r), uint8(g), uint8(b), 255}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIdenticonFileBuffer(username string) (*bytes.Buffer, string, error) {
|
||||||
|
username = strings.TrimSpace(strings.ToLower(username))
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, username)
|
||||||
|
hashedUsername := hash.Sum(nil)
|
||||||
|
|
||||||
|
// Define the size of the image
|
||||||
|
const imageSize = 420
|
||||||
|
const cellSize = imageSize / 7
|
||||||
|
|
||||||
|
// Create a new image
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, imageSize, imageSize))
|
||||||
|
|
||||||
|
// Create a context
|
||||||
|
dc := gg.NewContextForRGBA(img)
|
||||||
|
|
||||||
|
// Set a background color
|
||||||
|
dc.SetColor(color.RGBA{240, 240, 240, 255})
|
||||||
|
dc.Clear()
|
||||||
|
|
||||||
|
// Get avatar color
|
||||||
|
avatarColor := getColor(hashedUsername)
|
||||||
|
|
||||||
|
// Draw cells
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
for j := 0; j < 7; j++ {
|
||||||
|
if (hashedUsername[i] >> uint(j) & 1) == 1 {
|
||||||
|
dc.SetColor(avatarColor)
|
||||||
|
dc.DrawRectangle(float64(j*cellSize), float64(i*cellSize), float64(cellSize), float64(cellSize))
|
||||||
|
dc.Fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save image to a bytes.Buffer
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
err := png.Encode(buffer, img)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to save image: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, ".png", nil
|
||||||
|
}
|
@@ -29,6 +29,8 @@ type Chat struct {
|
|||||||
|
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
|
Category string `xorm:"varchar(100)" json:"category"`
|
||||||
User1 string `xorm:"varchar(100)" json:"user1"`
|
User1 string `xorm:"varchar(100)" json:"user1"`
|
||||||
User2 string `xorm:"varchar(100)" json:"user2"`
|
User2 string `xorm:"varchar(100)" json:"user2"`
|
||||||
Users []string `xorm:"varchar(100)" json:"users"`
|
Users []string `xorm:"varchar(100)" json:"users"`
|
||||||
|
@@ -89,7 +89,7 @@ func initBuiltInOrganization() bool {
|
|||||||
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
|
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
|
||||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
||||||
Tags: []string{},
|
Tags: []string{},
|
||||||
Languages: []string{"en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vn"},
|
Languages: []string{"en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi"},
|
||||||
InitScore: 2000,
|
InitScore: 2000,
|
||||||
AccountItems: getBuiltInAccountItems(),
|
AccountItems: getBuiltInAccountItems(),
|
||||||
EnableSoftDeletion: false,
|
EnableSoftDeletion: false,
|
||||||
|
@@ -27,7 +27,7 @@ type Message struct {
|
|||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
Chat string `xorm:"varchar(100)" json:"chat"`
|
Chat string `xorm:"varchar(100) index" json:"chat"`
|
||||||
Author string `xorm:"varchar(100)" json:"author"`
|
Author string `xorm:"varchar(100)" json:"author"`
|
||||||
Text string `xorm:"mediumtext" json:"text"`
|
Text string `xorm:"mediumtext" json:"text"`
|
||||||
}
|
}
|
||||||
@@ -67,6 +67,16 @@ func GetMessages(owner string) []*Message {
|
|||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetChatMessages(chat string) []*Message {
|
||||||
|
messages := []*Message{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&messages, &Message{Chat: chat})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
func GetPaginationMessages(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Message {
|
func GetPaginationMessages(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Message {
|
||||||
messages := []*Message{}
|
messages := []*Message{}
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
@@ -18,6 +18,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
@@ -43,6 +44,26 @@ type OidcDiscovery struct {
|
|||||||
EndSessionEndpoint string `json:"end_session_endpoint"`
|
EndSessionEndpoint string `json:"end_session_endpoint"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isIpAddress(host string) bool {
|
||||||
|
// Attempt to split the host and port, ignoring the error
|
||||||
|
hostWithoutPort, _, err := net.SplitHostPort(host)
|
||||||
|
if err != nil {
|
||||||
|
// If an error occurs, it might be because there's no port
|
||||||
|
// In that case, use the original host string
|
||||||
|
hostWithoutPort = host
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse the host as an IP address (both IPv4 and IPv6)
|
||||||
|
ip := net.ParseIP(hostWithoutPort)
|
||||||
|
if ip != nil {
|
||||||
|
// The host is an IP address
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The host is not an IP address
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func getOriginFromHost(host string) (string, string) {
|
func getOriginFromHost(host string) (string, string) {
|
||||||
origin := conf.GetConfigString("origin")
|
origin := conf.GetConfigString("origin")
|
||||||
if origin != "" {
|
if origin != "" {
|
||||||
@@ -52,6 +73,8 @@ func getOriginFromHost(host string) (string, string) {
|
|||||||
protocol := "https://"
|
protocol := "https://"
|
||||||
if strings.HasPrefix(host, "localhost") {
|
if strings.HasPrefix(host, "localhost") {
|
||||||
protocol = "http://"
|
protocol = "http://"
|
||||||
|
} else if isIpAddress(host) {
|
||||||
|
protocol = "http://"
|
||||||
}
|
}
|
||||||
|
|
||||||
if host == "localhost:8000" {
|
if host == "localhost:8000" {
|
||||||
|
@@ -130,7 +130,7 @@ func getGroupingPolicies(permission *Permission) [][]string {
|
|||||||
for _, subUser := range roleObj.Users {
|
for _, subUser := range roleObj.Users {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
|
groupingPolicies = append(groupingPolicies, []string{subUser, role, domain, "", "", permissionId})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
|
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
|
||||||
@@ -140,7 +140,7 @@ func getGroupingPolicies(permission *Permission) [][]string {
|
|||||||
for _, subRole := range roleObj.Roles {
|
for _, subRole := range roleObj.Roles {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
|
groupingPolicies = append(groupingPolicies, []string{subRole, role, domain, "", "", permissionId})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
|
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
|
||||||
|
@@ -30,7 +30,10 @@ func TestProduct(t *testing.T) {
|
|||||||
product := GetProduct("admin/product_123")
|
product := GetProduct("admin/product_123")
|
||||||
provider := getProvider(product.Owner, "provider_pay_alipay")
|
provider := getProvider(product.Owner, "provider_pay_alipay")
|
||||||
cert := getCert(product.Owner, "cert-pay-alipay")
|
cert := getCert(product.Owner, "cert-pay-alipay")
|
||||||
pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey, provider.ClientId2)
|
pProvider, err := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey, provider.ClientId2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
paymentName := util.GenerateTimeId()
|
paymentName := util.GenerateTimeId()
|
||||||
returnUrl := ""
|
returnUrl := ""
|
||||||
|
@@ -23,29 +23,32 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
|
||||||
"github.com/casdoor/casdoor/i18n"
|
"github.com/casdoor/casdoor/i18n"
|
||||||
saml2 "github.com/russellhaering/gosaml2"
|
saml2 "github.com/russellhaering/gosaml2"
|
||||||
dsig "github.com/russellhaering/goxmldsig"
|
dsig "github.com/russellhaering/goxmldsig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseSamlResponse(samlResponse string, providerType string) (string, error) {
|
func ParseSamlResponse(samlResponse string, provider *Provider, host string) (string, error) {
|
||||||
samlResponse, _ = url.QueryUnescape(samlResponse)
|
samlResponse, _ = url.QueryUnescape(samlResponse)
|
||||||
sp, err := buildSp(&Provider{Type: providerType}, samlResponse)
|
sp, err := buildSp(provider, samlResponse, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
assertionInfo, err := sp.RetrieveAssertionInfo(samlResponse)
|
|
||||||
|
|
||||||
|
assertionInfo, err := sp.RetrieveAssertionInfo(samlResponse)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return assertionInfo.NameID, err
|
return assertionInfo.NameID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateSamlLoginUrl(id, relayState, lang string) (auth string, method string, err error) {
|
func GenerateSamlRequest(id, relayState, host, lang string) (auth string, method string, err error) {
|
||||||
provider := GetProvider(id)
|
provider := GetProvider(id)
|
||||||
if provider.Category != "SAML" {
|
if provider.Category != "SAML" {
|
||||||
return "", "", fmt.Errorf(i18n.Translate(lang, "saml_sp:provider %s's category is not SAML"), provider.Name)
|
return "", "", fmt.Errorf(i18n.Translate(lang, "saml_sp:provider %s's category is not SAML"), provider.Name)
|
||||||
}
|
}
|
||||||
sp, err := buildSp(provider, "")
|
|
||||||
|
sp, err := buildSp(provider, "", host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
@@ -67,35 +70,22 @@ func GenerateSamlLoginUrl(id, relayState, lang string) (auth string, method stri
|
|||||||
return auth, method, nil
|
return auth, method, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSp(provider *Provider, samlResponse string) (*saml2.SAMLServiceProvider, error) {
|
func buildSp(provider *Provider, samlResponse string, host string) (*saml2.SAMLServiceProvider, error) {
|
||||||
origin := conf.GetConfigString("origin")
|
_, origin := getOriginFromHost(host)
|
||||||
|
|
||||||
certStore := dsig.MemoryX509CertificateStore{
|
certStore, err := buildSpCertificateStore(provider, samlResponse)
|
||||||
Roots: []*x509.Certificate{},
|
|
||||||
}
|
|
||||||
|
|
||||||
certEncodedData := ""
|
|
||||||
if samlResponse != "" {
|
|
||||||
certEncodedData = parseSamlResponse(samlResponse, provider.Type)
|
|
||||||
} else if provider.IdP != "" {
|
|
||||||
certEncodedData = provider.IdP
|
|
||||||
}
|
|
||||||
certData, err := base64.StdEncoding.DecodeString(certEncodedData)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
idpCert, err := x509.ParseCertificate(certData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
certStore.Roots = append(certStore.Roots, idpCert)
|
|
||||||
sp := &saml2.SAMLServiceProvider{
|
sp := &saml2.SAMLServiceProvider{
|
||||||
ServiceProviderIssuer: fmt.Sprintf("%s/api/acs", origin),
|
ServiceProviderIssuer: fmt.Sprintf("%s/api/acs", origin),
|
||||||
AssertionConsumerServiceURL: fmt.Sprintf("%s/api/acs", origin),
|
AssertionConsumerServiceURL: fmt.Sprintf("%s/api/acs", origin),
|
||||||
IDPCertificateStore: &certStore,
|
|
||||||
SignAuthnRequests: false,
|
SignAuthnRequests: false,
|
||||||
|
IDPCertificateStore: &certStore,
|
||||||
SPKeyStore: dsig.RandomKeyStoreForTest(),
|
SPKeyStore: dsig.RandomKeyStoreForTest(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.Endpoint != "" {
|
if provider.Endpoint != "" {
|
||||||
sp.IdentityProviderSSOURL = provider.Endpoint
|
sp.IdentityProviderSSOURL = provider.Endpoint
|
||||||
sp.IdentityProviderIssuer = provider.IssuerUrl
|
sp.IdentityProviderIssuer = provider.IssuerUrl
|
||||||
@@ -104,10 +94,45 @@ func buildSp(provider *Provider, samlResponse string) (*saml2.SAMLServiceProvide
|
|||||||
sp.SignAuthnRequests = true
|
sp.SignAuthnRequests = true
|
||||||
sp.SPKeyStore = buildSpKeyStore()
|
sp.SPKeyStore = buildSpKeyStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
return sp, nil
|
return sp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSamlResponse(samlResponse string, providerType string) string {
|
func buildSpKeyStore() dsig.X509KeyStore {
|
||||||
|
keyPair, err := tls.LoadX509KeyPair("object/token_jwt_key.pem", "object/token_jwt_key.key")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &dsig.TLSCertKeyStore{
|
||||||
|
PrivateKey: keyPair.PrivateKey,
|
||||||
|
Certificate: keyPair.Certificate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSpCertificateStore(provider *Provider, samlResponse string) (dsig.MemoryX509CertificateStore, error) {
|
||||||
|
certEncodedData := ""
|
||||||
|
if samlResponse != "" {
|
||||||
|
certEncodedData = getCertificateFromSamlResponse(samlResponse, provider.Type)
|
||||||
|
} else if provider.IdP != "" {
|
||||||
|
certEncodedData = provider.IdP
|
||||||
|
}
|
||||||
|
|
||||||
|
certData, err := base64.StdEncoding.DecodeString(certEncodedData)
|
||||||
|
if err != nil {
|
||||||
|
return dsig.MemoryX509CertificateStore{}, err
|
||||||
|
}
|
||||||
|
idpCert, err := x509.ParseCertificate(certData)
|
||||||
|
if err != nil {
|
||||||
|
return dsig.MemoryX509CertificateStore{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certStore := dsig.MemoryX509CertificateStore{
|
||||||
|
Roots: []*x509.Certificate{idpCert},
|
||||||
|
}
|
||||||
|
return certStore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCertificateFromSamlResponse(samlResponse string, providerType string) string {
|
||||||
de, err := base64.StdEncoding.DecodeString(samlResponse)
|
de, err := base64.StdEncoding.DecodeString(samlResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -122,14 +147,3 @@ func parseSamlResponse(samlResponse string, providerType string) string {
|
|||||||
res := regexp.MustCompile(expression).FindStringSubmatch(deStr)
|
res := regexp.MustCompile(expression).FindStringSubmatch(deStr)
|
||||||
return res[1]
|
return res[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSpKeyStore() dsig.X509KeyStore {
|
|
||||||
keyPair, err := tls.LoadX509KeyPair("object/token_jwt_key.pem", "object/token_jwt_key.key")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &dsig.TLSCertKeyStore{
|
|
||||||
PrivateKey: keyPair.PrivateKey,
|
|
||||||
Certificate: keyPair.Certificate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -15,10 +15,12 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@@ -48,6 +50,7 @@ type User struct {
|
|||||||
EmailVerified bool `json:"emailVerified"`
|
EmailVerified bool `json:"emailVerified"`
|
||||||
Phone string `xorm:"varchar(20) index" json:"phone"`
|
Phone string `xorm:"varchar(20) index" json:"phone"`
|
||||||
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
|
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
|
||||||
|
Region string `xorm:"varchar(100)" json:"region"`
|
||||||
Location string `xorm:"varchar(100)" json:"location"`
|
Location string `xorm:"varchar(100)" json:"location"`
|
||||||
Address []string `json:"address"`
|
Address []string `json:"address"`
|
||||||
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
|
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
|
||||||
@@ -57,7 +60,6 @@ type User struct {
|
|||||||
Homepage string `xorm:"varchar(100)" json:"homepage"`
|
Homepage string `xorm:"varchar(100)" json:"homepage"`
|
||||||
Bio string `xorm:"varchar(100)" json:"bio"`
|
Bio string `xorm:"varchar(100)" json:"bio"`
|
||||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||||
Region string `xorm:"varchar(100)" json:"region"`
|
|
||||||
Language string `xorm:"varchar(100)" json:"language"`
|
Language string `xorm:"varchar(100)" json:"language"`
|
||||||
Gender string `xorm:"varchar(100)" json:"gender"`
|
Gender string `xorm:"varchar(100)" json:"gender"`
|
||||||
Birthday string `xorm:"varchar(100)" json:"birthday"`
|
Birthday string `xorm:"varchar(100)" json:"birthday"`
|
||||||
@@ -449,7 +451,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
|
|||||||
if len(columns) == 0 {
|
if len(columns) == 0 {
|
||||||
columns = []string{
|
columns = []string{
|
||||||
"owner", "display_name", "avatar",
|
"owner", "display_name", "avatar",
|
||||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "signup_application",
|
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||||
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
||||||
"signin_wrong_times", "last_signin_wrong_time",
|
"signin_wrong_times", "last_signin_wrong_time",
|
||||||
}
|
}
|
||||||
@@ -513,7 +515,10 @@ func AddUser(user *User) bool {
|
|||||||
user.UpdateUserHash()
|
user.UpdateUserHash()
|
||||||
user.PreHash = user.Hash
|
user.PreHash = user.Hash
|
||||||
|
|
||||||
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
|
updated := user.refreshAvatar()
|
||||||
|
if updated && user.PermanentAvatar != "*" {
|
||||||
|
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
|
||||||
|
}
|
||||||
|
|
||||||
user.Ranking = GetUserCount(user.Owner, "", "") + 1
|
user.Ranking = GetUserCount(user.Owner, "", "") + 1
|
||||||
|
|
||||||
@@ -693,3 +698,40 @@ func userChangeTrigger(oldName string, newName string) error {
|
|||||||
|
|
||||||
return session.Commit()
|
return session.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) refreshAvatar() bool {
|
||||||
|
var err error
|
||||||
|
var fileBuffer *bytes.Buffer
|
||||||
|
var ext string
|
||||||
|
|
||||||
|
// Gravatar + Identicon
|
||||||
|
if strings.Contains(user.Avatar, "Gravatar") && user.Email != "" {
|
||||||
|
client := proxy.ProxyHttpClient
|
||||||
|
has, err := hasGravatar(client, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has {
|
||||||
|
fileBuffer, ext, err = getGravatarFileBuffer(client, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileBuffer == nil && strings.Contains(user.Avatar, "Identicon") {
|
||||||
|
fileBuffer, ext, err = getIdenticonFileBuffer(user.Name)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileBuffer != nil {
|
||||||
|
avatarUrl := getPermanentAvatarUrlFromBuffer(user.Owner, user.Name, fileBuffer, ext, true)
|
||||||
|
user.Avatar = avatarUrl
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -131,6 +131,12 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
|
|||||||
if user.DisplayName == "" {
|
if user.DisplayName == "" {
|
||||||
user.DisplayName = userInfo.DisplayName
|
user.DisplayName = userInfo.DisplayName
|
||||||
}
|
}
|
||||||
|
} else if user.DisplayName == "" {
|
||||||
|
if userInfo.Username != "" {
|
||||||
|
user.DisplayName = userInfo.Username
|
||||||
|
} else {
|
||||||
|
user.DisplayName = userInfo.Id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if userInfo.Email != "" {
|
if userInfo.Email != "" {
|
||||||
propertyName := fmt.Sprintf("oauth_%s_email", providerType)
|
propertyName := fmt.Sprintf("oauth_%s_email", providerType)
|
||||||
|
@@ -17,7 +17,7 @@ import "./App.less";
|
|||||||
import {Helmet} from "react-helmet";
|
import {Helmet} from "react-helmet";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||||
import {BarsOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
import {BarsOutlined, CommentOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
||||||
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
|
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
|
||||||
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||||
import OrganizationListPage from "./OrganizationListPage";
|
import OrganizationListPage from "./OrganizationListPage";
|
||||||
@@ -44,8 +44,9 @@ import SyncerListPage from "./SyncerListPage";
|
|||||||
import SyncerEditPage from "./SyncerEditPage";
|
import SyncerEditPage from "./SyncerEditPage";
|
||||||
import CertListPage from "./CertListPage";
|
import CertListPage from "./CertListPage";
|
||||||
import CertEditPage from "./CertEditPage";
|
import CertEditPage from "./CertEditPage";
|
||||||
import ChatEditPage from "./ChatEditPage";
|
|
||||||
import ChatListPage from "./ChatListPage";
|
import ChatListPage from "./ChatListPage";
|
||||||
|
import ChatEditPage from "./ChatEditPage";
|
||||||
|
import ChatPage from "./ChatPage";
|
||||||
import MessageEditPage from "./MessageEditPage";
|
import MessageEditPage from "./MessageEditPage";
|
||||||
import MessageListPage from "./MessageListPage";
|
import MessageListPage from "./MessageListPage";
|
||||||
import ProductListPage from "./ProductListPage";
|
import ProductListPage from "./ProductListPage";
|
||||||
@@ -325,12 +326,17 @@ class App extends Component {
|
|||||||
items.push(Setting.getItem(<><SettingOutlined /> {i18next.t("account:My Account")}</>,
|
items.push(Setting.getItem(<><SettingOutlined /> {i18next.t("account:My Account")}</>,
|
||||||
"/account"
|
"/account"
|
||||||
));
|
));
|
||||||
|
items.push(Setting.getItem(<><CommentOutlined /> {i18next.t("account:Chats & Messages")}</>,
|
||||||
|
"/chat"
|
||||||
|
));
|
||||||
items.push(Setting.getItem(<><LogoutOutlined /> {i18next.t("account:Logout")}</>,
|
items.push(Setting.getItem(<><LogoutOutlined /> {i18next.t("account:Logout")}</>,
|
||||||
"/logout"));
|
"/logout"));
|
||||||
|
|
||||||
const onClick = (e) => {
|
const onClick = (e) => {
|
||||||
if (e.key === "/account") {
|
if (e.key === "/account") {
|
||||||
this.props.history.push("/account");
|
this.props.history.push("/account");
|
||||||
|
} else if (e.key === "/chat") {
|
||||||
|
this.props.history.push("/chat");
|
||||||
} else if (e.key === "/logout") {
|
} else if (e.key === "/logout") {
|
||||||
this.logout();
|
this.logout();
|
||||||
}
|
}
|
||||||
@@ -547,6 +553,7 @@ class App extends Component {
|
|||||||
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/chats" render={(props) => this.renderLoginIfNotLoggedIn(<ChatListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/chats" render={(props) => this.renderLoginIfNotLoggedIn(<ChatListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/chats/:chatName" render={(props) => this.renderLoginIfNotLoggedIn(<ChatEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/chats/:chatName" render={(props) => this.renderLoginIfNotLoggedIn(<ChatEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/chat" render={(props) => this.renderLoginIfNotLoggedIn(<ChatPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/messages" render={(props) => this.renderLoginIfNotLoggedIn(<MessageListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/messages" render={(props) => this.renderLoginIfNotLoggedIn(<MessageListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/messages/:messageName" render={(props) => this.renderLoginIfNotLoggedIn(<MessageEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/messages/:messageName" render={(props) => this.renderLoginIfNotLoggedIn(<MessageEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
|
||||||
@@ -622,7 +629,7 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
</Header>
|
</Header>
|
||||||
<Content style={{display: "flex", flexDirection: "column"}} >
|
<Content style={{display: "flex", flexDirection: "column"}} >
|
||||||
{Setting.isMobile() ?
|
{(Setting.isMobile() || window.location.pathname === "/chat") ?
|
||||||
this.renderRouter() :
|
this.renderRouter() :
|
||||||
<Card className="content-warp-card">
|
<Card className="content-warp-card">
|
||||||
{this.renderRouter()}
|
{this.renderRouter()}
|
||||||
|
161
web/src/ChatBox.js
Normal file
161
web/src/ChatBox.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Avatar, Input, List} from "antd";
|
||||||
|
import {CopyOutlined, DislikeOutlined, LikeOutlined, SendOutlined} from "@ant-design/icons";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
|
||||||
|
const {TextArea} = Input;
|
||||||
|
|
||||||
|
class ChatBox extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
inputValue: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = (e) => {
|
||||||
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (this.state.inputValue !== "") {
|
||||||
|
this.send(this.state.inputValue);
|
||||||
|
this.setState({inputValue: ""});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
send = (text) => {
|
||||||
|
Setting.showMessage("success", text);
|
||||||
|
this.setState({inputValue: ""});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderList() {
|
||||||
|
return (
|
||||||
|
<div style={{position: "relative"}}>
|
||||||
|
<List
|
||||||
|
style={{maxHeight: "calc(100vh - 140px)", overflowY: "auto"}}
|
||||||
|
itemLayout="horizontal"
|
||||||
|
dataSource={this.props.messages === undefined ? undefined : [...this.props.messages, {}]}
|
||||||
|
renderItem={(item, index) => {
|
||||||
|
if (Object.keys(item).length === 0 && item.constructor === Object) {
|
||||||
|
return <List.Item style={{
|
||||||
|
height: "160px",
|
||||||
|
backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)",
|
||||||
|
borderBottom: "1px solid rgb(229, 229, 229)",
|
||||||
|
position: "relative",
|
||||||
|
}} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List.Item style={{
|
||||||
|
backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)",
|
||||||
|
borderBottom: "1px solid rgb(229, 229, 229)",
|
||||||
|
position: "relative",
|
||||||
|
}}>
|
||||||
|
<div style={{width: "800px", margin: "0 auto", position: "relative"}}>
|
||||||
|
<List.Item.Meta
|
||||||
|
avatar={<Avatar style={{width: "30px", height: "30px", borderRadius: "3px"}} src={item.author === `${this.props.account.owner}/${this.props.account.name}` ? this.props.account.avatar : "https://cdn.casbin.com/casdoor/resource/built-in/admin/gpt.png"} />}
|
||||||
|
title={<div style={{fontSize: "16px", fontWeight: "normal", lineHeight: "24px", marginTop: "-15px", marginLeft: "5px", marginRight: "80px"}}>{item.text}</div>}
|
||||||
|
/>
|
||||||
|
<div style={{position: "absolute", top: "0px", right: "0px"}}
|
||||||
|
>
|
||||||
|
<CopyOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||||
|
<LikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||||
|
<DislikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</List.Item>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
height: "120px",
|
||||||
|
background: "linear-gradient(transparent 0%, rgba(255, 255, 255, 0.8) 50%, white 100%)",
|
||||||
|
pointerEvents: "none",
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInput() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
bottom: "90px",
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{position: "relative", width: "760px", marginLeft: "-280px"}}>
|
||||||
|
<TextArea
|
||||||
|
placeholder={"Send a message..."}
|
||||||
|
autoSize={{maxRows: 8}}
|
||||||
|
value={this.state.inputValue}
|
||||||
|
onChange={(e) => this.setState({inputValue: e.target.value})}
|
||||||
|
onKeyDown={this.handleKeyDown}
|
||||||
|
style={{
|
||||||
|
fontSize: "16px",
|
||||||
|
fontWeight: "normal",
|
||||||
|
lineHeight: "24px",
|
||||||
|
width: "770px",
|
||||||
|
height: "48px",
|
||||||
|
borderRadius: "6px",
|
||||||
|
borderColor: "rgb(229,229,229)",
|
||||||
|
boxShadow: "0 0 15px rgba(0, 0, 0, 0.1)",
|
||||||
|
paddingLeft: "17px",
|
||||||
|
paddingRight: "17px",
|
||||||
|
paddingTop: "12px",
|
||||||
|
paddingBottom: "12px",
|
||||||
|
}}
|
||||||
|
suffix={<SendOutlined style={{color: "rgb(210,210,217"}} onClick={() => this.send(this.state.inputValue)} />}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
<SendOutlined
|
||||||
|
style={{
|
||||||
|
color: this.state.inputValue === "" ? "rgb(210,210,217)" : "rgb(142,142,160)",
|
||||||
|
position: "absolute",
|
||||||
|
bottom: "17px",
|
||||||
|
right: "17px",
|
||||||
|
}}
|
||||||
|
onClick={() => this.send(this.state.inputValue)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
this.renderList()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this.renderInput()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatBox;
|
@@ -126,7 +126,33 @@ class ChatEditPage extends React.Component {
|
|||||||
</Row>
|
</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("chat:User1"), i18next.t("general:User1 - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.type} onChange={(value => {
|
||||||
|
this.updateChatField("type", value);
|
||||||
|
})}
|
||||||
|
options={[
|
||||||
|
{value: "Single", name: i18next.t("chat:Single")},
|
||||||
|
{value: "Group", name: i18next.t("chat:Group")},
|
||||||
|
{value: "AI", name: i18next.t("chat:AI")},
|
||||||
|
].map((item) => Setting.getOption(item.name, item.value))}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.chat.category} onChange={e => {
|
||||||
|
this.updateChatField("category", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("chat:User1"), i18next.t("chat:User1 - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user1} onChange={(value => {this.updateChatField("user1", value);})}
|
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user1} onChange={(value => {this.updateChatField("user1", value);})}
|
||||||
@@ -136,7 +162,7 @@ class ChatEditPage extends React.Component {
|
|||||||
</Row>
|
</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("chat:User2"), i18next.t("general:User2 - Tooltip"))} :
|
{Setting.getLabel(i18next.t("chat:User2"), i18next.t("chat:User2 - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user2} onChange={(value => {this.updateChatField("user2", value);})}
|
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user2} onChange={(value => {this.updateChatField("user2", value);})}
|
||||||
@@ -146,7 +172,7 @@ class ChatEditPage extends React.Component {
|
|||||||
</Row>
|
</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("chat:Sub users"), i18next.t("chat:Sub users - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Users"), i18next.t("chat:Users - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select mode="tags" style={{width: "100%"}} value={this.state.chat.users}
|
<Select mode="tags" style={{width: "100%"}} value={this.state.chat.users}
|
||||||
|
@@ -32,6 +32,8 @@ class ChatListPage extends BaseListPage {
|
|||||||
updatedTime: moment().format(),
|
updatedTime: moment().format(),
|
||||||
organization: this.props.account.owner,
|
organization: this.props.account.owner,
|
||||||
displayName: `New Chat - ${randomName}`,
|
displayName: `New Chat - ${randomName}`,
|
||||||
|
type: "Single",
|
||||||
|
category: "Chat Category - 1",
|
||||||
user1: `${this.props.account.owner}/${this.props.account.name}`,
|
user1: `${this.props.account.owner}/${this.props.account.name}`,
|
||||||
user2: "",
|
user2: "",
|
||||||
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
||||||
@@ -134,6 +136,30 @@ class ChatListPage extends BaseListPage {
|
|||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("displayName"),
|
...this.getColumnSearchProps("displayName"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("provider:Type"),
|
||||||
|
dataIndex: "type",
|
||||||
|
key: "type",
|
||||||
|
width: "110px",
|
||||||
|
sorter: true,
|
||||||
|
filterMultiple: false,
|
||||||
|
filters: [
|
||||||
|
{text: "Single", value: "Single"},
|
||||||
|
{text: "Group", value: "Group"},
|
||||||
|
{text: "AI", value: "AI"},
|
||||||
|
],
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return i18next.t(`chat:${text}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("provider:Category"),
|
||||||
|
dataIndex: "category",
|
||||||
|
key: "category",
|
||||||
|
// width: '100px',
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("category"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("chat:User1"),
|
title: i18next.t("chat:User1"),
|
||||||
dataIndex: "user1",
|
dataIndex: "user1",
|
||||||
|
100
web/src/ChatMenu.js
Normal file
100
web/src/ChatMenu.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Menu} from "antd";
|
||||||
|
import {LayoutOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
|
class ChatMenu extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const items = this.chatsToItems(this.props.chats);
|
||||||
|
const openKeys = items.map((item) => item.key);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
openKeys: openKeys,
|
||||||
|
selectedKeys: ["0-0"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
chatsToItems(chats) {
|
||||||
|
const categories = {};
|
||||||
|
chats.forEach((chat) => {
|
||||||
|
if (!categories[chat.category]) {
|
||||||
|
categories[chat.category] = [];
|
||||||
|
}
|
||||||
|
categories[chat.category].push(chat);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.keys(categories).map((category, index) => {
|
||||||
|
return {
|
||||||
|
key: `${index}`,
|
||||||
|
icon: <LayoutOutlined />,
|
||||||
|
label: category,
|
||||||
|
children: categories[category].map((chat, chatIndex) => {
|
||||||
|
const globalChatIndex = chats.indexOf(chat);
|
||||||
|
return {
|
||||||
|
key: `${index}-${chatIndex}`,
|
||||||
|
dataIndex: globalChatIndex,
|
||||||
|
label: chat.displayName,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect = (info) => {
|
||||||
|
const [categoryIndex, chatIndex] = info.selectedKeys[0].split("-").map(Number);
|
||||||
|
const selectedItem = this.chatsToItems(this.props.chats)[categoryIndex].children[chatIndex];
|
||||||
|
this.setState({selectedKeys: [`${categoryIndex}-${chatIndex}`]});
|
||||||
|
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(selectedItem.dataIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getRootSubmenuKeys(items) {
|
||||||
|
return items.map((item, index) => `${index}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpenChange = (keys) => {
|
||||||
|
const items = this.chatsToItems(this.props.chats);
|
||||||
|
const rootSubmenuKeys = this.getRootSubmenuKeys(items);
|
||||||
|
const latestOpenKey = keys.find((key) => this.state.openKeys.indexOf(key) === -1);
|
||||||
|
|
||||||
|
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||||
|
this.setState({openKeys: keys});
|
||||||
|
} else {
|
||||||
|
this.setState({openKeys: latestOpenKey ? [latestOpenKey] : []});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const items = this.chatsToItems(this.props.chats);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
mode="inline"
|
||||||
|
openKeys={this.state.openKeys}
|
||||||
|
selectedKeys={this.state.selectedKeys}
|
||||||
|
onOpenChange={this.onOpenChange}
|
||||||
|
onSelect={this.onSelect}
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatMenu;
|
157
web/src/ChatPage.js
Normal file
157
web/src/ChatPage.js
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Spin} from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import ChatMenu from "./ChatMenu";
|
||||||
|
import ChatBox from "./ChatBox";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
import * as ChatBackend from "./backend/ChatBackend";
|
||||||
|
import * as MessageBackend from "./backend/MessageBackend";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import BaseListPage from "./BaseListPage";
|
||||||
|
|
||||||
|
class ChatPage extends BaseListPage {
|
||||||
|
newChat() {
|
||||||
|
const randomName = Setting.getRandomName();
|
||||||
|
return {
|
||||||
|
owner: "admin", // this.props.account.applicationName,
|
||||||
|
name: `chat_${randomName}`,
|
||||||
|
createdTime: moment().format(),
|
||||||
|
updatedTime: moment().format(),
|
||||||
|
organization: this.props.account.owner,
|
||||||
|
displayName: `New Chat - ${randomName}`,
|
||||||
|
type: "Single",
|
||||||
|
category: "Chat Category - 1",
|
||||||
|
user1: `${this.props.account.owner}/${this.props.account.name}`,
|
||||||
|
user2: "",
|
||||||
|
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
||||||
|
messageCount: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addChat() {
|
||||||
|
const newChat = this.newChat();
|
||||||
|
ChatBackend.addChat(newChat)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.props.history.push({pathname: `/chats/${newChat.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteChat(i) {
|
||||||
|
ChatBackend.deleteChat(this.state.data[i])
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
|
this.setState({
|
||||||
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTable(chats) {
|
||||||
|
return (this.state.loading) ? <Spin size="large" style={{marginLeft: "50%", marginTop: "10%"}} /> : (
|
||||||
|
(
|
||||||
|
<div style={{display: "flex", height: "calc(100vh - 140px)"}}>
|
||||||
|
<div style={{width: "250px", height: "100%", backgroundColor: "white", borderRight: "1px solid rgb(245,245,245)"}}>
|
||||||
|
<ChatMenu chats={chats} onSelect={(i) => {
|
||||||
|
const chat = chats[i];
|
||||||
|
this.getMessages(chat.name);
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
<div style={{flex: 1, height: "100%", backgroundColor: "white", position: "relative"}}>
|
||||||
|
<div style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundImage: "url(https://cdn.casbin.org/img/casdoor-logo_1185x256.png)",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
backgroundRepeat: "no-repeat",
|
||||||
|
backgroundSize: "200px auto",
|
||||||
|
backgroundBlendMode: "luminosity",
|
||||||
|
filter: "grayscale(80%) brightness(140%) contrast(90%)",
|
||||||
|
opacity: 0.5,
|
||||||
|
}}>
|
||||||
|
</div>
|
||||||
|
<ChatBox messages={this.state.messages} account={this.props.account} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch = (params = {}) => {
|
||||||
|
let field = params.searchedColumn, value = params.searchText;
|
||||||
|
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||||
|
if (params.category !== undefined && params.category !== null) {
|
||||||
|
field = "category";
|
||||||
|
value = params.category;
|
||||||
|
} else if (params.type !== undefined && params.type !== null) {
|
||||||
|
field = "type";
|
||||||
|
value = params.type;
|
||||||
|
}
|
||||||
|
this.setState({loading: true});
|
||||||
|
ChatBackend.getChats("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
data: res.data,
|
||||||
|
pagination: {
|
||||||
|
...params.pagination,
|
||||||
|
total: res.data2,
|
||||||
|
},
|
||||||
|
searchText: params.searchText,
|
||||||
|
searchedColumn: params.searchedColumn,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (Setting.isResponseDenied(res)) {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
isAuthorized: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getMessages(chatName) {
|
||||||
|
MessageBackend.getChatMessages(chatName)
|
||||||
|
.then((messages) => {
|
||||||
|
this.setState({
|
||||||
|
messages: messages,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatPage;
|
@@ -69,7 +69,7 @@ class EntryPage extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
|
<div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
|
||||||
<Spin spinning={this.state.application === undefined} tip={i18next.t("login:Loading")} style={{margin: "0 auto"}} />
|
<Spin size="large" spinning={this.state.application === undefined} tip={i18next.t("login:Loading")} style={{margin: "0 auto"}} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
||||||
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
||||||
|
@@ -140,7 +140,7 @@ class MessageEditPage extends React.Component {
|
|||||||
</Row>
|
</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("message:Author"), i18next.t("general:Author - Tooltip"))} :
|
{Setting.getLabel(i18next.t("message:Author"), i18next.t("message:Author - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.message.author} onChange={(value => {this.updateMessageField("author", value);})}
|
<Select virtual={false} style={{width: "100%"}} value={this.state.message.author} onChange={(value => {this.updateMessageField("author", value);})}
|
||||||
|
@@ -42,7 +42,7 @@ export const Countries = [{label: "English", key: "en", country: "US", alt: "Eng
|
|||||||
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
|
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
|
||||||
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
|
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
|
||||||
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
|
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
|
||||||
{label: "TiếngViệt", key: "vn", country: "VN", alt: "TiếngViệt"},
|
{label: "TiếngViệt", key: "vi", country: "VN", alt: "TiếngViệt"},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getThemeData(organization, application) {
|
export function getThemeData(organization, application) {
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Card, Col, Input, Result, Row, Select, Spin, Switch} from "antd";
|
import {Button, Card, Col, Input, InputNumber, Result, Row, Select, Spin, Switch} from "antd";
|
||||||
import * as UserBackend from "./backend/UserBackend";
|
import * as UserBackend from "./backend/UserBackend";
|
||||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
@@ -110,9 +110,9 @@ class UserEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseUserField(key, value) {
|
parseUserField(key, value) {
|
||||||
// if ([].includes(key)) {
|
if (["score", "karma", "ranking"].includes(key)) {
|
||||||
// value = Setting.myParseInt(value);
|
value = Setting.myParseInt(value);
|
||||||
// }
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +360,19 @@ class UserEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
} else if (accountItem.name === "Address") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Address"), i18next.t("user:Address - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.address} onChange={e => {
|
||||||
|
this.updateUserField("address", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
} else if (accountItem.name === "Affiliation") {
|
} else if (accountItem.name === "Affiliation") {
|
||||||
return (
|
return (
|
||||||
(this.state.application === null || this.state.user === null) ? null : (
|
(this.state.application === null || this.state.user === null) ? null : (
|
||||||
@@ -379,6 +392,32 @@ class UserEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
} else if (accountItem.name === "ID card type") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:ID card type"), i18next.t("user:ID card type - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.idCardType} onChange={e => {
|
||||||
|
this.updateUserField("idCardType", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "ID card") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:ID card"), i18next.t("user:ID card - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.idCard} onChange={e => {
|
||||||
|
this.updateUserField("idCard", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
} else if (accountItem.name === "Homepage") {
|
} else if (accountItem.name === "Homepage") {
|
||||||
return (
|
return (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@@ -431,6 +470,97 @@ class UserEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
} else if (accountItem.name === "Language") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Language"), i18next.t("user:Language - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.language} onChange={e => {
|
||||||
|
this.updateUserField("language", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "Gender") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Gender"), i18next.t("user:Gender - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.gender} onChange={e => {
|
||||||
|
this.updateUserField("gender", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "Birthday") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Birthday"), i18next.t("user:Birthday - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.birthday} onChange={e => {
|
||||||
|
this.updateUserField("birthday", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "Education") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Education"), i18next.t("user:Education - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.education} onChange={e => {
|
||||||
|
this.updateUserField("education", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "Score") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Score"), i18next.t("user:Score - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<InputNumber value={this.state.user.score} onChange={value => {
|
||||||
|
this.updateUserField("score", value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "Karma") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Karma"), i18next.t("user:Karma - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<InputNumber value={this.state.user.karma} onChange={value => {
|
||||||
|
this.updateUserField("karma", value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
} else if (accountItem.name === "Ranking") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:Ranking"), i18next.t("user:Ranking - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<InputNumber value={this.state.user.ranking} onChange={value => {
|
||||||
|
this.updateUserField("ranking", value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
} else if (accountItem.name === "Signup application") {
|
} else if (accountItem.name === "Signup application") {
|
||||||
return (
|
return (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
@@ -787,11 +787,11 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const visibleOAuthProviderItems = application.providers.filter(providerItem => this.isProviderVisible(providerItem));
|
const visibleOAuthProviderItems = application.providers.filter(providerItem => this.isProviderVisible(providerItem));
|
||||||
if (this.props.application === undefined && !application.enablePassword && visibleOAuthProviderItems.length === 1) {
|
if (this.props.preview !== "auto" && !application.enablePassword && visibleOAuthProviderItems.length === 1) {
|
||||||
Setting.goToLink(Provider.getAuthUrl(application, visibleOAuthProviderItems[0].provider, "signup"));
|
Setting.goToLink(Provider.getAuthUrl(application, visibleOAuthProviderItems[0].provider, "signup"));
|
||||||
return (
|
return (
|
||||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
|
<div style={{display: "flex", justifyContent: "center", alignItems: "center", width: "100%"}}>
|
||||||
<Spin size="large" tip={i18next.t("login:Signing in...")} style={{paddingTop: "10%"}} />
|
<Spin size="large" tip={i18next.t("login:Signing in...")} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -68,7 +68,7 @@ class ResultPage extends React.Component {
|
|||||||
if (linkInStorage !== null && linkInStorage !== "") {
|
if (linkInStorage !== null && linkInStorage !== "") {
|
||||||
Setting.goToLink(linkInStorage);
|
Setting.goToLink(linkInStorage);
|
||||||
} else {
|
} else {
|
||||||
Setting.redirectToLoginPage(application);
|
Setting.redirectToLoginPage(application, this.props.history);
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{i18next.t("login:Sign In")}
|
{i18next.t("login:Sign In")}
|
||||||
|
@@ -408,24 +408,27 @@ class SignupPage extends React.Component {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Input.Group>
|
</Input.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{
|
||||||
name="phoneCode"
|
signupItem.rule !== "No verification" &&
|
||||||
label={i18next.t("code:Phone code")}
|
<Form.Item
|
||||||
rules={[
|
name="phoneCode"
|
||||||
{
|
label={i18next.t("code:Phone code")}
|
||||||
required: required,
|
rules={[
|
||||||
message: i18next.t("code:Please input your phone verification code!"),
|
{
|
||||||
},
|
required: required,
|
||||||
]}
|
message: i18next.t("code:Please input your phone verification code!"),
|
||||||
>
|
},
|
||||||
<SendCodeInput
|
]}
|
||||||
disabled={!this.state.validPhone}
|
>
|
||||||
method={"signup"}
|
<SendCodeInput
|
||||||
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
disabled={!this.state.validPhone}
|
||||||
application={application}
|
method={"signup"}
|
||||||
countryCode={this.form.current?.getFieldValue("countryCode")}
|
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
||||||
/>
|
application={application}
|
||||||
</Form.Item>
|
countryCode={this.form.current?.getFieldValue("countryCode")}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
} else if (signupItem.name === "Password") {
|
} else if (signupItem.name === "Password") {
|
||||||
|
@@ -24,6 +24,16 @@ export function getMessages(owner, page = "", pageSize = "", field = "", value =
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getChatMessages(chat) {
|
||||||
|
return fetch(`${Setting.ServerUrl}/api/get-messages?chat=${chat}`, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Accept-Language": Setting.getAcceptLanguage(),
|
||||||
|
},
|
||||||
|
}).then(res => res.json());
|
||||||
|
}
|
||||||
|
|
||||||
export function getMessage(owner, name) {
|
export function getMessage(owner, name) {
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, {
|
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@@ -22,7 +22,7 @@ import id from "./locales/id/data.json";
|
|||||||
import ja from "./locales/ja/data.json";
|
import ja from "./locales/ja/data.json";
|
||||||
import ko from "./locales/ko/data.json";
|
import ko from "./locales/ko/data.json";
|
||||||
import ru from "./locales/ru/data.json";
|
import ru from "./locales/ru/data.json";
|
||||||
import vn from "./locales/vn/data.json";
|
import vi from "./locales/vi/data.json";
|
||||||
import * as Conf from "./Conf";
|
import * as Conf from "./Conf";
|
||||||
import {initReactI18next} from "react-i18next";
|
import {initReactI18next} from "react-i18next";
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ const resources = {
|
|||||||
ja: ja,
|
ja: ja,
|
||||||
ko: ko,
|
ko: ko,
|
||||||
ru: ru,
|
ru: ru,
|
||||||
vn: vn,
|
vi: vi,
|
||||||
};
|
};
|
||||||
|
|
||||||
function initLanguage() {
|
function initLanguage() {
|
||||||
@@ -80,8 +80,8 @@ function initLanguage() {
|
|||||||
case "ru":
|
case "ru":
|
||||||
language = "ru";
|
language = "ru";
|
||||||
break;
|
break;
|
||||||
case "vn":
|
case "vi":
|
||||||
language = "vn";
|
language = "vi";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
language = Conf.DefaultLanguage;
|
language = Conf.DefaultLanguage;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Abmelden",
|
"Logout": "Abmelden",
|
||||||
"My Account": "Mein Konto",
|
"My Account": "Mein Konto",
|
||||||
"Sign Up": "Anmelden"
|
"Sign Up": "Anmelden"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Art des Zertifikats"
|
"Type - Tooltip": "Art des Zertifikats"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Der Code, den Sie erhalten haben",
|
"Code you received": "Der Code, den Sie erhalten haben",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Applikation",
|
"Application": "Applikation",
|
||||||
"Applications": "Anwendungen",
|
"Applications": "Anwendungen",
|
||||||
"Applications that require authentication": "Anwendungen, die eine Authentifizierung erfordern",
|
"Applications that require authentication": "Anwendungen, die eine Authentifizierung erfordern",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"Avatar - Tooltip": "Öffentliches Avatarbild für den Benutzer",
|
"Avatar - Tooltip": "Öffentliches Avatarbild für den Benutzer",
|
||||||
"Back Home": "Zurück nach Hause",
|
"Back Home": "Zurück nach Hause",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "Nutzerpools",
|
"User containers": "Nutzerpools",
|
||||||
"User type": "Benutzertyp",
|
"User type": "Benutzertyp",
|
||||||
"User type - Tooltip": "Tags, denen der Benutzer angehört, standardmäßig auf \"normaler Benutzer\" festgelegt",
|
"User type - Tooltip": "Tags, denen der Benutzer angehört, standardmäßig auf \"normaler Benutzer\" festgelegt",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Benutzer",
|
"Users": "Benutzer",
|
||||||
"Users under all organizations": "Benutzer unter allen Organisationen",
|
"Users under all organizations": "Benutzer unter allen Organisationen",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Logout",
|
"Logout": "Logout",
|
||||||
"My Account": "My Account",
|
"My Account": "My Account",
|
||||||
"Sign Up": "Sign Up"
|
"Sign Up": "Sign Up"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Type of certificate"
|
"Type - Tooltip": "Type of certificate"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Code you received",
|
"Code you received": "Code you received",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Application",
|
"Application": "Application",
|
||||||
"Applications": "Applications",
|
"Applications": "Applications",
|
||||||
"Applications that require authentication": "Applications that require authentication",
|
"Applications that require authentication": "Applications that require authentication",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"Avatar - Tooltip": "Public avatar image for the user",
|
"Avatar - Tooltip": "Public avatar image for the user",
|
||||||
"Back Home": "Back Home",
|
"Back Home": "Back Home",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "User pools",
|
"User containers": "User pools",
|
||||||
"User type": "User type",
|
"User type": "User type",
|
||||||
"User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
|
"User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Users",
|
"Users": "Users",
|
||||||
"Users under all organizations": "Users under all organizations",
|
"Users under all organizations": "Users under all organizations",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Cierre de sesión",
|
"Logout": "Cierre de sesión",
|
||||||
"My Account": "Mi cuenta",
|
"My Account": "Mi cuenta",
|
||||||
"Sign Up": "Registrarse"
|
"Sign Up": "Registrarse"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Tipo de certificado"
|
"Type - Tooltip": "Tipo de certificado"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Código que recibió",
|
"Code you received": "Código que recibió",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Aplicación",
|
"Application": "Aplicación",
|
||||||
"Applications": "Aplicaciones",
|
"Applications": "Aplicaciones",
|
||||||
"Applications that require authentication": "Aplicaciones que requieren autenticación",
|
"Applications that require authentication": "Aplicaciones que requieren autenticación",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"Avatar - Tooltip": "Imagen de avatar pública para el usuario",
|
"Avatar - Tooltip": "Imagen de avatar pública para el usuario",
|
||||||
"Back Home": "Regreso a casa",
|
"Back Home": "Regreso a casa",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "Piscinas de usuarios",
|
"User containers": "Piscinas de usuarios",
|
||||||
"User type": "Tipo de usuario",
|
"User type": "Tipo de usuario",
|
||||||
"User type - Tooltip": "Etiquetas a las que el usuario pertenece, con una configuración predeterminada en \"usuario-normal\"",
|
"User type - Tooltip": "Etiquetas a las que el usuario pertenece, con una configuración predeterminada en \"usuario-normal\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Usuarios",
|
"Users": "Usuarios",
|
||||||
"Users under all organizations": "Usuarios bajo todas las organizaciones",
|
"Users under all organizations": "Usuarios bajo todas las organizaciones",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Déconnexion",
|
"Logout": "Déconnexion",
|
||||||
"My Account": "Mon Compte",
|
"My Account": "Mon Compte",
|
||||||
"Sign Up": "S'inscrire"
|
"Sign Up": "S'inscrire"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Type de certificat"
|
"Type - Tooltip": "Type de certificat"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Le code que vous avez reçu",
|
"Code you received": "Le code que vous avez reçu",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Application",
|
"Application": "Application",
|
||||||
"Applications": "Applications",
|
"Applications": "Applications",
|
||||||
"Applications that require authentication": "Applications qui nécessitent une authentification",
|
"Applications that require authentication": "Applications qui nécessitent une authentification",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"Avatar - Tooltip": "Image d'avatar public pour l'utilisateur",
|
"Avatar - Tooltip": "Image d'avatar public pour l'utilisateur",
|
||||||
"Back Home": "Retour à la maison",
|
"Back Home": "Retour à la maison",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "Piscines d'utilisateurs",
|
"User containers": "Piscines d'utilisateurs",
|
||||||
"User type": "Type d'utilisateur",
|
"User type": "Type d'utilisateur",
|
||||||
"User type - Tooltip": "Balises auxquelles l'utilisateur appartient, avec une valeur par défaut \"utilisateur-normal\"",
|
"User type - Tooltip": "Balises auxquelles l'utilisateur appartient, avec une valeur par défaut \"utilisateur-normal\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Utilisateurs",
|
"Users": "Utilisateurs",
|
||||||
"Users under all organizations": "Utilisateurs sous toutes les organisations",
|
"Users under all organizations": "Utilisateurs sous toutes les organisations",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Keluar",
|
"Logout": "Keluar",
|
||||||
"My Account": "Akun Saya",
|
"My Account": "Akun Saya",
|
||||||
"Sign Up": "Mendaftar"
|
"Sign Up": "Mendaftar"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Jenis sertifikat"
|
"Type - Tooltip": "Jenis sertifikat"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Kode yang kamu terima",
|
"Code you received": "Kode yang kamu terima",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Aplikasi",
|
"Application": "Aplikasi",
|
||||||
"Applications": "Aplikasi",
|
"Applications": "Aplikasi",
|
||||||
"Applications that require authentication": "Aplikasi yang memerlukan autentikasi",
|
"Applications that require authentication": "Aplikasi yang memerlukan autentikasi",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"Avatar - Tooltip": "Gambar avatar publik untuk pengguna",
|
"Avatar - Tooltip": "Gambar avatar publik untuk pengguna",
|
||||||
"Back Home": "Kembali ke Rumah",
|
"Back Home": "Kembali ke Rumah",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "User pools",
|
"User containers": "User pools",
|
||||||
"User type": "Jenis pengguna",
|
"User type": "Jenis pengguna",
|
||||||
"User type - Tooltip": "Tag yang dimiliki oleh pengguna, defaultnya adalah \"normal-user\"",
|
"User type - Tooltip": "Tag yang dimiliki oleh pengguna, defaultnya adalah \"normal-user\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Pengguna-pengguna",
|
"Users": "Pengguna-pengguna",
|
||||||
"Users under all organizations": "Pengguna di bawah semua organisasi",
|
"Users under all organizations": "Pengguna di bawah semua organisasi",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "ログアウト",
|
"Logout": "ログアウト",
|
||||||
"My Account": "マイアカウント",
|
"My Account": "マイアカウント",
|
||||||
"Sign Up": "新規登録"
|
"Sign Up": "新規登録"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "証明書の種類"
|
"Type - Tooltip": "証明書の種類"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "受け取ったコード",
|
"Code you received": "受け取ったコード",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "アプリケーション",
|
"Application": "アプリケーション",
|
||||||
"Applications": "アプリケーション",
|
"Applications": "アプリケーション",
|
||||||
"Applications that require authentication": "認証が必要なアプリケーション",
|
"Applications that require authentication": "認証が必要なアプリケーション",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "アバター",
|
"Avatar": "アバター",
|
||||||
"Avatar - Tooltip": "ユーザーのパブリックアバター画像",
|
"Avatar - Tooltip": "ユーザーのパブリックアバター画像",
|
||||||
"Back Home": "帰宅",
|
"Back Home": "帰宅",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "ユーザープール",
|
"User containers": "ユーザープール",
|
||||||
"User type": "ユーザータイプ",
|
"User type": "ユーザータイプ",
|
||||||
"User type - Tooltip": "ユーザーが属するタグは、デフォルトでは「通常ユーザー」となります",
|
"User type - Tooltip": "ユーザーが属するタグは、デフォルトでは「通常ユーザー」となります",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "ユーザー",
|
"Users": "ユーザー",
|
||||||
"Users under all organizations": "すべての組織のユーザー",
|
"Users under all organizations": "すべての組織のユーザー",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "로그아웃",
|
"Logout": "로그아웃",
|
||||||
"My Account": "내 계정",
|
"My Account": "내 계정",
|
||||||
"Sign Up": "가입하기"
|
"Sign Up": "가입하기"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "증명서 유형"
|
"Type - Tooltip": "증명서 유형"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "받은 코드",
|
"Code you received": "받은 코드",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "응용 프로그램",
|
"Application": "응용 프로그램",
|
||||||
"Applications": "응용 프로그램",
|
"Applications": "응용 프로그램",
|
||||||
"Applications that require authentication": "인증이 필요한 애플리케이션들",
|
"Applications that require authentication": "인증이 필요한 애플리케이션들",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "아바타",
|
"Avatar": "아바타",
|
||||||
"Avatar - Tooltip": "사용자를 위한 공개 아바타 이미지",
|
"Avatar - Tooltip": "사용자를 위한 공개 아바타 이미지",
|
||||||
"Back Home": "집으로 돌아오기",
|
"Back Home": "집으로 돌아오기",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "사용자 풀",
|
"User containers": "사용자 풀",
|
||||||
"User type": "사용자 유형",
|
"User type": "사용자 유형",
|
||||||
"User type - Tooltip": "사용자가 속한 태그는 기본적으로 \"보통 사용자\"로 설정됩니다",
|
"User type - Tooltip": "사용자가 속한 태그는 기본적으로 \"보통 사용자\"로 설정됩니다",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "사용자들",
|
"Users": "사용자들",
|
||||||
"Users under all organizations": "모든 조직의 사용자",
|
"Users under all organizations": "모든 조직의 사용자",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Выход",
|
"Logout": "Выход",
|
||||||
"My Account": "Мой аккаунт",
|
"My Account": "Мой аккаунт",
|
||||||
"Sign Up": "Зарегистрироваться"
|
"Sign Up": "Зарегистрироваться"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Тип сертификата"
|
"Type - Tooltip": "Тип сертификата"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Код, который вы получили",
|
"Code you received": "Код, который вы получили",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Приложение",
|
"Application": "Приложение",
|
||||||
"Applications": "Приложения",
|
"Applications": "Приложения",
|
||||||
"Applications that require authentication": "Приложения, которые требуют аутентификации",
|
"Applications that require authentication": "Приложения, которые требуют аутентификации",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Аватар",
|
"Avatar": "Аватар",
|
||||||
"Avatar - Tooltip": "Публичное изображение аватара пользователя",
|
"Avatar - Tooltip": "Публичное изображение аватара пользователя",
|
||||||
"Back Home": "Домой",
|
"Back Home": "Домой",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "Пользовательские пулы",
|
"User containers": "Пользовательские пулы",
|
||||||
"User type": "Тип пользователя",
|
"User type": "Тип пользователя",
|
||||||
"User type - Tooltip": "Теги, к которым принадлежит пользователь, по умолчанию \"обычный пользователь\"",
|
"User type - Tooltip": "Теги, к которым принадлежит пользователь, по умолчанию \"обычный пользователь\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Пользователи",
|
"Users": "Пользователи",
|
||||||
"Users under all organizations": "Пользователи всех организаций",
|
"Users under all organizations": "Пользователи всех организаций",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "Chats & Messages",
|
||||||
"Logout": "Đăng xuất",
|
"Logout": "Đăng xuất",
|
||||||
"My Account": "Tài khoản của tôi",
|
"My Account": "Tài khoản của tôi",
|
||||||
"Sign Up": "Đăng ký"
|
"Sign Up": "Đăng ký"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "Loại chứng chỉ"
|
"Type - Tooltip": "Loại chứng chỉ"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
|
"AI": "AI",
|
||||||
"Edit Chat": "Edit Chat",
|
"Edit Chat": "Edit Chat",
|
||||||
|
"Group": "Group",
|
||||||
"Message count": "Message count",
|
"Message count": "Message count",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "New Chat",
|
||||||
"Sub users": "Sub users",
|
"Single": "Single",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
|
||||||
"User1": "User1",
|
"User1": "User1",
|
||||||
"User2": "User2"
|
"User1 - Tooltip": "User1 - Tooltip",
|
||||||
|
"User2": "User2",
|
||||||
|
"User2 - Tooltip": "User2 - Tooltip",
|
||||||
|
"Users - Tooltip": "Users - Tooltip"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "Mã bạn nhận được",
|
"Code you received": "Mã bạn nhận được",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "Ứng dụng",
|
"Application": "Ứng dụng",
|
||||||
"Applications": "Ứng dụng",
|
"Applications": "Ứng dụng",
|
||||||
"Applications that require authentication": "Các ứng dụng yêu cầu xác thực",
|
"Applications that require authentication": "Các ứng dụng yêu cầu xác thực",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "Ảnh đại diện",
|
"Avatar": "Ảnh đại diện",
|
||||||
"Avatar - Tooltip": "Ảnh đại diện công khai cho người dùng",
|
"Avatar - Tooltip": "Ảnh đại diện công khai cho người dùng",
|
||||||
"Back Home": "Trở về nhà",
|
"Back Home": "Trở về nhà",
|
||||||
@@ -287,8 +291,6 @@
|
|||||||
"User containers": "Nhóm người dùng",
|
"User containers": "Nhóm người dùng",
|
||||||
"User type": "Loại người dùng",
|
"User type": "Loại người dùng",
|
||||||
"User type - Tooltip": "Các thẻ mà người dùng thuộc vào, mặc định là \"người dùng bình thường\"",
|
"User type - Tooltip": "Các thẻ mà người dùng thuộc vào, mặc định là \"người dùng bình thường\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "Người dùng",
|
"Users": "Người dùng",
|
||||||
"Users under all organizations": "Người dùng trong tất cả các tổ chức",
|
"Users under all organizations": "Người dùng trong tất cả các tổ chức",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -352,6 +354,7 @@
|
|||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
"Author - Tooltip": "Author - Tooltip",
|
||||||
"Chat": "Chat",
|
"Chat": "Chat",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat - Tooltip": "Chat - Tooltip",
|
||||||
"Edit Message": "Edit Message",
|
"Edit Message": "Edit Message",
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"account": {
|
"account": {
|
||||||
|
"Chats & Messages": "聊天 & 消息",
|
||||||
"Logout": "登出",
|
"Logout": "登出",
|
||||||
"My Account": "我的账户",
|
"My Account": "我的账户",
|
||||||
"Sign Up": "注册"
|
"Sign Up": "注册"
|
||||||
@@ -118,13 +119,17 @@
|
|||||||
"Type - Tooltip": "公钥证书的类型"
|
"Type - Tooltip": "公钥证书的类型"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"Edit Chat": "Edit Chat",
|
"AI": "AI",
|
||||||
"Message count": "Message count",
|
"Edit Chat": "编辑聊天",
|
||||||
"New Chat": "New Chat",
|
"Group": "群聊",
|
||||||
"Sub users": "Sub users",
|
"Message count": "消息个数",
|
||||||
"Sub users - Tooltip": "Sub users - Tooltip",
|
"New Chat": "添加聊天",
|
||||||
"User1": "User1",
|
"Single": "单聊",
|
||||||
"User2": "User2"
|
"User1": "用户1",
|
||||||
|
"User1 - Tooltip": "当聊天类型为单聊时,该值为聊天的发起者;当聊天类型为群聊时,该值为群主",
|
||||||
|
"User2": "用户2",
|
||||||
|
"User2 - Tooltip": "当聊天类型为单聊时,该值为聊天的对象;当聊天类型为群聊时,该值为管理员",
|
||||||
|
"Users - Tooltip": "能够接收到聊天消息的所有用户"
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"Code you received": "验证码",
|
"Code you received": "验证码",
|
||||||
@@ -160,7 +165,6 @@
|
|||||||
"Application": "应用",
|
"Application": "应用",
|
||||||
"Applications": "应用",
|
"Applications": "应用",
|
||||||
"Applications that require authentication": "需要认证和鉴权的应用",
|
"Applications that require authentication": "需要认证和鉴权的应用",
|
||||||
"Author - Tooltip": "Author - Tooltip",
|
|
||||||
"Avatar": "头像",
|
"Avatar": "头像",
|
||||||
"Avatar - Tooltip": "公开展示的用户头像",
|
"Avatar - Tooltip": "公开展示的用户头像",
|
||||||
"Back Home": "返回到首页",
|
"Back Home": "返回到首页",
|
||||||
@@ -169,7 +173,7 @@
|
|||||||
"Cert": "证书",
|
"Cert": "证书",
|
||||||
"Cert - Tooltip": "该应用所对应的客户端SDK需要验证的公钥证书",
|
"Cert - Tooltip": "该应用所对应的客户端SDK需要验证的公钥证书",
|
||||||
"Certs": "证书",
|
"Certs": "证书",
|
||||||
"Chats": "Chats",
|
"Chats": "聊天",
|
||||||
"Click to Upload": "点击上传",
|
"Click to Upload": "点击上传",
|
||||||
"Client IP": "客户端IP",
|
"Client IP": "客户端IP",
|
||||||
"Close": "关闭",
|
"Close": "关闭",
|
||||||
@@ -214,7 +218,7 @@
|
|||||||
"Master password": "万能密码",
|
"Master password": "万能密码",
|
||||||
"Master password - Tooltip": "可用来登录该组织下的所有用户,方便管理员以该用户身份登录,以解决技术问题",
|
"Master password - Tooltip": "可用来登录该组织下的所有用户,方便管理员以该用户身份登录,以解决技术问题",
|
||||||
"Menu": "目录",
|
"Menu": "目录",
|
||||||
"Messages": "Messages",
|
"Messages": "消息",
|
||||||
"Method": "方法",
|
"Method": "方法",
|
||||||
"Model": "模型",
|
"Model": "模型",
|
||||||
"Model - Tooltip": "Casbin的访问控制模型",
|
"Model - Tooltip": "Casbin的访问控制模型",
|
||||||
@@ -281,14 +285,12 @@
|
|||||||
"URL": "链接",
|
"URL": "链接",
|
||||||
"URL - Tooltip": "URL链接",
|
"URL - Tooltip": "URL链接",
|
||||||
"Up": "上移",
|
"Up": "上移",
|
||||||
"Updated time": "Updated time",
|
"Updated time": "更新时间",
|
||||||
"User": "用户",
|
"User": "用户",
|
||||||
"User - Tooltip": "请确保用户名正确",
|
"User - Tooltip": "请确保用户名正确",
|
||||||
"User containers": "用户池",
|
"User containers": "用户池",
|
||||||
"User type": "用户类型",
|
"User type": "用户类型",
|
||||||
"User type - Tooltip": "用户所属的标签,默认为\"normal-user\"",
|
"User type - Tooltip": "用户所属的标签,默认为\"normal-user\"",
|
||||||
"User1 - Tooltip": "User1 - Tooltip",
|
|
||||||
"User2 - Tooltip": "User2 - Tooltip",
|
|
||||||
"Users": "用户",
|
"Users": "用户",
|
||||||
"Users under all organizations": "所有组织里的用户",
|
"Users under all organizations": "所有组织里的用户",
|
||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
@@ -351,13 +353,14 @@
|
|||||||
"username, Email or phone": "用户名、Email或手机号"
|
"username, Email or phone": "用户名、Email或手机号"
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"Author": "Author",
|
"Author": "作者",
|
||||||
"Chat": "Chat",
|
"Author - Tooltip": "发出消息的用户",
|
||||||
"Chat - Tooltip": "Chat - Tooltip",
|
"Chat": "聊天",
|
||||||
"Edit Message": "Edit Message",
|
"Chat - Tooltip": "消息所属的聊天ID",
|
||||||
"New Message": "New Message",
|
"Edit Message": "编辑消息",
|
||||||
"Text": "Text",
|
"New Message": "添加消息",
|
||||||
"Text - Tooltip": "Text - Tooltip"
|
"Text": "内容",
|
||||||
|
"Text - Tooltip": "消息的内容"
|
||||||
},
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "编辑模型",
|
"Edit Model": "编辑模型",
|
||||||
|
@@ -81,16 +81,27 @@ class AccountTable extends React.Component {
|
|||||||
{name: "Country code", displayName: i18next.t("user:Country code")},
|
{name: "Country code", displayName: i18next.t("user:Country code")},
|
||||||
{name: "Country/Region", displayName: i18next.t("user:Country/Region")},
|
{name: "Country/Region", displayName: i18next.t("user:Country/Region")},
|
||||||
{name: "Location", displayName: i18next.t("user:Location")},
|
{name: "Location", displayName: i18next.t("user:Location")},
|
||||||
|
{name: "Address", displayName: i18next.t("user:Address")},
|
||||||
{name: "Affiliation", displayName: i18next.t("user:Affiliation")},
|
{name: "Affiliation", displayName: i18next.t("user:Affiliation")},
|
||||||
{name: "Title", displayName: i18next.t("user:Title")},
|
{name: "Title", displayName: i18next.t("user:Title")},
|
||||||
|
{name: "ID card type", displayName: i18next.t("user:ID card type")},
|
||||||
|
{name: "ID card", displayName: i18next.t("user:ID card")},
|
||||||
{name: "Homepage", displayName: i18next.t("user:Homepage")},
|
{name: "Homepage", displayName: i18next.t("user:Homepage")},
|
||||||
{name: "Bio", displayName: i18next.t("user:Bio")},
|
{name: "Bio", displayName: i18next.t("user:Bio")},
|
||||||
{name: "Tag", displayName: i18next.t("user:Tag")},
|
{name: "Tag", displayName: i18next.t("user:Tag")},
|
||||||
|
{name: "Language", displayName: i18next.t("user:Language")},
|
||||||
|
{name: "Gender", displayName: i18next.t("user:Gender")},
|
||||||
|
{name: "Birthday", displayName: i18next.t("user:Birthday")},
|
||||||
|
{name: "Education", displayName: i18next.t("user:Education")},
|
||||||
|
{name: "Score", displayName: i18next.t("user:Score")},
|
||||||
|
{name: "Karma", displayName: i18next.t("user:Karma")},
|
||||||
|
{name: "Ranking", displayName: i18next.t("user:Ranking")},
|
||||||
{name: "Signup application", displayName: i18next.t("general:Signup application")},
|
{name: "Signup application", displayName: i18next.t("general:Signup application")},
|
||||||
{name: "Roles", displayName: i18next.t("general:Roles")},
|
{name: "Roles", displayName: i18next.t("general:Roles")},
|
||||||
{name: "Permissions", displayName: i18next.t("general:Permissions")},
|
{name: "Permissions", displayName: i18next.t("general:Permissions")},
|
||||||
{name: "3rd-party logins", displayName: i18next.t("user:3rd-party logins")},
|
{name: "3rd-party logins", displayName: i18next.t("user:3rd-party logins")},
|
||||||
{name: "Properties", displayName: i18next.t("user:Properties")},
|
{name: "Properties", displayName: i18next.t("user:Properties")},
|
||||||
|
{name: "Is online", displayName: i18next.t("user:Is online")},
|
||||||
{name: "Is admin", displayName: i18next.t("user:Is admin")},
|
{name: "Is admin", displayName: i18next.t("user:Is admin")},
|
||||||
{name: "Is global admin", displayName: i18next.t("user:Is global admin")},
|
{name: "Is global admin", displayName: i18next.t("user:Is global admin")},
|
||||||
{name: "Is forbidden", displayName: i18next.t("user:Is forbidden")},
|
{name: "Is forbidden", displayName: i18next.t("user:Is forbidden")},
|
||||||
|
@@ -186,6 +186,11 @@ class SignupTable extends React.Component {
|
|||||||
{id: "Normal", name: i18next.t("application:Normal")},
|
{id: "Normal", name: i18next.t("application:Normal")},
|
||||||
{id: "No verification", name: i18next.t("application:No verification")},
|
{id: "No verification", name: i18next.t("application:No verification")},
|
||||||
];
|
];
|
||||||
|
} else if (record.name === "Phone") {
|
||||||
|
options = [
|
||||||
|
{id: "Normal", name: i18next.t("application:Normal")},
|
||||||
|
{id: "No verification", name: i18next.t("application:No verification")},
|
||||||
|
];
|
||||||
} else if (record.name === "Agreement") {
|
} else if (record.name === "Agreement") {
|
||||||
options = [
|
options = [
|
||||||
{id: "None", name: i18next.t("application:Only signup")},
|
{id: "None", name: i18next.t("application:Only signup")},
|
||||||
|
Reference in New Issue
Block a user