Compare commits

...

42 Commits

Author SHA1 Message Date
4123d47174 feat: callback will jump to blank page when from param start with "http" (#2778) 2024-03-06 01:07:52 +08:00
fbdd5a926d Fix normal user my-account page blank bug 2024-03-06 01:07:28 +08:00
92b6fda0f6 feat: support more objects in init_data JSON (#2776) 2024-03-05 23:41:46 +08:00
6a7ac35e65 fix: fix wechat media account can not bind issue (#2774)
* fix: fix wechat media account can not bind

* fix: improve code format
2024-03-05 18:46:28 +08:00
fc137b9f76 feat: fix custom JS doesn't reload after refresh bug (#2773) 2024-03-05 15:03:25 +08:00
11dbd5ba9a fix: fix duplicated load bug of custom JS (#2771) 2024-03-05 00:09:37 +08:00
19942a8bd4 Add webhook.SingleOrgOnly 2024-03-04 21:14:52 +08:00
f9ee8a68cb Support Chrome extension redirecting 2024-03-04 18:31:56 +08:00
Ron
f241336ad7 feat: add OSON SMS provider (#2769)
* implemented SMS provider 'OSON SMS' for frontend

* feat: add 'OSON SMS' provider for frontend
2024-03-04 01:05:53 +08:00
8b64d113fb Upgrade go-sms-sender dependency to 0.20.0 2024-03-04 01:05:28 +08:00
a8800c4d5c fix: add missing / for style tag in signin items (#2768) 2024-03-03 23:46:57 +08:00
75fc9ab9f7 Improve GetMaskedApplication()'s logic 2024-03-03 22:01:49 +08:00
d06da76c3d feat: fix bug in /get-organization-applications API 2024-03-03 21:08:36 +08:00
bc399837cc Rename label to "Custom CSS" 2024-03-03 20:45:14 +08:00
265abfe102 fix: handle error in storage.GetStorageProvider() 2024-03-03 18:18:54 +08:00
12acb24dbc feat: add transaction pages (#2761) 2024-03-02 10:41:16 +08:00
ba1ddc7e50 fix: admin can modify user ID now 2024-02-28 18:07:53 +08:00
59e07a35aa Add balance to user 2024-02-28 16:54:30 +08:00
cabe830f55 feat: use dynamic import to load web3Auth (#2757)
* feat: use dynamic import to load web3Auth and success reduce the size of signin page to 720KB when web3 idp disabled

* feat: avoid frequent import in OAuthWidget.js which may cause e2e test EPIPE error

* feat: remove import may cause e2e error

* feat: remove import may cause e2e error

* feat: remove bug may cause e2e error

* feat: try use chrome in ci/cd instead of electron to solve e2e error
2024-02-28 15:58:04 +08:00
78af5daec3 feat: use resourcesToBackend to load i18n files (#2755) 2024-02-28 01:43:55 +08:00
6c76913f71 fix: Set default value for email and SMS rule to all instead of none (#2754) 2024-02-28 01:28:59 +08:00
5a0d1bcb6e Support login by user ID 2024-02-28 01:28:24 +08:00
37232faa07 feat: fix bug for missing SMS and Email provider in application 2024-02-27 22:54:35 +08:00
4d9c81ef96 Fix broken error messages 2024-02-27 22:48:33 +08:00
b0d87f60ae feat: use lazy load to load management pages (#2752) 2024-02-27 22:31:02 +08:00
a5499219d1 fix: refactor out ManagementPage.js from App.js (#2750)
* feat: basic separate

* feat: nearly fully separate

* feat: add License

* feat: full load application in /login url, lazy load in /login/oauth... etc

* fix: fix onChangeTheme error in organization edit page

* fix: revert lazy load
2024-02-27 18:49:23 +08:00
6a813a1f8c feat: fix headerHtml script not running bug (#2749)
* fix: fix custom head not exec <script> tag

* fix: fix create element bug
2024-02-26 20:21:07 +08:00
e4cf244cf8 fix: theme will fully restore after page reload (#2743)
* fix: theme will set to default after flush

* fix: use consume theme to ensure EntryPage will always use default themeAlgorithm

* fix: fix logo render, add try catch to handle
potential err cause by JSON.parse
2024-02-25 00:05:13 +08:00
f5a6415e57 feat: improve dark theme UI (#2742) 2024-02-24 20:11:42 +08:00
13e871043c fix: fix theme switch bug (#2741) 2024-02-24 16:56:12 +08:00
a8699d0b87 feat: use React routing to remove spin between signup and signin pages (#2740)
* fix: Regarding the color of loading

* fix: use goToLinkSoft and use same code format with result and forget psw

* fix: update signup url
2024-02-24 12:59:09 +08:00
6621d693de feat: revert "feat: use i18next-resources-to-backend to lazy load i18n" (#2739)
This reverts commit dc3131c683.
2024-02-23 23:38:49 +08:00
dc3131c683 feat: use i18next-resources-to-backend to lazy load i18n (#2738)
* feat: use i18next-resources-to-backend to lazy load i18n file

* feat: change source in yarn.lock
2024-02-23 22:35:59 +08:00
042a8d0ad6 feat: add rule for SMS and Email provider (#2733)
* add phonecoderule

* feat:add phone code rule

* feat: add email rule

* fix: merge
2024-02-23 00:09:37 +08:00
44abfb3430 feat: support custom header HTML in entry pages (#2731) 2024-02-22 17:56:47 +08:00
53b8424a1f feat: fix JSON typo in init_data.json template 2024-02-21 17:33:08 +08:00
23c2ba3a2b feat: support ssh key/pem file in DB syncer (#2727)
* feat: support connect database with ssh tunnel in syncer

* feat: improve i18n translate

* feat: improve code format and i18n
2024-02-21 17:27:37 +08:00
3a9ffedce4 feat: support phone and Email in /api/login/oauth/access_token API (#2725)
Phone Number supports for /api/login/oauth/access_token as username

 Closes: #2724
2024-02-21 17:27:24 +08:00
03f005389f feat: fix organizationChangeTrigger() and userChangeTrigger() bugs 2024-02-21 01:14:32 +08:00
69a8346d05 Remove "/auto-signup/oauth/authorize" path introduced in PR: #896 2024-02-20 17:40:39 +08:00
546512a0ea Fix getCasvisorApplication() 2024-02-20 13:45:03 +08:00
c4a307b9ec feat: add built-in "Records" pages back (#2720) 2024-02-20 13:28:29 +08:00
90 changed files with 3991 additions and 972 deletions

View File

@ -108,6 +108,7 @@ jobs:
working-directory: ./web
- uses: cypress-io/github-action@v5
with:
browser: chrome
start: yarn start
wait-on: 'http://localhost:7001'
wait-on-timeout: 210

View File

@ -177,7 +177,7 @@ func (c *ApiController) GetOrganizationApplications() {
return
}
applications, err = object.GetAllowedApplications(applications, userId)
applications, err = object.GetAllowedApplications(applications, userId, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
@ -194,13 +194,19 @@ func (c *ApiController) GetOrganizationApplications() {
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
application, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
applications, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
applications := object.GetMaskedApplications(application, userId)
applications, err = object.GetAllowedApplications(applications, userId, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
applications = object.GetMaskedApplications(applications, userId)
c.ResponseOk(applications, paginator.Nums())
}
}

123
controllers/record.go Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2021 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 controllers
import (
"encoding/json"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetRecords
// @Title GetRecords
// @Tag Record API
// @Description get all records
// @Param pageSize query string true "The size of each page"
// @Param p query string true "The number of the page"
// @Success 200 {object} object.Record The Response object
// @router /get-records [get]
func (c *ApiController) GetRecords() {
organization, ok := c.RequireAdmin()
if !ok {
return
}
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
organizationName := c.Input().Get("organizationName")
if limit == "" || page == "" {
records, err := object.GetRecords()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records)
} else {
limit := util.ParseInt(limit)
if c.IsGlobalAdmin() && organizationName != "" {
organization = organizationName
}
filterRecord := &casvisorsdk.Record{Organization: organization}
count, err := object.GetRecordCount(field, value, filterRecord)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
records, err := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder, filterRecord)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records, paginator.Nums())
}
}
// GetRecordsByFilter
// @Tag Record API
// @Title GetRecordsByFilter
// @Description get records by filter
// @Param filter body string true "filter Record message"
// @Success 200 {object} object.Record The Response object
// @router /get-records-filter [post]
func (c *ApiController) GetRecordsByFilter() {
body := string(c.Ctx.Input.RequestBody)
record := &casvisorsdk.Record{}
err := util.JsonToStruct(body, record)
if err != nil {
c.ResponseError(err.Error())
return
}
records, err := object.GetRecordsByField(record)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records)
}
// AddRecord
// @Title AddRecord
// @Tag Record API
// @Description add a record
// @Param body body object.Record true "The details of the record"
// @Success 200 {object} controllers.Response The Response object
// @router /add-record [post]
func (c *ApiController) AddRecord() {
var record casvisorsdk.Record
err := json.Unmarshal(c.Ctx.Input.RequestBody, &record)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddRecord(&record))
c.ServeJSON()
}

View File

@ -168,3 +168,20 @@ func (c *ApiController) RunSyncer() {
c.ResponseOk()
}
func (c *ApiController) TestSyncerDb() {
var syncer object.Syncer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
if err != nil {
c.ResponseError(err.Error())
return
}
err = object.TestSyncerDb(syncer)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
}

167
controllers/transaction.go Normal file
View File

@ -0,0 +1,167 @@
// Copyright 2024 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 controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetTransactions
// @Title GetTransactions
// @Tag Transaction API
// @Description get transactions
// @Param owner query string true "The owner of transactions"
// @Success 200 {array} object.Transaction The Response object
// @router /get-transactions [get]
func (c *ApiController) GetTransactions() {
owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" {
transactions, err := object.GetTransactions(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transactions)
} else {
limit := util.ParseInt(limit)
count, err := object.GetTransactionCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
transactions, err := object.GetPaginationTransactions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transactions, paginator.Nums())
}
}
// GetUserTransactions
// @Title GetUserTransaction
// @Tag Transaction API
// @Description get transactions for a user
// @Param owner query string true "The owner of transactions"
// @Param organization query string true "The organization of the user"
// @Param user query string true "The username of the user"
// @Success 200 {array} object.Transaction The Response object
// @router /get-user-transactions [get]
func (c *ApiController) GetUserTransactions() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
transactions, err := object.GetUserTransactions(owner, user)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transactions)
}
// GetTransaction
// @Title GetTransaction
// @Tag Transaction API
// @Description get transaction
// @Param id query string true "The id ( owner/name ) of the transaction"
// @Success 200 {object} object.Transaction The Response object
// @router /get-transaction [get]
func (c *ApiController) GetTransaction() {
id := c.Input().Get("id")
transaction, err := object.GetTransaction(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transaction)
}
// UpdateTransaction
// @Title UpdateTransaction
// @Tag Transaction API
// @Description update transaction
// @Param id query string true "The id ( owner/name ) of the transaction"
// @Param body body object.Transaction true "The details of the transaction"
// @Success 200 {object} controllers.Response The Response object
// @router /update-transaction [post]
func (c *ApiController) UpdateTransaction() {
id := c.Input().Get("id")
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateTransaction(id, &transaction))
c.ServeJSON()
}
// AddTransaction
// @Title AddTransaction
// @Tag Transaction API
// @Description add transaction
// @Param body body object.Transaction true "The details of the transaction"
// @Success 200 {object} controllers.Response The Response object
// @router /add-transaction [post]
func (c *ApiController) AddTransaction() {
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddTransaction(&transaction))
c.ServeJSON()
}
// DeleteTransaction
// @Title DeleteTransaction
// @Tag Transaction API
// @Description delete transaction
// @Param body body object.Transaction true "The details of the transaction"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-transaction [post]
func (c *ApiController) DeleteTransaction() {
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteTransaction(&transaction))
c.ServeJSON()
}

View File

@ -164,7 +164,7 @@ func (c *ApiController) SendVerificationCode() {
c.SetSession(MfaDestSession, vform.Dest)
}
provider, err = application.GetEmailProvider()
provider, err = application.GetEmailProvider(vform.Method)
if err != nil {
c.ResponseError(err.Error())
return
@ -210,7 +210,7 @@ func (c *ApiController) SendVerificationCode() {
vform.CountryCode = mfaProps.CountryCode
}
provider, err = application.GetSmsProvider()
provider, err = application.GetSmsProvider(vform.Method)
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -27,7 +27,10 @@ import (
)
func deployStaticFiles(provider *object.Provider) {
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
if err != nil {
panic(err)
}
if storageProvider == nil {
panic(fmt.Sprintf("the provider type: %s is not supported", provider.Type))
}

6
go.mod
View File

@ -9,10 +9,10 @@ require (
github.com/beego/beego v1.12.12
github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.77.2
github.com/casdoor/go-sms-sender v0.19.0
github.com/casdoor/go-sms-sender v0.20.0
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.5.0
github.com/casdoor/oss v1.6.0
github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.0.3
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
@ -59,7 +59,7 @@ require (
github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.14.0
golang.org/x/crypto v0.19.0
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.13.0
google.golang.org/api v0.150.0

20
go.sum
View File

@ -1083,14 +1083,14 @@ github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
github.com/casdoor/go-sms-sender v0.19.0 h1:qVz7RLXx8aGgfzLUGvhNe6pbhxDqP2/qNY+XPepfpyQ=
github.com/casdoor/go-sms-sender v0.19.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs=
github.com/casdoor/go-sms-sender v0.20.0 h1:yLbCakV04DzzehhgBklOrSeCFjMwpfKBeemz9b+Y8OM=
github.com/casdoor/go-sms-sender v0.20.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs=
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
github.com/casdoor/oss v1.5.0 h1:mi1htaXR5fynskDry1S3wk+Dd2nRY1z1pVcnGsqMqP4=
github.com/casdoor/oss v1.5.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.0.3 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
@ -2117,8 +2117,9 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -2447,8 +2448,9 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -2465,8 +2467,9 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -2486,8 +2489,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -8,12 +8,62 @@
"favicon": "",
"passwordType": "plain",
"passwordSalt": "",
"passwordOptions": ["AtLeast6"],
"countryCodes": ["US", "GB", "ES", "FR", "DE", "CN", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR", "DZ", "IL", "PH", "NL", "PL", "FI", "SE", "UA", "KZ"],
"passwordOptions": [
"AtLeast6"
],
"countryCodes": [
"US",
"GB",
"ES",
"FR",
"DE",
"CN",
"JP",
"KR",
"VN",
"ID",
"SG",
"IN",
"IT",
"MY",
"TR",
"DZ",
"IL",
"PH",
"NL",
"PL",
"FI",
"SE",
"UA",
"KZ"
],
"defaultAvatar": "",
"defaultApplication": "",
"tags": [],
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "nl", "pl", "fi", "sv", "uk", "kk", "fa"],
"languages": [
"en",
"zh",
"es",
"fr",
"de",
"id",
"ja",
"ko",
"ru",
"vi",
"it",
"ms",
"tr",
"ar",
"he",
"nl",
"pl",
"fi",
"sv",
"uk",
"kk",
"fa"
],
"masterPassword": "",
"defaultPassword": "",
"initScore": 2000,
@ -49,18 +99,18 @@
{
"name": "Password",
"displayName": "Password",
"rule": "All",
"rule": "All"
},
{
"name": "Verification code",
"displayName": "Verification code",
"rule": "All",
"rule": "All"
},
{
"name": "WebAuthn",
"displayName": "WebAuthn",
"rule": "None",
},
"rule": "None"
}
],
"signupItems": [
{
@ -72,56 +122,65 @@
},
{
"name": "Username",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
},
{
"name": "Display name",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
},
{
"name": "Password",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
},
{
"name": "Confirm password",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
},
{
"name": "Email",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
},
{
"name": "Phone",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
},
{
"name": "Agreement",
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
"visible": true,
"required": true,
"prompted": false,
"rule": "None"
}
],
"grantTypes": ["authorization_code", "password", "client_credentials", "token", "id_token", "refresh_token"],
"redirectUris": [""],
"grantTypes": [
"authorization_code",
"password",
"client_credentials",
"token",
"id_token",
"refresh_token"
],
"redirectUris": [
""
],
"expireInHours": 168,
"failedSigninLimit": 5,
"failedSigninFrozenTime": 15
@ -354,71 +413,71 @@
],
"groups": [
{
"owner": "",
"name":"",
"displayName": "",
"manager": "",
"contactEmail": "",
"type": "",
"parent_id": "",
"isTopGroup": true,
"title": "",
"key": "",
"children": "",
"isEnabled": true
"owner": "",
"name": "",
"displayName": "",
"manager": "",
"contactEmail": "",
"type": "",
"parent_id": "",
"isTopGroup": true,
"title": "",
"key": "",
"children": "",
"isEnabled": true
}
],
"adapters": [
{
"owner": "",
"name": "",
"table": "",
"useSameDb": true,
"type": "",
"databaseType": "",
"database": "",
"host": "",
"port": 0,
"user": "",
"password": "",
"owner": "",
"name": "",
"table": "",
"useSameDb": true,
"type": "",
"databaseType": "",
"database": "",
"host": "",
"port": 0,
"user": "",
"password": ""
}
],
"enforcers": [
{
"owner": "",
"name": "",
"displayName": "",
"description": "",
"model": "",
"adapter": "",
"enforcer": ""
"owner": "",
"name": "",
"displayName": "",
"description": "",
"model": "",
"adapter": "",
"enforcer": ""
}
],
"plans": [
{
"owner": "",
"name": "",
"displayName": "",
"description": "",
"price": 0,
"currency": "",
"period": "",
"product": "",
"paymentProviders": [],
"isEnabled": true,
"role", ""
"owner": "",
"name": "",
"displayName": "",
"description": "",
"price": 0,
"currency": "",
"period": "",
"product": "",
"paymentProviders": [],
"isEnabled": true,
"role": ""
}
],
"pricings": [
{
"owner": "",
"name": "",
"displayName": "",
"description": "",
"plans": [],
"isEnabled": true,
"trialDuration": 0,
"application": "",
"owner": "",
"name": "",
"displayName": "",
"description": "",
"plans": [],
"isEnabled": true,
"trialDuration": 0,
"application": ""
}
]
}

View File

@ -19,6 +19,7 @@ import (
"regexp"
"strings"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
)
@ -66,6 +67,7 @@ type Application struct {
Description string `xorm:"varchar(100)" json:"description"`
Organization string `xorm:"varchar(100)" json:"organization"`
Cert string `xorm:"varchar(100)" json:"cert"`
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
EnablePassword bool `json:"enablePassword"`
EnableSignUp bool `json:"enableSignUp"`
EnableSigninSession bool `json:"enableSigninSession"`
@ -221,7 +223,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Logo",
Visible: true,
Label: "\n<style>\n .login-logo-box {\n }\n<style>\n",
Label: "\n<style>\n .login-logo-box {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -229,7 +231,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Signin methods",
Visible: true,
Label: "\n<style>\n .signin-methods {\n }\n<style>\n",
Label: "\n<style>\n .signin-methods {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -237,7 +239,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Username",
Visible: true,
Label: "\n<style>\n .login-username {\n }\n<style>\n",
Label: "\n<style>\n .login-username {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -245,7 +247,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Password",
Visible: true,
Label: "\n<style>\n .login-password {\n }\n<style>\n",
Label: "\n<style>\n .login-password {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -253,7 +255,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Agreement",
Visible: true,
Label: "\n<style>\n .login-agreement {\n }\n<style>\n",
Label: "\n<style>\n .login-agreement {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -261,7 +263,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Forgot password?",
Visible: true,
Label: "\n<style>\n .login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n }\n<style>\n",
Label: "\n<style>\n .login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -269,7 +271,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Login button",
Visible: true,
Label: "\n<style>\n .login-button-box {\n margin-bottom: 5px;\n }\n .login-button {\n width: 100%;\n }\n<style>\n",
Label: "\n<style>\n .login-button-box {\n margin-bottom: 5px;\n }\n .login-button {\n width: 100%;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -277,7 +279,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{
Name: "Signup link",
Visible: true,
Label: "\n<style>\n .login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}\n<style>\n",
Label: "\n<style>\n .login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}\n</style>\n",
Placeholder: "",
Rule: "None",
}
@ -467,36 +469,70 @@ func GetMaskedApplication(application *Application, userId string) *Application
application.FailedSigninFrozenTime = DefaultFailedSigninFrozenTime
}
isOrgUser := false
if userId != "" {
if isUserIdGlobalAdmin(userId) {
return application
}
user, _ := GetUser(userId)
if user != nil && user.IsApplicationAdmin(application) {
return application
user, err := GetUser(userId)
if err != nil {
panic(err)
}
if user != nil {
if user.IsApplicationAdmin(application) {
return application
}
if user.Owner == application.Organization {
isOrgUser = true
}
}
}
if application.ClientSecret != "" {
application.ClientSecret = "***"
application.ClientSecret = "***"
application.Cert = "***"
application.EnablePassword = false
application.EnableSigninSession = false
application.EnableCodeSignin = false
application.EnableSamlCompress = false
application.EnableSamlC14n10 = false
application.EnableSamlPostBinding = false
application.EnableWebAuthn = false
application.EnableLinkWithEmail = false
application.SamlReplyUrl = "***"
providerItems := []*ProviderItem{}
for _, providerItem := range application.Providers {
if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3") {
providerItems = append(providerItems, providerItem)
}
}
application.Providers = providerItems
application.GrantTypes = nil
application.Tags = nil
application.RedirectUris = nil
application.TokenFormat = "***"
application.TokenFields = nil
application.ExpireInHours = -1
application.RefreshExpireInHours = -1
application.FailedSigninLimit = -1
application.FailedSigninFrozenTime = -1
if application.OrganizationObj != nil {
if application.OrganizationObj.MasterPassword != "" {
application.OrganizationObj.MasterPassword = "***"
}
if application.OrganizationObj.DefaultPassword != "" {
application.OrganizationObj.DefaultPassword = "***"
}
if application.OrganizationObj.MasterVerificationCode != "" {
application.OrganizationObj.MasterVerificationCode = "***"
}
if application.OrganizationObj.PasswordType != "" {
application.OrganizationObj.PasswordType = "***"
}
if application.OrganizationObj.PasswordSalt != "" {
application.OrganizationObj.PasswordSalt = "***"
application.OrganizationObj.MasterPassword = "***"
application.OrganizationObj.DefaultPassword = "***"
application.OrganizationObj.MasterVerificationCode = "***"
application.OrganizationObj.PasswordType = "***"
application.OrganizationObj.PasswordSalt = "***"
application.OrganizationObj.InitScore = -1
application.OrganizationObj.EnableSoftDeletion = false
application.OrganizationObj.IsProfilePublic = false
if !isOrgUser {
application.OrganizationObj.MfaItems = nil
application.OrganizationObj.AccountItems = nil
}
}
@ -514,8 +550,12 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
return applications
}
func GetAllowedApplications(applications []*Application, userId string) ([]*Application, error) {
if userId == "" || isUserIdGlobalAdmin(userId) {
func GetAllowedApplications(applications []*Application, userId string, lang string) ([]*Application, error) {
if userId == "" {
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
}
if isUserIdGlobalAdmin(userId) {
return applications, nil
}
@ -523,7 +563,11 @@ func GetAllowedApplications(applications []*Application, userId string) ([]*Appl
if err != nil {
return nil, err
}
if user != nil && user.IsAdmin {
if user == nil {
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
}
if user.IsAdmin {
return applications, nil
}
@ -638,7 +682,7 @@ func (application *Application) GetId() string {
}
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app"}, application.RedirectUris...)
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...)
for _, targetUri := range redirectUris {
targetUriRegex := regexp.MustCompile(targetUri)
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {

View File

@ -38,12 +38,38 @@ func (application *Application) GetProviderByCategory(category string) (*Provide
return nil, nil
}
func (application *Application) GetEmailProvider() (*Provider, error) {
return application.GetProviderByCategory("Email")
func (application *Application) GetProviderByCategoryAndRule(category string, method string) (*Provider, error) {
providers, err := GetProviders(application.Organization)
if err != nil {
return nil, err
}
m := map[string]*Provider{}
for _, provider := range providers {
if provider.Category != category {
continue
}
m[provider.Name] = provider
}
for _, providerItem := range application.Providers {
if providerItem.Rule == method || (providerItem.Rule == "all" || providerItem.Rule == "" || providerItem.Rule == "None") {
if provider, ok := m[providerItem.Name]; ok {
return provider, nil
}
}
}
return nil, nil
}
func (application *Application) GetSmsProvider() (*Provider, error) {
return application.GetProviderByCategory("SMS")
func (application *Application) GetEmailProvider(method string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("Email", method)
}
func (application *Application) GetSmsProvider(method string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("SMS", method)
}
func (application *Application) GetStorageProvider() (*Provider, error) {

View File

@ -17,29 +17,35 @@ package object
import (
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
)
type InitData struct {
Organizations []*Organization `json:"organizations"`
Applications []*Application `json:"applications"`
Users []*User `json:"users"`
Certs []*Cert `json:"certs"`
Providers []*Provider `json:"providers"`
Ldaps []*Ldap `json:"ldaps"`
Models []*Model `json:"models"`
Permissions []*Permission `json:"permissions"`
Payments []*Payment `json:"payments"`
Products []*Product `json:"products"`
Resources []*Resource `json:"resources"`
Roles []*Role `json:"roles"`
Syncers []*Syncer `json:"syncers"`
Tokens []*Token `json:"tokens"`
Webhooks []*Webhook `json:"webhooks"`
Groups []*Group `json:"groups"`
Adapters []*Adapter `json:"adapters"`
Enforcers []*Enforcer `json:"enforcers"`
Plans []*Plan `json:"plans"`
Pricings []*Pricing `json:"pricings"`
Organizations []*Organization `json:"organizations"`
Applications []*Application `json:"applications"`
Users []*User `json:"users"`
Certs []*Cert `json:"certs"`
Providers []*Provider `json:"providers"`
Ldaps []*Ldap `json:"ldaps"`
Models []*Model `json:"models"`
Permissions []*Permission `json:"permissions"`
Payments []*Payment `json:"payments"`
Products []*Product `json:"products"`
Resources []*Resource `json:"resources"`
Roles []*Role `json:"roles"`
Syncers []*Syncer `json:"syncers"`
Tokens []*Token `json:"tokens"`
Webhooks []*Webhook `json:"webhooks"`
Groups []*Group `json:"groups"`
Adapters []*Adapter `json:"adapters"`
Enforcers []*Enforcer `json:"enforcers"`
Plans []*Plan `json:"plans"`
Pricings []*Pricing `json:"pricings"`
Invitations []*Invitation `json:"invitations"`
Records []*casvisorsdk.Record `json:"records"`
Sessions []*Session `json:"sessions"`
Subscriptions []*Subscription `json:"subscriptions"`
Transactions []*Transaction `json:"transactions"`
}
func InitFromFile() {
@ -114,6 +120,21 @@ func InitFromFile() {
for _, pricing := range initData.Pricings {
initDefinedPricing(pricing)
}
for _, invitation := range initData.Invitations {
initDefinedInvitation(invitation)
}
for _, record := range initData.Records {
initDefinedRecord(record)
}
for _, session := range initData.Sessions {
initDefinedSession(session)
}
for _, subscription := range initData.Subscriptions {
initDefinedSubscription(subscription)
}
for _, transaction := range initData.Transactions {
initDefinedTransaction(transaction)
}
}
}
@ -145,6 +166,11 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
Enforcers: []*Enforcer{},
Plans: []*Plan{},
Pricings: []*Pricing{},
Invitations: []*Invitation{},
Records: []*casvisorsdk.Record{},
Sessions: []*Session{},
Subscriptions: []*Subscription{},
Transactions: []*Transaction{},
}
err := util.JsonToStruct(s, data)
if err != nil {
@ -225,6 +251,11 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
pricing.Plans = []string{}
}
}
for _, session := range data.Sessions {
if session.SessionId == nil {
session.SessionId = []string{}
}
}
return data, nil
}
@ -543,3 +574,61 @@ func initDefinedPricing(pricing *Pricing) {
panic(err)
}
}
func initDefinedInvitation(invitation *Invitation) {
existed, err := getInvitation(invitation.Owner, invitation.Name)
if err != nil {
panic(err)
}
if existed != nil {
return
}
invitation.CreatedTime = util.GetCurrentTime()
_, err = AddInvitation(invitation, "en")
if err != nil {
panic(err)
}
}
func initDefinedRecord(record *casvisorsdk.Record) {
record.CreatedTime = util.GetCurrentTime()
_ = AddRecord(record)
}
func initDefinedSession(session *Session) {
session.CreatedTime = util.GetCurrentTime()
_, err := AddSession(session)
if err != nil {
panic(err)
}
}
func initDefinedSubscription(subscription *Subscription) {
existed, err := getSubscription(subscription.Owner, subscription.Name)
if err != nil {
panic(err)
}
if existed != nil {
return
}
subscription.CreatedTime = util.GetCurrentTime()
_, err = AddSubscription(subscription)
if err != nil {
panic(err)
}
}
func initDefinedTransaction(transaction *Transaction) {
existed, err := getTransaction(transaction.Owner, transaction.Name)
if err != nil {
panic(err)
}
if existed != nil {
return
}
transaction.CreatedTime = util.GetCurrentTime()
_, err = AddTransaction(transaction)
if err != nil {
panic(err)
}
}

View File

@ -121,6 +121,31 @@ func writeInitDataToFile(filePath string) error {
return err
}
invitations, err := GetInvitations("")
if err != nil {
return err
}
records, err := GetRecords()
if err != nil {
return err
}
sessions, err := GetSessions("")
if err != nil {
return err
}
subscriptions, err := GetSubscriptions("")
if err != nil {
return err
}
transactions, err := GetTransactions("")
if err != nil {
return err
}
data := &InitData{
Organizations: organizations,
Applications: applications,
@ -142,6 +167,11 @@ func writeInitDataToFile(filePath string) error {
Enforcers: enforcers,
Plans: plans,
Pricings: pricings,
Invitations: invitations,
Records: records,
Sessions: sessions,
Subscriptions: subscriptions,
Transactions: transactions,
}
text := util.StructToJsonFormatted(data)

View File

@ -459,6 +459,16 @@ func organizationChangeTrigger(oldName string, newName string) error {
return err
}
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
if err.Error() != "no columns found to be updated" {
return err
}
}
resource := new(Resource)
resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource)

View File

@ -23,6 +23,8 @@ import (
"runtime"
"strings"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
"github.com/beego/beego"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
@ -30,8 +32,9 @@ import (
_ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres
"github.com/xorm-io/core"
"github.com/xorm-io/xorm"
"github.com/xorm-io/xorm/core"
"github.com/xorm-io/xorm/names"
_ "modernc.org/sqlite" // db = sqlite
)
@ -96,7 +99,7 @@ func InitAdapter() {
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, tableNamePrefix)
tbMapper := names.NewPrefixMapper(names.SnakeMapper{}, tableNamePrefix)
ormer.Engine.SetTableMapper(tbMapper)
}
@ -116,6 +119,7 @@ type Ormer struct {
driverName string
dataSourceName string
dbName string
Db *sql.DB
Engine *xorm.Engine
}
@ -125,6 +129,13 @@ func finalizer(a *Ormer) {
if err != nil {
panic(err)
}
if a.Db != nil {
err = a.Db.Close()
if err != nil {
panic(err)
}
}
}
// NewAdapter is the constructor for Ormer.
@ -146,6 +157,26 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) (*Ormer
return a, nil
}
// NewAdapterFromdb is the constructor for Ormer.
func NewAdapterFromDb(driverName string, dataSourceName string, dbName string, db *sql.DB) (*Ormer, error) {
a := &Ormer{}
a.driverName = driverName
a.dataSourceName = dataSourceName
a.dbName = dbName
a.Db = db
// Open the DB, create it if not existed.
err := a.openFromDb(a.Db)
if err != nil {
return nil, err
}
// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)
return a, nil
}
func refineDataSourceNameForPostgres(dataSourceName string) string {
reg := regexp.MustCompile(`dbname=[^ ]+\s*`)
return reg.ReplaceAllString(dataSourceName, "")
@ -224,6 +255,30 @@ func (a *Ormer) open() error {
return nil
}
func (a *Ormer) openFromDb(db *sql.DB) error {
dataSourceName := a.dataSourceName + a.dbName
if a.driverName != "mysql" {
dataSourceName = a.dataSourceName
}
xormDb := core.FromDB(db)
engine, err := xorm.NewEngineWithDB(a.driverName, dataSourceName, xormDb)
if err != nil {
return err
}
if a.driverName == "postgres" {
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
if schema != "" {
engine.SetSchema(schema)
}
}
a.Engine = engine
return nil
}
func (a *Ormer) close() {
_ = a.Engine.Close()
a.Engine = nil
@ -333,11 +388,21 @@ func (a *Ormer) createTable() {
panic(err)
}
err = a.Engine.Sync2(new(Transaction))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Syncer))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(casvisorsdk.Record))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Webhook))
if err != nil {
panic(err)

View File

@ -82,7 +82,12 @@ func AddRecord(record *casvisorsdk.Record) bool {
}
if casvisorsdk.GetClient() == nil {
return false
affected, err := ormer.Engine.Insert(record)
if err != nil {
panic(err)
}
return affected != 0
}
affected, err := casvisorsdk.AddRecord(record)
@ -93,13 +98,55 @@ func AddRecord(record *casvisorsdk.Record) bool {
return affected
}
func getFilteredWebhooks(webhooks []*Webhook, action string) []*Webhook {
func GetRecordCount(field, value string, filterRecord *casvisorsdk.Record) (int64, error) {
session := GetSession("", -1, -1, field, value, "", "")
return session.Count(filterRecord)
}
func GetRecords() ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.Record{}
err := ormer.Engine.Desc("id").Find(&records)
if err != nil {
return records, err
}
return records, nil
}
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.Record{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&records, filterRecord)
if err != nil {
return records, err
}
return records, nil
}
func GetRecordsByField(record *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.Record{}
err := ormer.Engine.Find(&records, record)
if err != nil {
return records, err
}
return records, nil
}
func getFilteredWebhooks(webhooks []*Webhook, organization string, action string) []*Webhook {
res := []*Webhook{}
for _, webhook := range webhooks {
if !webhook.IsEnabled {
continue
}
if webhook.SingleOrgOnly {
if webhook.Organization != organization {
continue
}
}
matched := false
for _, event := range webhook.Events {
if action == event {
@ -122,7 +169,7 @@ func SendWebhooks(record *casvisorsdk.Record) error {
}
errs := []error{}
webhooks = getFilteredWebhooks(webhooks, record.Action)
webhooks = getFilteredWebhooks(webhooks, record.Organization, record.Action)
for _, webhook := range webhooks {
var user *User
if webhook.IsUserExtended {

View File

@ -27,7 +27,7 @@ func getCasvisorApplication() *Application {
}
for _, application := range applications {
if strings.Contains(strings.ToLower(application.Name), "casvisor") {
if strings.Contains(strings.ToLower(application.Name), "casvisor-my") {
return application
}
}

View File

@ -109,14 +109,17 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if err != nil {
return nil, err
}
if storageProvider == nil {
return nil, fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
}
if provider.Domain == "" {
provider.Domain = storageProvider.GetEndpoint()
_, err := UpdateProvider(provider.GetId(), provider)
_, err = UpdateProvider(provider.GetId(), provider)
if err != nil {
return nil, err
}

View File

@ -39,11 +39,17 @@ type Syncer struct {
Type string `xorm:"varchar(100)" json:"type"`
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
SslMode string `xorm:"varchar(100)" json:"sslMode"`
SshType string `xorm:"varchar(100)" json:"sshType"`
Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"`
User string `xorm:"varchar(100)" json:"user"`
Password string `xorm:"varchar(150)" json:"password"`
SshHost string `xorm:"varchar(100)" json:"sshHost"`
SshPort int `json:"sshPort"`
SshUser string `xorm:"varchar(100)" json:"sshUser"`
SshPassword string `xorm:"varchar(150)" json:"sshPassword"`
Cert string `xorm:"varchar(100)" json:"cert"`
Database string `xorm:"varchar(100)" json:"database"`
Table string `xorm:"varchar(100)" json:"table"`
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
@ -279,3 +285,25 @@ func RunSyncer(syncer *Syncer) error {
return syncer.syncUsers()
}
func TestSyncerDb(syncer Syncer) error {
oldSyncer, err := getSyncer(syncer.Owner, syncer.Name)
if err != nil {
return err
}
if syncer.Password == "***" {
syncer.Password = oldSyncer.Password
}
err = syncer.initAdapter()
if err != nil {
return err
}
err = syncer.Ormer.Engine.Ping()
if err != nil {
return err
}
return nil
}

View File

@ -15,12 +15,17 @@
package object
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"strings"
"time"
"golang.org/x/crypto/ssh"
"github.com/casdoor/casdoor/util"
"github.com/go-sql-driver/mysql"
)
type OriginalUser = User
@ -124,6 +129,19 @@ func (syncer *Syncer) calculateHash(user *OriginalUser) string {
return util.GetMd5Hash(s)
}
type dsnConnector struct {
dsn string
driver driver.Driver
}
func (t dsnConnector) Connect(ctx context.Context) (driver.Conn, error) {
return t.driver.Open(t.dsn)
}
func (t dsnConnector) Driver() driver.Driver {
return t.driver
}
func (syncer *Syncer) initAdapter() error {
if syncer.Ormer != nil {
return nil
@ -142,12 +160,38 @@ func (syncer *Syncer) initAdapter() error {
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
}
var db *sql.DB
var err error
if syncer.SshType != "" && (syncer.DatabaseType == "mysql" || syncer.DatabaseType == "postgres" || syncer.DatabaseType == "mssql") {
var dial *ssh.Client
if syncer.SshType == "password" {
dial, err = DialWithPassword(syncer.SshUser, syncer.SshPassword, syncer.SshHost, syncer.SshPort)
} else {
dial, err = DialWithCert(syncer.SshUser, syncer.Owner+"/"+syncer.Cert, syncer.SshHost, syncer.SshPort)
}
if err != nil {
return err
}
if syncer.DatabaseType == "mysql" {
dataSourceName = fmt.Sprintf("%s:%s@%s(%s:%d)/", syncer.User, syncer.Password, syncer.Owner+syncer.Name, syncer.Host, syncer.Port)
mysql.RegisterDialContext(syncer.Owner+syncer.Name, (&ViaSSHDialer{Client: dial, Context: nil}).MysqlDial)
} else if syncer.DatabaseType == "postgres" || syncer.DatabaseType == "mssql" {
db = sql.OpenDB(dsnConnector{dsn: dataSourceName, driver: &ViaSSHDialer{Client: dial, Context: nil, DatabaseType: syncer.DatabaseType}})
}
}
if !isCloudIntranet {
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
}
var err error
syncer.Ormer, err = NewAdapter(syncer.DatabaseType, dataSourceName, syncer.Database)
if db != nil {
syncer.Ormer, err = NewAdapterFromDb(syncer.DatabaseType, dataSourceName, syncer.Database, db)
} else {
syncer.Ormer, err = NewAdapter(syncer.DatabaseType, dataSourceName, syncer.Database)
}
return err
}

View File

@ -681,7 +681,7 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
// GetPasswordToken
// Resource Owner Password Credentials flow
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError, error) {
user, err := getUser(application.Organization, username)
user, err := GetUserByFields(application.Organization, username)
if err != nil {
return nil, nil, err
}

144
object/transaction.go Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2024 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 (
"fmt"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
)
type Transaction struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
// Transaction Provider Info
Provider string `xorm:"varchar(100)" json:"provider"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
// Product Info
ProductName string `xorm:"varchar(100)" json:"productName"`
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
Detail string `xorm:"varchar(255)" json:"detail"`
Tag string `xorm:"varchar(100)" json:"tag"`
Currency string `xorm:"varchar(100)" json:"currency"`
Amount float64 `json:"amount"`
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
// User Info
User string `xorm:"varchar(100)" json:"user"`
Application string `xorm:"varchar(100)" json:"application"`
Payment string `xorm:"varchar(100)" json:"payment"`
State string `xorm:"varchar(100)" json:"state"`
}
func GetTransactionCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Transaction{Owner: owner})
}
func GetTransactions(owner string) ([]*Transaction, error) {
transactions := []*Transaction{}
err := ormer.Engine.Desc("created_time").Find(&transactions, &Transaction{Owner: owner})
if err != nil {
return nil, err
}
return transactions, nil
}
func GetUserTransactions(owner, user string) ([]*Transaction, error) {
transactions := []*Transaction{}
err := ormer.Engine.Desc("created_time").Find(&transactions, &Transaction{Owner: owner, User: user})
if err != nil {
return nil, err
}
return transactions, nil
}
func GetPaginationTransactions(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Transaction, error) {
transactions := []*Transaction{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&transactions, &Transaction{Owner: owner})
if err != nil {
return nil, err
}
return transactions, nil
}
func getTransaction(owner string, name string) (*Transaction, error) {
if owner == "" || name == "" {
return nil, nil
}
transaction := Transaction{Owner: owner, Name: name}
existed, err := ormer.Engine.Get(&transaction)
if err != nil {
return nil, err
}
if existed {
return &transaction, nil
} else {
return nil, nil
}
}
func GetTransaction(id string) (*Transaction, error) {
owner, name := util.GetOwnerAndNameFromId(id)
return getTransaction(owner, name)
}
func UpdateTransaction(id string, transaction *Transaction) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id)
if p, err := getTransaction(owner, name); err != nil {
return false, err
} else if p == nil {
return false, nil
}
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(transaction)
if err != nil {
return false, err
}
return affected != 0, nil
}
func AddTransaction(transaction *Transaction) (bool, error) {
affected, err := ormer.Engine.Insert(transaction)
if err != nil {
return false, err
}
return affected != 0, nil
}
func DeleteTransaction(transaction *Transaction) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{transaction.Owner, transaction.Name}).Delete(&Transaction{})
if err != nil {
return false, err
}
return affected != 0, nil
}
func (transaction *Transaction) GetId() string {
return fmt.Sprintf("%s/%s", transaction.Owner, transaction.Name)
}

View File

@ -86,6 +86,8 @@ type User struct {
Score int `json:"score"`
Karma int `json:"karma"`
Ranking int `json:"ranking"`
Balance float64 `json:"balance"`
Currency string `xorm:"varchar(100)" json:"currency"`
IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"`
@ -675,7 +677,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
}
}
if isAdmin {
columns = append(columns, "name", "email", "phone", "country_code", "type")
columns = append(columns, "name", "id", "email", "phone", "country_code", "type")
}
columns = append(columns, "updated_time")
@ -1021,6 +1023,10 @@ func userChangeTrigger(oldName string, newName string) error {
}
for _, permission := range permissions {
for j, u := range permission.Users {
if u == "*" {
continue
}
// u = organization/username
owner, name := util.GetOwnerAndNameFromId(u)
if name == oldName {

View File

@ -77,6 +77,12 @@ func GetUserByFields(organization string, field string) (*User, error) {
return user, err
}
// check user ID
user, err = GetUserByField(organization, "id", field)
if user != nil || err != nil {
return user, err
}
// check ID card
user, err = GetUserByField(organization, "id_card", field)
if user != nil || err != nil {

116
object/viaSSHDialer.go Normal file
View File

@ -0,0 +1,116 @@
// Copyright 2024 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 (
"context"
"database/sql/driver"
"fmt"
"net"
"time"
mssql "github.com/denisenkom/go-mssqldb"
"github.com/lib/pq"
"golang.org/x/crypto/ssh"
)
type ViaSSHDialer struct {
Client *ssh.Client
Context *context.Context
DatabaseType string
}
func (v *ViaSSHDialer) MysqlDial(ctx context.Context, addr string) (net.Conn, error) {
return v.Client.Dial("tcp", addr)
}
func (v *ViaSSHDialer) Open(s string) (_ driver.Conn, err error) {
if v.DatabaseType == "mssql" {
c, err := mssql.NewConnector(s)
if err != nil {
return nil, err
}
c.Dialer = v
return c.Connect(context.Background())
} else if v.DatabaseType == "postgres" {
return pq.DialOpen(v, s)
}
return nil, nil
}
func (v *ViaSSHDialer) Dial(network, address string) (net.Conn, error) {
return v.Client.Dial(network, address)
}
func (v *ViaSSHDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
return v.Client.DialContext(ctx, network, addr)
}
func (v *ViaSSHDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
return v.Client.Dial(network, address)
}
func DialWithPassword(SshUser string, SshPassword string, SshHost string, SshPort int) (*ssh.Client, error) {
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
config := &ssh.ClientConfig{
User: SshUser,
Auth: []ssh.AuthMethod{
ssh.Password(SshPassword),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
return ssh.Dial("tcp", address, config)
}
func DialWithCert(SshUser string, CertId string, SshHost string, SshPort int) (*ssh.Client, error) {
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
config := &ssh.ClientConfig{
User: SshUser,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
cert, err := GetCert(CertId)
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey([]byte(cert.PrivateKey))
if err != nil {
return nil, err
}
config.Auth = []ssh.AuthMethod{
ssh.PublicKeys(signer),
}
return ssh.Dial("tcp", address, config)
}
func DialWithPrivateKey(SshUser string, PrivateKey []byte, SshHost string, SshPort int) (*ssh.Client, error) {
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
config := &ssh.ClientConfig{
User: SshUser,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
signer, err := ssh.ParsePrivateKey(PrivateKey)
if err != nil {
return nil, err
}
config.Auth = []ssh.AuthMethod{
ssh.PublicKeys(signer),
}
return ssh.Dial("tcp", address, config)
}

View File

@ -39,6 +39,7 @@ type Webhook struct {
Headers []*Header `xorm:"mediumtext" json:"headers"`
Events []string `xorm:"varchar(1000)" json:"events"`
IsUserExtended bool `json:"isUserExtended"`
SingleOrgOnly bool `json:"singleOrgOnly"`
IsEnabled bool `json:"isEnabled"`
}

View File

@ -48,7 +48,7 @@ func CorsFilter(ctx *context.Context) {
originHostname := getHostname(origin)
host := removePort(ctx.Request.Host)
if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") {
if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") || strings.Contains(origin, ".chromiumapp.org") {
setCorsHeaders(ctx, origin)
return
}

View File

@ -221,6 +221,12 @@ func initAPI() {
beego.Router("/api/add-subscription", &controllers.ApiController{}, "POST:AddSubscription")
beego.Router("/api/delete-subscription", &controllers.ApiController{}, "POST:DeleteSubscription")
beego.Router("/api/get-transactions", &controllers.ApiController{}, "GET:GetTransactions")
beego.Router("/api/get-transaction", &controllers.ApiController{}, "GET:GetTransaction")
beego.Router("/api/update-transaction", &controllers.ApiController{}, "POST:UpdateTransaction")
beego.Router("/api/add-transaction", &controllers.ApiController{}, "POST:AddTransaction")
beego.Router("/api/delete-transaction", &controllers.ApiController{}, "POST:DeleteTransaction")
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo")
beego.Router("/api/health", &controllers.ApiController{}, "GET:Health")
@ -233,6 +239,7 @@ func initAPI() {
beego.Router("/api/add-syncer", &controllers.ApiController{}, "POST:AddSyncer")
beego.Router("/api/delete-syncer", &controllers.ApiController{}, "POST:DeleteSyncer")
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
beego.Router("/api/test-syncer-db", &controllers.ApiController{}, "POST:TestSyncerDb")
beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")
@ -261,6 +268,10 @@ func initAPI() {
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord")
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification")

View File

@ -19,8 +19,8 @@ import (
"github.com/casdoor/oss/qiniu"
)
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
sp := qiniu.New(&qiniu.Config{
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
sp, err := qiniu.New(&qiniu.Config{
AccessID: clientId,
AccessKey: clientSecret,
Region: region,
@ -28,5 +28,5 @@ func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, regi
Endpoint: endpoint,
})
return sp
return sp, err
}

View File

@ -16,27 +16,27 @@ package storage
import "github.com/casdoor/oss"
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
switch providerType {
case "Local File System":
return NewLocalFileSystemStorageProvider()
return NewLocalFileSystemStorageProvider(), nil
case "AWS S3":
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "MinIO":
return NewMinIOS3StorageProvider(clientId, clientSecret, "_", bucket, endpoint)
return NewMinIOS3StorageProvider(clientId, clientSecret, "_", bucket, endpoint), nil
case "Aliyun OSS":
return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint)
return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "Tencent Cloud COS":
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint)
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "Azure Blob":
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint)
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "Qiniu Cloud Kodo":
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Google Cloud Storage":
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint)
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil
case "Synology":
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint)
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
}
return nil
return nil, nil
}

View File

@ -12,67 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {Component} from "react";
import React, {Component, Suspense, lazy} from "react";
import "./App.less";
import {Helmet} from "react-helmet";
import * as Setting from "./Setting";
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
import {AppstoreTwoTone, BarsOutlined, DeploymentUnitOutlined, DollarTwoTone, DownOutlined, GithubOutlined, HomeTwoTone, InfoCircleFilled, LockTwoTone, LogoutOutlined, SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone, ShareAltOutlined, WalletTwoTone} from "@ant-design/icons";
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result, Tooltip} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import AccountPage from "./account/AccountPage";
import Dashboard from "./basic/Dashboard";
import ShortcutsPage from "./basic/ShortcutsPage";
import AppListPage from "./basic/AppListPage";
import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
import GroupEditPage from "./GroupEdit";
import GroupListPage from "./GroupList";
import GroupTreePage from "./GroupTreePage";
import UserListPage from "./UserListPage";
import UserEditPage from "./UserEditPage";
import InvitationListPage from "./InvitationListPage";
import InvitationEditPage from "./InvitationEditPage";
import ApplicationListPage from "./ApplicationListPage";
import ApplicationEditPage from "./ApplicationEditPage";
import ProviderListPage from "./ProviderListPage";
import ProviderEditPage from "./ProviderEditPage";
import ResourceListPage from "./ResourceListPage";
import CertListPage from "./CertListPage";
import CertEditPage from "./CertEditPage";
import RoleListPage from "./RoleListPage";
import RoleEditPage from "./RoleEditPage";
import PermissionListPage from "./PermissionListPage";
import PermissionEditPage from "./PermissionEditPage";
import ModelListPage from "./ModelListPage";
import ModelEditPage from "./ModelEditPage";
import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage";
import EnforcerEditPage from "./EnforcerEditPage";
import EnforcerListPage from "./EnforcerListPage";
import SessionListPage from "./SessionListPage";
import TokenListPage from "./TokenListPage";
import TokenEditPage from "./TokenEditPage";
import ProductListPage from "./ProductListPage";
import ProductEditPage from "./ProductEditPage";
import ProductBuyPage from "./ProductBuyPage";
import PaymentListPage from "./PaymentListPage";
import PaymentEditPage from "./PaymentEditPage";
import PaymentResultPage from "./PaymentResultPage";
import PricingListPage from "./PricingListPage";
import PricingEditPage from "./PricingEditPage";
import PlanListPage from "./PlanListPage";
import PlanEditPage from "./PlanEditPage";
import SubscriptionListPage from "./SubscriptionListPage";
import SubscriptionEditPage from "./SubscriptionEditPage";
import SystemInfo from "./SystemInfo";
import SyncerListPage from "./SyncerListPage";
import SyncerEditPage from "./SyncerEditPage";
import WebhookListPage from "./WebhookListPage";
import WebhookEditPage from "./WebhookEditPage";
import LdapEditPage from "./LdapEditPage";
import LdapSyncPage from "./LdapSyncPage";
import MfaSetupPage from "./auth/MfaSetupPage";
import {GithubOutlined, InfoCircleFilled, ShareAltOutlined} from "@ant-design/icons";
import {Alert, Button, ConfigProvider, Drawer, FloatButton, Layout, Result, Tooltip} from "antd";
import {Route, Switch, withRouter} from "react-router-dom";
import CustomGithubCorner from "./common/CustomGithubCorner";
import * as Conf from "./Conf";
@ -80,19 +27,11 @@ import * as Auth from "./auth/Auth";
import EntryPage from "./EntryPage";
import * as AuthBackend from "./auth/AuthBackend";
import AuthCallback from "./auth/AuthCallback";
import OdicDiscoveryPage from "./auth/OidcDiscoveryPage";
import SamlCallback from "./auth/SamlCallback";
import i18next from "i18next";
import {withTranslation} from "react-i18next";
import EnableMfaNotification from "./common/notifaction/EnableMfaNotification";
import LanguageSelect from "./common/select/LanguageSelect";
import ThemeSelect from "./common/select/ThemeSelect";
import OrganizationSelect from "./common/select/OrganizationSelect";
import {clearWeb3AuthToken} from "./auth/Web3Auth";
import AccountAvatar from "./account/AccountAvatar";
import OpenTour from "./common/OpenTour";
const {Header, Footer, Content} = Layout;
const ManagementPage = lazy(() => import("./ManagementPage"));
const {Footer, Content} = Layout;
import {setTwoToneColor} from "@ant-design/icons";
@ -101,19 +40,23 @@ setTwoToneColor("rgb(87,52,211)");
class App extends Component {
constructor(props) {
super(props);
let storageThemeAlgorithm = [];
try {
storageThemeAlgorithm = localStorage.getItem("themeAlgorithm") ? JSON.parse(localStorage.getItem("themeAlgorithm")) : ["default"];
} catch {
storageThemeAlgorithm = ["default"];
}
this.state = {
classes: props,
selectedMenuKey: 0,
account: undefined,
uri: null,
menuVisible: false,
themeAlgorithm: ["default"],
themeAlgorithm: storageThemeAlgorithm,
themeData: Conf.ThemeDefault,
logo: this.getLogo(Setting.getAlgorithmNames(Conf.ThemeDefault)),
logo: this.getLogo(storageThemeAlgorithm),
requiredEnableMfa: false,
isAiAssistantOpen: false,
};
Setting.initServerUrl();
Auth.initAuthWithConfig({
serverUrl: Setting.ServerUrl,
@ -227,6 +170,19 @@ class App extends Component {
});
if (initThemeAlgorithm) {
if (localStorage.getItem("themeAlgorithm")) {
let storageThemeAlgorithm = [];
try {
storageThemeAlgorithm = JSON.parse(localStorage.getItem("themeAlgorithm"));
} catch {
storageThemeAlgorithm = ["default"];
}
this.setState({
logo: this.getLogo(storageThemeAlgorithm),
themeAlgorithm: storageThemeAlgorithm,
});
return;
}
this.setState({
logo: this.getLogo(Setting.getAlgorithmNames(theme)),
themeAlgorithm: Setting.getAlgorithmNames(theme),
@ -273,385 +229,12 @@ class App extends Component {
});
}
logout() {
this.setState({
expired: false,
submitted: false,
});
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
const owner = this.state.account.owner;
this.setState({
account: null,
themeAlgorithm: ["default"],
});
clearWeb3AuthToken();
Setting.showMessage("success", i18next.t("application:Logged out successfully"));
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
} else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft(this, "/");
}
} else {
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});
}
onUpdateAccount(account) {
this.setState({
account: account,
});
}
renderAvatar() {
if (this.state.account.avatar === "") {
return (
<Avatar style={{backgroundColor: Setting.getAvatarColor(this.state.account.name), verticalAlign: "middle"}} size="large">
{Setting.getShortName(this.state.account.name)}
</Avatar>
);
} else {
return (
<Avatar src={this.state.account.avatar} style={{verticalAlign: "middle"}} size="large"
icon={<AccountAvatar src={this.state.account.avatar} style={{verticalAlign: "middle"}} size={40} />}
>
{Setting.getShortName(this.state.account.name)}
</Avatar>
);
}
}
renderRightDropdown() {
const items = [];
if (this.state.requiredEnableMfa === false) {
items.push(Setting.getItem(<><SettingOutlined />&nbsp;&nbsp;{i18next.t("account:My Account")}</>,
"/account"
));
}
items.push(Setting.getItem(<><LogoutOutlined />&nbsp;&nbsp;{i18next.t("account:Logout")}</>,
"/logout"));
const onClick = (e) => {
if (e.key === "/account") {
this.props.history.push("/account");
} else if (e.key === "/subscription") {
this.props.history.push("/subscription");
} else if (e.key === "/logout") {
this.logout();
}
};
return (
<Dropdown key="/rightDropDown" menu={{items, onClick}} >
<div className="rightDropDown">
{
this.renderAvatar()
}
&nbsp;
&nbsp;
{Setting.isMobile() ? null : Setting.getShortText(Setting.getNameAtLeast(this.state.account.displayName), 30)} &nbsp; <DownOutlined />
&nbsp;
&nbsp;
&nbsp;
</div>
</Dropdown>
);
}
renderAccountMenu() {
if (this.state.account === undefined) {
return null;
} else if (this.state.account === null) {
return (
<React.Fragment>
<LanguageSelect />
</React.Fragment>
);
} else {
return (
<React.Fragment>
{this.renderRightDropdown()}
<ThemeSelect
themeAlgorithm={this.state.themeAlgorithm}
onChange={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
}} />
<LanguageSelect languages={this.state.account.organization.languages} />
<Tooltip title="Click to open AI assitant">
<div className="select-box" onClick={() => {
this.setState({
isAiAssistantOpen: true,
});
}}>
<DeploymentUnitOutlined style={{fontSize: "24px", color: "rgb(77,77,77)"}} />
</div>
</Tooltip>
<OpenTour />
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() && (this.state.uri.indexOf("/trees") === -1) &&
<OrganizationSelect
initValue={Setting.getOrganization()}
withAll={true}
style={{marginRight: "20px", width: "180px", display: "flex"}}
onChange={(value) => {
Setting.setOrganization(value);
}}
className="select-box"
/>
}
</React.Fragment>
);
}
}
getMenuItems() {
const res = [];
if (this.state.account === null || this.state.account === undefined) {
return [];
}
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/home", <HomeTwoTone />, [
Setting.getItem(<Link to="/">{i18next.t("general:Dashboard")}</Link>, "/"),
Setting.getItem(<Link to="/shortcuts">{i18next.t("general:Shortcuts")}</Link>, "/shortcuts"),
Setting.getItem(<Link to="/apps">{i18next.t("general:Apps")}</Link>, "/apps"),
].filter(item => {
return Setting.isLocalAdminUser(this.state.account);
})));
if (Setting.isLocalAdminUser(this.state.account)) {
if (Conf.ShowGithubCorner) {
res.push(Setting.getItem(<a href={"https://casdoor.com"}>
<span style={{fontWeight: "bold", backgroundColor: "rgba(87,52,211,0.4)", marginTop: "12px", paddingLeft: "5px", paddingRight: "5px", display: "flex", alignItems: "center", height: "40px", borderRadius: "5px"}}>
🚀 SaaS Hosting 🔥
</span>
</a>, "#"));
}
res.push(Setting.getItem(<Link style={{color: "black"}} to="/organizations">{i18next.t("general:User Management")}</Link>, "/orgs", <AppstoreTwoTone />, [
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
Setting.getItem(<Link to="/invitations">{i18next.t("general:Invitations")}</Link>, "/invitations"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone />, [
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>, "/models"),
Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>, "/adapters"),
Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>, "/enforcers"),
].filter(item => {
if (!Setting.isLocalAdminUser(this.state.account) && ["/models", "/adapters", "/enforcers"].includes(item.key)) {
return false;
} else {
return true;
}
})));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sessions">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
Setting.getItem(<a target="_blank" rel="noreferrer" href={Conf.CasvisorUrl}>{i18next.t("general:Records")}</a>, "/records"),
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone />, [
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>, "/pricings"),
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
]));
if (Setting.isAdminUser(this.state.account)) {
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
} else {
res.push(Setting.getItem(<Link style={{color: "black"}} to="/syncers">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks")]));
}
}
return res;
}
renderLoginIfNotLoggedIn(component) {
if (this.state.account === null) {
sessionStorage.setItem("from", window.location.pathname);
return <Redirect to="/login" />;
} else if (this.state.account === undefined) {
return null;
} else {
return component;
}
}
renderRouter() {
return (
<Switch>
<Route exact path="/" render={(props) => this.renderLoginIfNotLoggedIn(<Dashboard account={this.state.account} {...props} />)} />
<Route exact path="/apps" render={(props) => this.renderLoginIfNotLoggedIn(<AppListPage account={this.state.account} {...props} />)} />
<Route exact path="/shortcuts" render={(props) => this.renderLoginIfNotLoggedIn(<ShortcutsPage account={this.state.account} {...props} />)} />
<Route exact path="/account" render={(props) => this.renderLoginIfNotLoggedIn(<AccountPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} onChangeTheme={this.setTheme} {...props} />)} />
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/trees/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
<Route exact path="/trees/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
<Route exact path="/groups" render={(props) => this.renderLoginIfNotLoggedIn(<GroupListPage account={this.state.account} {...props} />)} />
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
<Route exact path="/invitations" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationListPage account={this.state.account} {...props} />)} />
<Route exact path="/invitations/:organizationName/:invitationName" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:organizationName/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
<Route exact path="/roles/:organizationName/:roleName" render={(props) => this.renderLoginIfNotLoggedIn(<RoleEditPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionListPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionEditPage account={this.state.account} {...props} />)} />
<Route exact path="/models" render={(props) => this.renderLoginIfNotLoggedIn(<ModelListPage account={this.state.account} {...props} />)} />
<Route exact path="/models/:organizationName/:modelName" render={(props) => this.renderLoginIfNotLoggedIn(<ModelEditPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
<Route exact path="/enforcers" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerListPage account={this.state.account} {...props} />)} />
<Route exact path="/enforcers/:organizationName/:enforcerName" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/sessions" render={(props) => this.renderLoginIfNotLoggedIn(<SessionListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
<Route exact path="/plans/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => this.renderLoginIfNotLoggedIn(<PricingEditPage account={this.state.account} {...props} />)} />
<Route exact path="/subscriptions" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionListPage account={this.state.account} {...props} />)} />
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionEditPage account={this.state.account} {...props} />)} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/sync/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapSyncPage account={this.state.account} {...props} />)} />
<Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={() => this.setState({requiredEnableMfa: false})} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
);
}
onClose = () => {
this.setState({
menuVisible: false,
});
};
showMenu = () => {
this.setState({
menuVisible: true,
});
};
isWithoutCard() {
return Setting.isMobile() || window.location.pathname.startsWith("/trees");
}
renderContent() {
const onClick = ({key}) => {
if (key !== "/swagger" && key !== "/records") {
if (this.state.requiredEnableMfa) {
Setting.showMessage("info", "Please enable MFA first!");
} else {
this.props.history.push(key);
}
}
};
const menuStyleRight = Setting.isAdminUser(this.state.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "280px";
return (
<Layout id="parent-area">
<EnableMfaNotification account={this.state.account} />
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm.includes("dark") ? "black" : "white"}} >
{Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" style={{background: `url(${this.state.logo})`}} />
</Link>
)}
{this.state.requiredEnableMfa || (Setting.isMobile() ?
<React.Fragment>
<Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}>
<Menu
items={this.getMenuItems()}
mode={"inline"}
selectedKeys={[this.state.selectedMenuKey]}
style={{lineHeight: "64px"}}
onClick={this.onClose}
>
</Menu>
</Drawer>
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
</React.Fragment> :
<Menu
onClick={onClick}
items={this.getMenuItems()}
mode={"horizontal"}
selectedKeys={[this.state.selectedMenuKey]}
style={{position: "absolute", left: "145px", right: menuStyleRight}}
/>
)}
{
this.renderAccountMenu()
}
</Header>
<Content style={{display: "flex", flexDirection: "column"}} >
{this.isWithoutCard() ?
this.renderRouter() :
<Card className="content-warp-card">
{this.renderRouter()}
</Card>
}
</Content>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
);
}
renderFooter() {
return (
<React.Fragment>
@ -718,49 +301,63 @@ class App extends Component {
window.location.pathname.startsWith("/prompt") ||
window.location.pathname.startsWith("/result") ||
window.location.pathname.startsWith("/cas") ||
window.location.pathname.startsWith("/auto-signup") ||
window.location.pathname.startsWith("/select-plan") ||
window.location.pathname.startsWith("/buy-plan") ||
window.location.pathname.startsWith("/qrcode") ;
}
onClick = ({key}) => {
if (key !== "/swagger" && key !== "/records") {
if (this.state.requiredEnableMfa) {
Setting.showMessage("info", "Please enable MFA first!");
} else {
this.props.history.push(key);
}
}
};
renderPage() {
if (this.isDoorPages()) {
return (
<Layout id="parent-area">
<Content style={{display: "flex", justifyContent: "center"}}>
{
this.isEntryPages() ?
<EntryPage
account={this.state.account}
theme={this.state.themeData}
onLoginSuccess={(redirectUrl) => {
if (redirectUrl) {
localStorage.setItem("mfaRedirectUrl", redirectUrl);
}
this.getAccount();
}}
onUpdateAccount={(account) => this.onUpdateAccount(account)}
updataThemeData={this.setTheme}
/> :
<Switch>
<Route exact path="/callback" component={AuthCallback} />
<Route exact path="/callback/saml" component={SamlCallback} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
}
</Content>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
<ConfigProvider theme={{
algorithm: Setting.getAlgorithm(["default"]),
}}>
<StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}>
<Layout id="parent-area">
<Content style={{display: "flex", justifyContent: "center"}}>
{
this.isEntryPages() ?
<EntryPage
account={this.state.account}
theme={this.state.themeData}
onLoginSuccess={(redirectUrl) => {
if (redirectUrl) {
localStorage.setItem("mfaRedirectUrl", redirectUrl);
}
this.getAccount();
}}
onUpdateAccount={(account) => this.onUpdateAccount(account)}
updataThemeData={this.setTheme}
/> :
<Switch>
<Route exact path="/callback" component={AuthCallback} />
<Route exact path="/callback/saml" component={SamlCallback} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
}
</Content>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
</StyleProvider>
</ConfigProvider>
);
}
return (
<React.Fragment>
{/* { */}
@ -769,7 +366,49 @@ class App extends Component {
<FloatButton.BackTop />
<CustomGithubCorner />
{
this.renderContent()
<Suspense fallback={<div>loading</div>}>
<Layout id="parent-area">
<ManagementPage
account={this.state.account}
uri={this.state.uri}
themeData={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}
selectedMenuKey={this.state.selectedMenuKey}
requiredEnableMfa={this.state.requiredEnableMfa}
menuVisible={this.state.menuVisible}
logo={this.state.logo}
onChangeTheme={this.setTheme}
onClick = {this.onClick}
onfinish={() => {
this.setState({requiredEnableMfa: false});
}}
openAiAssistant={() => {
this.setState({
isAiAssistantOpen: true,
});
}}
setLogoAndThemeAlgorithm={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
localStorage.setItem("themeAlgorithm", JSON.stringify(nextThemeAlgorithm));
}}
setLogoutState={() => {
this.setState({
account: null,
themeAlgorithm: ["default"],
});
}}
/>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
</Suspense>
}
</React.Fragment>
);

View File

@ -54,7 +54,7 @@ img {
cursor: pointer;
&:hover {
background-color: #f5f5f5 !important;
background-color: #f5f5f5a5 !important;
}
}
@ -67,8 +67,7 @@ img {
cursor: pointer;
&:hover {
background-color: #f5f5f5;
color: black;
background-color: #f5f5f5a5;
}
}

View File

@ -760,7 +760,7 @@ class ApplicationEditPage extends React.Component {
</Row>
<Row>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Form CSS"), i18next.t("application:Form CSS - Tooltip"))} :
{Setting.getLabel(i18next.t("application:Custom CSS"), i18next.t("application:Custom CSS - Tooltip"))} :
</Col>
<Col span={22}>
<Popover placement="right" content={
@ -772,7 +772,7 @@ class ApplicationEditPage extends React.Component {
}}
/>
</div>
} title={i18next.t("application:Form CSS - Edit")} trigger="click">
} title={i18next.t("application:Custom CSS - Edit")} trigger="click">
<Input value={this.state.application.formCss} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("formCss", e.target.value);
}} />
@ -781,7 +781,7 @@ class ApplicationEditPage extends React.Component {
</Row>
<Row>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Form CSS Mobile"), i18next.t("application:Form CSS Mobile - Tooltip"))} :
{Setting.getLabel(i18next.t("application:Custom CSS Mobile"), i18next.t("application:Custom CSS Mobile - Tooltip"))} :
</Col>
<Col span={22}>
<Popover placement="right" content={
@ -793,7 +793,7 @@ class ApplicationEditPage extends React.Component {
}}
/>
</div>
} title={i18next.t("application:Form CSS Mobile - Edit")} trigger="click">
} title={i18next.t("application:Custom CSS Mobile - Edit")} trigger="click">
<Input value={this.state.application.formCssMobile} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("formCssMobile", e.target.value);
}} />
@ -865,6 +865,28 @@ class ApplicationEditPage extends React.Component {
}
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Header HTML"), i18next.t("application:Header HTML - Tooltip"))} :
</Col>
<Col span={22} >
<Popover placement="right" content={
<div style={{width: "900px", height: "300px"}} >
<CodeMirror
value={this.state.application.headerHtml}
options={{mode: "htmlmixed", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => {
this.updateApplicationField("headerHtml", value);
}}
/>
</div>
} title={i18next.t("application:Header HTML - Edit")} trigger="click">
<Input value={this.state.application.headerHtml} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("headerHtml", e.target.value);
}} />
</Popover>
</Col>
</Row>
{
<React.Fragment>
<Row style={{marginTop: "20px"}} >

View File

@ -14,7 +14,7 @@
export const DefaultApplication = "app-built-in";
export const CasvisorUrl = "https://github.com/casbin/casvisor";
export const CasvisorUrl = "";
export const ShowGithubCorner = false;
export const IsDemoMode = false;

View File

@ -32,6 +32,7 @@ import {authConfig} from "./auth/Auth";
import ProductBuyPage from "./ProductBuyPage";
import PaymentResultPage from "./PaymentResultPage";
import QrCodePage from "./QrCodePage";
import CustomHead from "./basic/CustomHead";
class EntryPage extends React.Component {
constructor(props) {
@ -66,7 +67,6 @@ class EntryPage extends React.Component {
this.setState({
application: application,
});
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData);
};
@ -82,7 +82,6 @@ class EntryPage extends React.Component {
Setting.showMessage("error", res.msg);
return;
}
const application = res.data;
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData);
@ -90,33 +89,35 @@ class EntryPage extends React.Component {
};
return (
<div className="loginBackground"
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
style={{margin: "0 auto"}} />
<Switch>
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} applicationName={authConfig.appName} 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="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signup"} onUpdateApplication={onUpdateApplication}{...props} />} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
</Switch>
</div>
<React.Fragment>
<CustomHead headerHtml={this.state.application?.headerHtml} />
<div className="loginBackground"
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
style={{margin: "0 auto"}} />
<Switch>
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} applicationName={authConfig.appName} 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="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
</Switch>
</div>
</React.Fragment>
);
}
}

448
web/src/ManagementPage.js Normal file
View File

@ -0,0 +1,448 @@
// Copyright 2024 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 * as Setting from "./Setting";
import {Avatar, Button, Card, Drawer, Dropdown, Menu, Result, Tooltip} from "antd";
import EnableMfaNotification from "./common/notifaction/EnableMfaNotification";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import React, {useState} from "react";
import i18next from "i18next";
import {
AppstoreTwoTone,
BarsOutlined, DeploymentUnitOutlined, DollarTwoTone, DownOutlined,
HomeTwoTone,
LockTwoTone, LogoutOutlined,
SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone,
WalletTwoTone
} from "@ant-design/icons";
import Dashboard from "./basic/Dashboard";
import AppListPage from "./basic/AppListPage";
import ShortcutsPage from "./basic/ShortcutsPage";
import AccountPage from "./account/AccountPage";
import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
import UserListPage from "./UserListPage";
import GroupTreePage from "./GroupTreePage";
import GroupListPage from "./GroupList";
import GroupEditPage from "./GroupEdit";
import UserEditPage from "./UserEditPage";
import InvitationListPage from "./InvitationListPage";
import InvitationEditPage from "./InvitationEditPage";
import ApplicationListPage from "./ApplicationListPage";
import ApplicationEditPage from "./ApplicationEditPage";
import ProviderListPage from "./ProviderListPage";
import ProviderEditPage from "./ProviderEditPage";
import RecordListPage from "./RecordListPage";
import ResourceListPage from "./ResourceListPage";
import CertListPage from "./CertListPage";
import CertEditPage from "./CertEditPage";
import RoleListPage from "./RoleListPage";
import RoleEditPage from "./RoleEditPage";
import PermissionListPage from "./PermissionListPage";
import PermissionEditPage from "./PermissionEditPage";
import ModelListPage from "./ModelListPage";
import ModelEditPage from "./ModelEditPage";
import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage";
import EnforcerListPage from "./EnforcerListPage";
import EnforcerEditPage from "./EnforcerEditPage";
import SessionListPage from "./SessionListPage";
import TokenListPage from "./TokenListPage";
import TokenEditPage from "./TokenEditPage";
import ProductListPage from "./ProductListPage";
import ProductEditPage from "./ProductEditPage";
import ProductBuyPage from "./ProductBuyPage";
import PaymentListPage from "./PaymentListPage";
import PaymentEditPage from "./PaymentEditPage";
import PaymentResultPage from "./PaymentResultPage";
import PlanListPage from "./PlanListPage";
import PlanEditPage from "./PlanEditPage";
import PricingListPage from "./PricingListPage";
import PricingEditPage from "./PricingEditPage";
import SubscriptionListPage from "./SubscriptionListPage";
import SubscriptionEditPage from "./SubscriptionEditPage";
import SystemInfo from "./SystemInfo";
import SyncerListPage from "./SyncerListPage";
import SyncerEditPage from "./SyncerEditPage";
import WebhookListPage from "./WebhookListPage";
import WebhookEditPage from "./WebhookEditPage";
import LdapEditPage from "./LdapEditPage";
import LdapSyncPage from "./LdapSyncPage";
import MfaSetupPage from "./auth/MfaSetupPage";
import OdicDiscoveryPage from "./auth/OidcDiscoveryPage";
import * as Conf from "./Conf";
import LanguageSelect from "./common/select/LanguageSelect";
import ThemeSelect from "./common/select/ThemeSelect";
import OpenTour from "./common/OpenTour";
import OrganizationSelect from "./common/select/OrganizationSelect";
import AccountAvatar from "./account/AccountAvatar";
import {Content, Header} from "antd/es/layout/layout";
import * as AuthBackend from "./auth/AuthBackend";
import {clearWeb3AuthToken} from "./auth/Web3Auth";
import TransactionListPage from "./TransactionListPage";
import TransactionEditPage from "./TransactionEditPage";
function ManagementPage(props) {
const [menuVisible, setMenuVisible] = useState(false);
function logout() {
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
const owner = props.account.owner;
props.setLogoutState();
clearWeb3AuthToken();
Setting.showMessage("success", i18next.t("application:Logged out successfully"));
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
} else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft({props}, "/");
}
} else {
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});
}
function renderAvatar() {
if (props.account.avatar === "") {
return (
<Avatar style={{backgroundColor: Setting.getAvatarColor(props.account.name), verticalAlign: "middle"}} size="large">
{Setting.getShortName(props.account.name)}
</Avatar>
);
} else {
return (
<Avatar src={props.account.avatar} style={{verticalAlign: "middle"}} size="large"
icon={<AccountAvatar src={props.account.avatar} style={{verticalAlign: "middle"}} size={40} />}
>
{Setting.getShortName(props.account.name)}
</Avatar>
);
}
}
function renderRightDropdown() {
const items = [];
if (props.requiredEnableMfa === false) {
items.push(Setting.getItem(<><SettingOutlined />&nbsp;&nbsp;{i18next.t("account:My Account")}</>,
"/account"
));
}
items.push(Setting.getItem(<><LogoutOutlined />&nbsp;&nbsp;{i18next.t("account:Logout")}</>,
"/logout"));
const onClick = (e) => {
if (e.key === "/account") {
props.history.push("/account");
} else if (e.key === "/subscription") {
props.history.push("/subscription");
} else if (e.key === "/logout") {
logout();
}
};
return (
<Dropdown key="/rightDropDown" menu={{items, onClick}} >
<div className="rightDropDown">
{
renderAvatar()
}
&nbsp;
&nbsp;
{Setting.isMobile() ? null : Setting.getShortText(Setting.getNameAtLeast(props.account.displayName), 30)} &nbsp; <DownOutlined />
&nbsp;
&nbsp;
&nbsp;
</div>
</Dropdown>
);
}
function renderAccountMenu() {
if (props.account === undefined) {
return null;
} else if (props.account === null) {
return (
<React.Fragment>
<LanguageSelect />
</React.Fragment>
);
} else {
return (
<React.Fragment>
{renderRightDropdown()}
<ThemeSelect
themeAlgorithm={props.themeAlgorithm}
onChange={props.setLogoAndThemeAlgorithm} />
<LanguageSelect languages={props.account.organization.languages} />
<Tooltip title="Click to open AI assitant">
<div className="select-box" onClick={props.openAiAssistant}>
<DeploymentUnitOutlined style={{fontSize: "24px"}} />
</div>
</Tooltip>
<OpenTour />
{Setting.isAdminUser(props.account) && !Setting.isMobile() && (props.uri.indexOf("/trees") === -1) &&
<OrganizationSelect
initValue={Setting.getOrganization()}
withAll={true}
style={{marginRight: "20px", width: "180px", display: "flex"}}
onChange={(value) => {
Setting.setOrganization(value);
}}
className="select-box"
/>
}
</React.Fragment>
);
}
}
function getMenuItems() {
const res = [];
if (props.account === null || props.account === undefined) {
return [];
}
const textColor = props.themeAlgorithm.includes("dark") ? "white" : "black";
const twoToneColor = props.themeData.colorPrimary;
res.push(Setting.getItem(<Link style={{color: textColor}} to="/">{i18next.t("general:Home")}</Link>, "/home", <HomeTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/">{i18next.t("general:Dashboard")}</Link>, "/"),
Setting.getItem(<Link to="/shortcuts">{i18next.t("general:Shortcuts")}</Link>, "/shortcuts"),
Setting.getItem(<Link to="/apps">{i18next.t("general:Apps")}</Link>, "/apps"),
].filter(item => {
return Setting.isLocalAdminUser(props.account);
})));
if (Setting.isLocalAdminUser(props.account)) {
if (Conf.ShowGithubCorner) {
res.push(Setting.getItem(<a href={"https://casdoor.com"}>
<span style={{fontWeight: "bold", backgroundColor: "rgba(87,52,211,0.4)", marginTop: "12px", paddingLeft: "5px", paddingRight: "5px", display: "flex", alignItems: "center", height: "40px", borderRadius: "5px"}}>
🚀 SaaS Hosting 🔥
</span>
</a>, "#"));
}
res.push(Setting.getItem(<Link style={{color: textColor}} to="/organizations">{i18next.t("general:User Management")}</Link>, "/orgs", <AppstoreTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
Setting.getItem(<Link to="/invitations">{i18next.t("general:Invitations")}</Link>, "/invitations"),
]));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
]));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>, "/models"),
Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>, "/adapters"),
Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>, "/enforcers"),
].filter(item => {
if (!Setting.isLocalAdminUser(props.account) && ["/models", "/adapters", "/enforcers"].includes(item.key)) {
return false;
} else {
return true;
}
})));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/sessions">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
Conf.CasvisorUrl ? Setting.getItem(<a target="_blank" rel="noreferrer" href={Conf.CasvisorUrl}>{i18next.t("general:Records")}</a>, "/records")
: Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>, "/records"),
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
]));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>, "/pricings"),
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
Setting.getItem(<Link to="/transactions">{i18next.t("general:Transactions")}</Link>, "/transactions"),
]));
if (Setting.isAdminUser(props.account)) {
res.push(Setting.getItem(<Link style={{color: textColor}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
} else {
res.push(Setting.getItem(<Link style={{color: textColor}} to="/syncers">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks")]));
}
}
return res;
}
function renderLoginIfNotLoggedIn(component) {
if (props.account === null) {
sessionStorage.setItem("from", window.location.pathname);
return <Redirect to="/login" />;
} else if (props.account === undefined) {
return null;
} else {
return component;
}
}
function renderRouter() {
const account = props.account;
const onChangeTheme = props.onChangeTheme;
const onfinish = props.onfinish;
return (
<Switch>
<Route exact path="/" render={(props) => renderLoginIfNotLoggedIn(<Dashboard account={account} {...props} />)} />
<Route exact path="/apps" render={(props) => renderLoginIfNotLoggedIn(<AppListPage account={account} {...props} />)} />
<Route exact path="/shortcuts" render={(props) => renderLoginIfNotLoggedIn(<ShortcutsPage account={account} {...props} />)} />
<Route exact path="/account" render={(props) => renderLoginIfNotLoggedIn(<AccountPage account={account} {...props} />)} />
<Route exact path="/organizations" render={(props) => renderLoginIfNotLoggedIn(<OrganizationListPage account={account} {...props} />)} />
<Route exact path="/organizations/:organizationName" render={(props) => renderLoginIfNotLoggedIn(<OrganizationEditPage account={account} onChangeTheme={onChangeTheme} {...props} />)} />
<Route exact path="/organizations/:organizationName/users" render={(props) => renderLoginIfNotLoggedIn(<UserListPage account={account} {...props} />)} />
<Route exact path="/trees/:organizationName" render={(props) => renderLoginIfNotLoggedIn(<GroupTreePage account={account} {...props} />)} />
<Route exact path="/trees/:organizationName/:groupName" render={(props) => renderLoginIfNotLoggedIn(<GroupTreePage account={account} {...props} />)} />
<Route exact path="/groups" render={(props) => renderLoginIfNotLoggedIn(<GroupListPage account={account} {...props} />)} />
<Route exact path="/groups/:organizationName/:groupName" render={(props) => renderLoginIfNotLoggedIn(<GroupEditPage account={account} {...props} />)} />
<Route exact path="/users" render={(props) => renderLoginIfNotLoggedIn(<UserListPage account={account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={account} {...props} />} />
<Route exact path="/invitations" render={(props) => renderLoginIfNotLoggedIn(<InvitationListPage account={account} {...props} />)} />
<Route exact path="/invitations/:organizationName/:invitationName" render={(props) => renderLoginIfNotLoggedIn(<InvitationEditPage account={account} {...props} />)} />
<Route exact path="/applications" render={(props) => renderLoginIfNotLoggedIn(<ApplicationListPage account={account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => renderLoginIfNotLoggedIn(<ApplicationEditPage account={account} {...props} />)} />
<Route exact path="/providers" render={(props) => renderLoginIfNotLoggedIn(<ProviderListPage account={account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => renderLoginIfNotLoggedIn(<ProviderEditPage account={account} {...props} />)} />
<Route exact path="/records" render={(props) => renderLoginIfNotLoggedIn(<RecordListPage account={account} {...props} />)} />
<Route exact path="/resources" render={(props) => renderLoginIfNotLoggedIn(<ResourceListPage account={account} {...props} />)} />
<Route exact path="/certs" render={(props) => renderLoginIfNotLoggedIn(<CertListPage account={account} {...props} />)} />
<Route exact path="/certs/:organizationName/:certName" render={(props) => renderLoginIfNotLoggedIn(<CertEditPage account={account} {...props} />)} />
<Route exact path="/roles" render={(props) => renderLoginIfNotLoggedIn(<RoleListPage account={account} {...props} />)} />
<Route exact path="/roles/:organizationName/:roleName" render={(props) => renderLoginIfNotLoggedIn(<RoleEditPage account={account} {...props} />)} />
<Route exact path="/permissions" render={(props) => renderLoginIfNotLoggedIn(<PermissionListPage account={account} {...props} />)} />
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => renderLoginIfNotLoggedIn(<PermissionEditPage account={account} {...props} />)} />
<Route exact path="/models" render={(props) => renderLoginIfNotLoggedIn(<ModelListPage account={account} {...props} />)} />
<Route exact path="/models/:organizationName/:modelName" render={(props) => renderLoginIfNotLoggedIn(<ModelEditPage account={account} {...props} />)} />
<Route exact path="/adapters" render={(props) => renderLoginIfNotLoggedIn(<AdapterListPage account={account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => renderLoginIfNotLoggedIn(<AdapterEditPage account={account} {...props} />)} />
<Route exact path="/enforcers" render={(props) => renderLoginIfNotLoggedIn(<EnforcerListPage account={account} {...props} />)} />
<Route exact path="/enforcers/:organizationName/:enforcerName" render={(props) => renderLoginIfNotLoggedIn(<EnforcerEditPage account={account} {...props} />)} />
<Route exact path="/sessions" render={(props) => renderLoginIfNotLoggedIn(<SessionListPage account={account} {...props} />)} />
<Route exact path="/tokens" render={(props) => renderLoginIfNotLoggedIn(<TokenListPage account={account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => renderLoginIfNotLoggedIn(<TokenEditPage account={account} {...props} />)} />
<Route exact path="/products" render={(props) => renderLoginIfNotLoggedIn(<ProductListPage account={account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName" render={(props) => renderLoginIfNotLoggedIn(<ProductEditPage account={account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => renderLoginIfNotLoggedIn(<ProductBuyPage account={account} {...props} />)} />
<Route exact path="/payments" render={(props) => renderLoginIfNotLoggedIn(<PaymentListPage account={account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => renderLoginIfNotLoggedIn(<PaymentEditPage account={account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => renderLoginIfNotLoggedIn(<PaymentResultPage account={account} {...props} />)} />
<Route exact path="/plans" render={(props) => renderLoginIfNotLoggedIn(<PlanListPage account={account} {...props} />)} />
<Route exact path="/plans/:organizationName/:planName" render={(props) => renderLoginIfNotLoggedIn(<PlanEditPage account={account} {...props} />)} />
<Route exact path="/pricings" render={(props) => renderLoginIfNotLoggedIn(<PricingListPage account={account} {...props} />)} />
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => renderLoginIfNotLoggedIn(<PricingEditPage account={account} {...props} />)} />
<Route exact path="/subscriptions" render={(props) => renderLoginIfNotLoggedIn(<SubscriptionListPage account={account} {...props} />)} />
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => renderLoginIfNotLoggedIn(<SubscriptionEditPage account={account} {...props} />)} />
<Route exact path="/sysinfo" render={(props) => renderLoginIfNotLoggedIn(<SystemInfo account={account} {...props} />)} />
<Route exact path="/syncers" render={(props) => renderLoginIfNotLoggedIn(<SyncerListPage account={account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => renderLoginIfNotLoggedIn(<SyncerEditPage account={account} {...props} />)} />
<Route exact path="/transactions" render={(props) => renderLoginIfNotLoggedIn(<TransactionListPage account={account} {...props} />)} />
<Route exact path="/transactions/:organizationName/:transactionName" render={(props) => renderLoginIfNotLoggedIn(<TransactionEditPage account={account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => renderLoginIfNotLoggedIn(<WebhookListPage account={account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => renderLoginIfNotLoggedIn(<WebhookEditPage account={account} {...props} />)} />
<Route exact path="/ldap/:organizationName/:ldapId" render={(props) => renderLoginIfNotLoggedIn(<LdapEditPage account={account} {...props} />)} />
<Route exact path="/ldap/sync/:organizationName/:ldapId" render={(props) => renderLoginIfNotLoggedIn(<LdapSyncPage account={account} {...props} />)} />
<Route exact path="/mfa/setup" render={(props) => renderLoginIfNotLoggedIn(<MfaSetupPage account={account} onfinish={onfinish} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
);
}
function isWithoutCard() {
return Setting.isMobile() || window.location.pathname.startsWith("/trees");
}
const menuStyleRight = Setting.isAdminUser(props.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "280px";
const onClose = () => {
setMenuVisible(false);
};
const showMenu = () => {
setMenuVisible(true);
};
return (
<React.Fragment>
<EnableMfaNotification account={props.account} />
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}} >
{Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" style={{background: `url(${props.logo})`}} />
</Link>
)}
{props.requiredEnableMfa || (Setting.isMobile() ?
<React.Fragment>
<Drawer title={i18next.t("general:Close")} placement="left" visible={menuVisible} onClose={onClose}>
<Menu
items={getMenuItems()}
mode={"inline"}
selectedKeys={[props.selectedMenuKey]}
style={{lineHeight: "64px"}}
onClick={onClose}
>
</Menu>
</Drawer>
<Button icon={<BarsOutlined />} onClick={showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
</React.Fragment> :
<Menu
onClick={onClose}
items={getMenuItems()}
mode={"horizontal"}
selectedKeys={[props.selectedMenuKey]}
style={{position: "absolute", left: "145px", right: menuStyleRight, backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}}
/>
)}
{
renderAccountMenu()
}
</Header>
<Content style={{display: "flex", flexDirection: "column"}} >
{isWithoutCard() ?
renderRouter() :
<Card className="content-warp-card">
{renderRouter()}
</Card>
}
</Content>
</React.Fragment>
);
}
export default withRouter(ManagementPage);

View File

@ -91,7 +91,7 @@ class PermissionListPage extends BaseListPage {
const {pagination} = this.state;
this.fetch({pagination});
} else {
Setting.showMessage("error", `Users failed to upload: ${res.msg}`);
Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
}
} else if (status === "error") {
Setting.showMessage("error", "File failed to upload");

View File

@ -191,7 +191,7 @@ class ProviderEditPage extends React.Component {
return Setting.getLabel(i18next.t("provider:App key"), i18next.t("provider:App key - Tooltip"));
} else if (provider.type === "UCloud SMS") {
return Setting.getLabel(i18next.t("provider:Public key"), i18next.t("provider:Public key - Tooltip"));
} else if (provider.type === "Msg91 SMS" || provider.type === "Infobip SMS") {
} else if (provider.type === "Msg91 SMS" || provider.type === "Infobip SMS" || provider.type === "OSON SMS") {
return Setting.getLabel(i18next.t("provider:Sender Id"), i18next.t("provider:Sender Id - Tooltip"));
} else {
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
@ -234,7 +234,7 @@ class ProviderEditPage extends React.Component {
return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
}
case "SMS":
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS") {
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS" || provider.type === "OSON SMS") {
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
} else if (provider.type === "Huawei Cloud SMS") {
return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip"));

239
web/src/RecordListPage.js Normal file
View File

@ -0,0 +1,239 @@
// Copyright 2021 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 {Link} from "react-router-dom";
import {Switch, Table} from "antd";
import * as Setting from "./Setting";
import * as RecordBackend from "./backend/RecordBackend";
import i18next from "i18next";
import moment from "moment";
import BaseListPage from "./BaseListPage";
class RecordListPage extends BaseListPage {
UNSAFE_componentWillMount() {
this.state.pagination.pageSize = 20;
const {pagination} = this.state;
this.fetch({pagination});
}
newRecord() {
return {
owner: "built-in",
name: "1234",
id: "1234",
clientIp: "::1",
timestamp: moment().format(),
organization: "built-in",
username: "admin",
requestUri: "/api/get-account",
action: "login",
isTriggered: false,
};
}
renderTable(records) {
let columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "320px",
sorter: true,
...this.getColumnSearchProps("name"),
},
{
title: i18next.t("general:ID"),
dataIndex: "id",
key: "id",
width: "90px",
sorter: true,
...this.getColumnSearchProps("id"),
},
{
title: i18next.t("general:Client IP"),
dataIndex: "clientIp",
key: "clientIp",
width: "150px",
sorter: true,
...this.getColumnSearchProps("clientIp"),
render: (text, record, index) => {
return (
<a target="_blank" rel="noreferrer" href={`https://db-ip.com/${text}`}>
{text}
</a>
);
},
},
{
title: i18next.t("general:Timestamp"),
dataIndex: "createdTime",
key: "createdTime",
width: "180px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "organization",
key: "organization",
width: "110px",
sorter: true,
...this.getColumnSearchProps("organization"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:User"),
dataIndex: "user",
key: "user",
width: "120px",
sorter: true,
...this.getColumnSearchProps("user"),
render: (text, record, index) => {
return (
<Link to={`/users/${record.organization}/${record.user}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Method"),
dataIndex: "method",
key: "method",
width: "110px",
sorter: true,
filterMultiple: false,
filters: [
{text: "GET", value: "GET"},
{text: "HEAD", value: "HEAD"},
{text: "POST", value: "POST"},
{text: "PUT", value: "PUT"},
{text: "DELETE", value: "DELETE"},
{text: "CONNECT", value: "CONNECT"},
{text: "OPTIONS", value: "OPTIONS"},
{text: "TRACE", value: "TRACE"},
{text: "PATCH", value: "PATCH"},
],
},
{
title: i18next.t("general:Request URI"),
dataIndex: "requestUri",
key: "requestUri",
// width: '300px',
sorter: true,
...this.getColumnSearchProps("requestUri"),
},
{
title: i18next.t("general:Action"),
dataIndex: "action",
key: "action",
width: "200px",
sorter: true,
...this.getColumnSearchProps("action"),
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return text;
},
},
{
title: i18next.t("record:Is triggered"),
dataIndex: "isTriggered",
key: "isTriggered",
width: "140px",
sorter: true,
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
if (!["signup", "login", "logout", "update-user"].includes(record.action)) {
return null;
}
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
);
},
},
];
if (Setting.isLocalAdminUser(this.props.account)) {
columns = columns.filter(column => column.key !== "name");
}
const paginationProps = {
total: this.state.pagination.total,
pageSize: this.state.pagination.pageSize,
showQuickJumper: true,
showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={records} rowKey="id" size="middle" bordered pagination={paginationProps}
title={() => (
<div>
{i18next.t("general:Records")}&nbsp;&nbsp;&nbsp;&nbsp;
</div>
)}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
</div>
);
}
fetch = (params = {}) => {
let field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder;
if (params.method !== undefined && params.method !== null) {
field = "method";
value = params.method;
}
this.setState({loading: true});
RecordBackend.getRecords(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => {
this.setState({
loading: false,
});
if (res.status === "ok") {
this.setState({
data: res.data,
pagination: {
...params.pagination,
total: res.data2,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.data.includes("Please login first")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};
}
export default RecordListPage;

View File

@ -83,7 +83,7 @@ class RoleListPage extends BaseListPage {
const {pagination} = this.state;
this.fetch({pagination});
} else {
Setting.showMessage("error", `Users failed to upload: ${res.msg}`);
Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
}
} else if (status === "error") {
Setting.showMessage("error", "File failed to upload");

View File

@ -69,7 +69,7 @@ export function getThemeData(organization, application) {
}
export function getAlgorithm(themeAlgorithmNames) {
return themeAlgorithmNames.map((algorithmName) => {
return themeAlgorithmNames.sort().reverse().map((algorithmName) => {
if (algorithmName === "dark") {
return theme.darkAlgorithm;
}
@ -143,6 +143,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_msg91.ico`,
url: "https://control.msg91.com/app/",
},
"OSON SMS": {
logo: "https://osonsms.com/images/osonsms-logo.svg",
url: "https://osonsms.com/",
},
"Custom HTTP SMS": {
logo: `${StaticBaseUrl}/img/social_default.png`,
url: "https://casdoor.org/docs/provider/sms/overview",
@ -703,6 +707,15 @@ export function goToLinkSoft(ths, link) {
ths.props.history.push(link);
}
export function goToLinkSoftOrJumpSelf(ths, link) {
if (link.startsWith("http")) {
goToLink(link);
return;
}
ths.props.history.push(link);
}
export function showMessage(type, text) {
if (type === "success") {
message.success(text);
@ -1005,6 +1018,7 @@ export function getProviderTypeOptions(category) {
{id: "Azure ACS", name: "Azure ACS"},
{id: "Custom HTTP SMS", name: "Custom HTTP SMS"},
{id: "Mock SMS", name: "Mock SMS"},
{id: "OSON SMS", name: "OSON SMS"},
{id: "Infobip SMS", name: "Infobip SMS"},
{id: "Tencent Cloud SMS", name: "Tencent Cloud SMS"},
{id: "Baidu Cloud SMS", name: "Baidu Cloud SMS"},
@ -1164,8 +1178,8 @@ export function getLoginLink(application) {
let url;
if (application === null) {
url = null;
} else if (window.location.pathname.includes("/auto-signup/oauth/authorize")) {
url = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize");
} else if (window.location.pathname.includes("/signup/oauth/authorize")) {
url = window.location.pathname.replace("/signup/oauth/authorize", "/login/oauth/authorize");
} else if (authConfig.appName === application.name) {
url = "/login";
} else if (application.signinUrl === "") {
@ -1173,7 +1187,7 @@ export function getLoginLink(application) {
} else {
url = application.signinUrl;
}
return url;
return url + window.location.search;
}
export function redirectToLoginPage(application, history) {
@ -1216,7 +1230,7 @@ export function renderSignupLink(application, text) {
if (application === null) {
url = null;
} else if (window.location.pathname.includes("/login/oauth/authorize")) {
url = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize");
url = window.location.pathname.replace("/login/oauth/authorize", "/signup/oauth/authorize");
} else if (authConfig.appName === application.name) {
url = "/signup";
} else {
@ -1228,10 +1242,10 @@ export function renderSignupLink(application, text) {
}
const storeSigninUrl = () => {
sessionStorage.setItem("signinUrl", window.location.href);
sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
};
return renderLink(url, text, storeSigninUrl);
return renderLink(url + window.location.search, text, storeSigninUrl);
}
export function renderForgetLink(application, text) {
@ -1249,7 +1263,7 @@ export function renderForgetLink(application, text) {
}
const storeSigninUrl = () => {
sessionStorage.setItem("signinUrl", window.location.href);
sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
};
return renderLink(url, text, storeSigninUrl);

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import {Button, Card, Col, Input, InputNumber, Radio, Row, Select, Switch} from "antd";
import {LinkOutlined} from "@ant-design/icons";
import * as SyncerBackend from "./backend/SyncerBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
@ -23,6 +23,7 @@ import SyncerTableColumnTable from "./table/SyncerTableColumnTable";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
import * as CertBackend from "./backend/CertBackend";
require("codemirror/theme/material-darker.css");
require("codemirror/mode/javascript/javascript");
@ -32,11 +33,13 @@ class SyncerEditPage extends React.Component {
constructor(props) {
super(props);
this.state = {
certs: [],
classes: props,
syncerName: props.match.params.syncerName,
syncer: null,
organizations: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
testDbLoading: false,
};
}
@ -64,12 +67,24 @@ class SyncerEditPage extends React.Component {
});
}
getCerts(owner) {
CertBackend.getCerts(owner)
.then((res) => {
this.setState({
certs: res.data || [],
});
});
}
getOrganizations() {
OrganizationBackend.getOrganizations("admin")
.then((res) => {
this.setState({
organizations: res.data || [],
});
if (res.data) {
this.getCerts(`${res.data.owner}/${res.data.name}`);
}
});
}
@ -228,7 +243,7 @@ class SyncerEditPage extends React.Component {
});
})}>
{
["Database", "LDAP", "Keycloak"]
["Database", "Keycloak"]
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
@ -317,7 +332,7 @@ class SyncerEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.syncer.password} onChange={e => {
<Input.Password value={this.state.syncer.password} onChange={e => {
this.updateSyncerField("password", e.target.value);
}} />
</Col>
@ -332,6 +347,88 @@ class SyncerEditPage extends React.Component {
}} />
</Col>
</Row>
{
this.state.syncer.databaseType === "mysql" || this.state.syncer.databaseType === "mssql" || this.state.syncer.databaseType === "postgres" ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:SSH type"), i18next.t("general:SSH type - Tooltip"))} :
</Col>
<Col span={22} >
<Radio.Group value={this.state.syncer.sshType} buttonStyle="solid" onChange={e => {
this.updateSyncerField("sshType", e.target.value);
}}>
<Radio.Button value="">{i18next.t("general:None")}</Radio.Button>
<Radio.Button value="password">{i18next.t("general:Password")}</Radio.Button>
<Radio.Button value="cert">{i18next.t("general:Cert")}</Radio.Button>
</Radio.Group>
</Col>
</Row>
) : null
}
{
this.state.syncer.sshType && this.state.syncer.databaseType === "mysql" || this.state.syncer.databaseType === "mssql" || this.state.syncer.databaseType === "postgres" ? (
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH host"), i18next.t("provider:Host - Tooltip"))} :
</Col>
<Col span={22} >
<Input prefix={<LinkOutlined />} value={this.state.syncer.sshHost} onChange={e => {
this.updateSyncerField("sshHost", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH port"), i18next.t("provider:Port - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber value={this.state.syncer.sshPort} onChange={value => {
this.updateSyncerField("sshPort", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH user"), i18next.t("general:User - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.syncer.sshUser} onChange={e => {
this.updateSyncerField("sshUser", e.target.value);
}} />
</Col>
</Row>
{
this.state.syncer.sshType === "password" && (this.state.syncer.databaseType === "mysql" || this.state.syncer.databaseType === "mssql" || this.state.syncer.databaseType === "postgres") ?
(
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH password"), i18next.t("general:Password - Tooltip"))} :
</Col>
<Col span={22} >
<Input.Password value={this.state.syncer.sshPassword} onChange={e => {
this.updateSyncerField("ssh " + "sshPassword", e.target.value);
}} />
</Col>
</Row>
) : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:SSH cert"), i18next.t("general:Cert - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.syncer.cert} onChange={(value => {this.updateSyncerField("cert", value);})}>
{
this.state?.certs.map((cert, index) => <Option key={index} value={cert.name}>{cert.name}</Option>)
}
</Select>
</Col>
</Row>
)
}
</React.Fragment>
) : null
}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:Table"), i18next.t("syncer:Table - Tooltip"))} :
@ -343,6 +440,31 @@ class SyncerEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:DB test"), i18next.t("provider:DB test - Tooltip"))} :
</Col>
<Col span={2} >
<Button type={"primary"} loading={this.state.testDbLoading} onClick={() => {
this.setState({testDbLoading: true});
SyncerBackend.testSyncerDb(this.state.syncer)
.then((res) => {
if (res.status === "ok") {
this.setState({testDbLoading: false});
Setting.showMessage("success", i18next.t("syncer:Connect successfully"));
} else {
this.setState({testDbLoading: false});
Setting.showMessage("error", `${i18next.t("syncer:Failed to connect")}: ${res.msg}`);
}
})
.catch(error => {
this.setState({testDbLoading: false});
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
}>{i18next.t("syncer:Test DB Connection")}</Button>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:Table columns"), i18next.t("syncer:Table columns - Tooltip"))} :

View File

@ -0,0 +1,324 @@
// Copyright 2024 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 * as TransactionBackend from "./backend/TransactionBackend";
import * as Setting from "./Setting";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import {Button, Card, Col, Input, Row} from "antd";
import i18next from "i18next";
class TransactionEditPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
transactionName: props.match.params.transactionName,
application: null,
transaction: null,
providers: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
};
}
UNSAFE_componentWillMount() {
this.getTransaction();
}
getTransaction() {
TransactionBackend.getTransaction(this.state.organizationName, this.state.transactionName)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
this.setState({
transaction: res.data,
});
Setting.scrollToDiv("invoice-area");
});
}
submitTransactionEdit(exitAfterSave) {
const transaction = Setting.deepCopy(this.state.transaction);
TransactionBackend.updateTransaction(this.state.transaction.owner, this.state.transactionName, transaction)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully saved"));
this.setState({
transactionName: this.state.transaction.name,
});
if (exitAfterSave) {
this.props.history.push("/transactions");
} else {
this.props.history.push(`/transactions/${this.state.organizationName}/${this.state.transaction.name}`);
}
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
this.updatePaymentField("name", this.state.transactionName);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
deleteTransaction() {
TransactionBackend.deleteTransaction(this.state.transaction)
.then((res) => {
if (res.status === "ok") {
this.props.history.push("/transactions");
} 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}`);
});
}
parseTransactionField(key, value) {
if ([""].includes(key)) {
value = Setting.myParseInt(value);
}
return value;
}
getApplication() {
ApplicationBackend.getApplication("admin", this.state.applicationName)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
if (res.status === "error") {
Setting.showMessage("error", res.msg);
return;
}
const application = res.data;
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
application.grantTypes = ["authorization_code"];
}
if (application.tags === null || application.tags === undefined) {
application.tags = [];
}
this.setState({
application: application,
});
this.getCerts(application.organization);
this.getSamlMetadata(application.enableSamlPostBinding);
});
}
renderTransaction() {
return (
<Card size="small" title={
<div>
{this.state.mode === "add" ? i18next.t("transaction:New Transaction") : i18next.t("transaction:Edit Transaction")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button onClick={() => this.submitTransactionEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitTransactionEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteTransaction()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
<Row style={{marginTop: "10px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.owner} onChange={e => {
// this.updatePaymentField('organization', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.name} onChange={e => {
// this.updatePaymentField('name', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.displayName} onChange={e => {
this.updatePaymentField("displayName", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Provider"), i18next.t("general:Provider - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.provider} onChange={e => {
// this.updatePaymentField('provider', e.target.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 disabled={true} value={this.state.transaction.category} onChange={e => {
this.updatePaymentField("displayName", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("payment:Type - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.type} onChange={e => {
// this.updatePaymentField('type', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("payment:Product"), i18next.t("payment:Product - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.productName} onChange={e => {
// this.updatePaymentField('productName', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Detail"), i18next.t("product:Detail - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.detail} onChange={e => {
// this.updatePaymentField('currency', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Tag"), i18next.t("transaction:Tag - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.tag} onChange={e => {
// this.updatePaymentField('currency', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("payment:Currency"), i18next.t("payment:Currency - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.currency} onChange={e => {
// this.updatePaymentField('currency', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("transaction:Amount"), i18next.t("transaction:Amount - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.amount} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Return URL"), i18next.t("product:Return URL - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.user} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:User"), i18next.t("general:User - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.user} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.application} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Payment"), i18next.t("general:Payment - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.payment} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.state} onChange={e => {
// this.updatePaymentField('state', e.target.value);
}} />
</Col>
</Row>
</Card>
);
}
render() {
return (
<div>
{
this.state.transaction !== null ? this.renderTransaction() : null
}
<div style={{marginTop: "20px", marginLeft: "40px"}}>
<Button size="large" onClick={() => this.submitTransactionEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitTransactionEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteTransaction()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
</div>
);
}
}
export default TransactionEditPage;

View File

@ -0,0 +1,333 @@
// Copyright 2024 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 BaseListPage from "./BaseListPage";
import i18next from "i18next";
import {Link} from "react-router-dom";
import * as Setting from "./Setting";
import * as Provider from "./auth/Provider";
import {Button, Table} from "antd";
import PopconfirmModal from "./common/modal/PopconfirmModal";
import React from "react";
import * as TransactionBackend from "./backend/TransactionBackend";
import moment from "moment/moment";
class TransactionListPage extends BaseListPage {
newTransaction() {
const randomName = Setting.getRandomName();
const organizationName = Setting.getRequestOrganization(this.props.account);
return {
owner: organizationName,
name: `transaction_${randomName}`,
createdTime: moment().format(),
displayName: `New Transaction - ${randomName}`,
provider: "provider_pay_paypal",
category: "",
type: "PayPal",
productName: "computer-1",
productDisplayName: "A notebook computer",
detail: "This is a computer with excellent CPU, memory and disk",
tag: "Promotion-1",
currency: "USD",
amount: 0,
returnUrl: "https://door.casdoor.com/transactions",
user: "admin",
application: "",
payment: "payment_bhn1ra",
state: "Paid",
};
}
deleteTransaction(i) {
TransactionBackend.deleteTransaction(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}`);
});
}
addTransaction() {
const newTransaction = this.newTransaction();
TransactionBackend.addTransaction(newTransaction)
.then((res) => {
if (res.status === "ok") {
this.props.history.push({pathname: `/transactions/${newTransaction.owner}/${newTransaction.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}`);
});
}
renderTable(transactions) {
const columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "180px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/transactions/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "120px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Provider"),
dataIndex: "provider",
key: "provider",
width: "150px",
sorter: true,
...this.getColumnSearchProps("provider"),
render: (text, record, index) => {
return (
<Link to={`/providers/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:User"),
dataIndex: "user",
key: "user",
width: "120px",
sorter: true,
...this.getColumnSearchProps("user"),
render: (text, record, index) => {
return (
<Link to={`/users/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Created time"),
dataIndex: "createdTime",
key: "createdTime",
width: "160px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("provider:Type"),
dataIndex: "type",
key: "type",
width: "140px",
align: "center",
filterMultiple: false,
filters: Setting.getProviderTypeOptions("Payment").map((o) => {return {text: o.id, value: o.name};}),
sorter: true,
render: (text, record, index) => {
record.category = "Payment";
return Provider.getProviderLogoWidget(record);
},
},
{
title: i18next.t("payment:Product"),
dataIndex: "productDisplayName",
key: "productDisplayName",
// width: '160px',
sorter: true,
...this.getColumnSearchProps("productDisplayName"),
render: (text, record, index) => {
return (
<Link to={`/products/${record.owner}/${record.productName}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("payment:Currency"),
dataIndex: "currency",
key: "currency",
width: "120px",
sorter: true,
...this.getColumnSearchProps("currency"),
},
{
title: i18next.t("transaction:Amount"),
dataIndex: "amount",
key: "amount",
width: "120px",
sorter: true,
...this.getColumnSearchProps("amount"),
},
{
title: i18next.t("general:User"),
dataIndex: "user",
key: "user",
width: "120px",
sorter: true,
...this.getColumnSearchProps("user"),
},
{
title: i18next.t("general:Application"),
dataIndex: "application",
key: "application",
width: "120px",
sorter: true,
...this.getColumnSearchProps("application"),
render: (text, record, index) => {
return (
<Link to={`/applications/${record.owner}/${record.application}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Payment"),
dataIndex: "payment",
key: "payment",
width: "120px",
sorter: true,
...this.getColumnSearchProps("payment"),
render: (text, record, index) => {
return (
<Link to={`/payments/${record.owner}/${record.payment}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:State"),
dataIndex: "state",
key: "state",
width: "120px",
sorter: true,
...this.getColumnSearchProps("state"),
},
{
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "240px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/transactions/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteTransaction(index)}
>
</PopconfirmModal>
</div>
);
},
},
];
const paginationProps = {
total: this.state.pagination.total,
showQuickJumper: true,
showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={transactions} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => (
<div>
{i18next.t("general:Transactions")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" size="small" onClick={this.addTransaction.bind(this)}>{i18next.t("general:Add")}</Button>
</div>
)}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
</div>
);
}
fetch = (params = {}) => {
let field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder;
if (params.type !== undefined && params.type !== null) {
field = "type";
value = params.type;
}
this.setState({loading: true});
TransactionBackend.getTransactions(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => {
this.setState({
loading: false,
});
if (res.status === "ok") {
this.setState({
data: res.data,
pagination: {
...params.pagination,
total: res.data2,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (Setting.isResponseDenied(res)) {
this.setState({
isAuthorized: false,
});
} else {
Setting.showMessage("error", res.msg);
}
}
});
};
}
export default TransactionListPage;

View File

@ -64,7 +64,9 @@ class UserEditPage extends React.Component {
UNSAFE_componentWillMount() {
this.getUser();
this.getOrganizations();
if (Setting.isLocalAdminUser(this.props.account)) {
this.getOrganizations();
}
this.getApplicationsByOrganization(this.state.organizationName);
this.getUserApplication();
this.setReturnUrl();
@ -350,7 +352,9 @@ class UserEditPage extends React.Component {
{Setting.getLabel("ID", i18next.t("general:ID - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.user.id} disabled={disabled} />
<Input value={this.state.user.id} disabled={disabled} onChange={e => {
this.updateUserField("id", e.target.value);
}} />
</Col>
</Row>
);
@ -999,7 +1003,7 @@ class UserEditPage extends React.Component {
<div style={{verticalAlign: "middle", marginBottom: 10}}>{`(${i18next.t("general:empty")})`}</div>
</Col>
}
<CropperDivModal disabled={disabled} tag={tag} setTitle={set} buttonText={`${title}...`} title={title} user={this.state.user} organization={this.state.organizations.find(organization => organization.name === this.state.organizationName)} />
<CropperDivModal disabled={disabled} tag={tag} setTitle={set} buttonText={`${title}...`} title={title} user={this.state.user} organization={this.getUserOrganization()} />
</Col>
);
}

View File

@ -309,6 +309,16 @@ class WebhookEditPage extends React.Component {
</div>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("webhook:Single org only"), i18next.t("webhook:Single org only - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.webhook.singleOrgOnly} onChange={checked => {
this.updateWebhookField("singleOrgOnly", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :

View File

@ -111,7 +111,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("general:Created time"),
dataIndex: "createdTime",
key: "createdTime",
width: "180px",
width: "150px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
@ -121,7 +121,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("general:URL"),
dataIndex: "url",
key: "url",
width: "300px",
width: "200px",
sorter: true,
...this.getColumnSearchProps("url"),
render: (text, record, index) => {
@ -138,7 +138,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("general:Method"),
dataIndex: "method",
key: "method",
width: "120px",
width: "100px",
sorter: true,
...this.getColumnSearchProps("method"),
},
@ -146,7 +146,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("webhook:Content type"),
dataIndex: "contentType",
key: "contentType",
width: "200px",
width: "140px",
sorter: true,
filterMultiple: false,
filters: [
@ -169,7 +169,19 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("webhook:Is user extended"),
dataIndex: "isUserExtended",
key: "isUserExtended",
width: "160px",
width: "140px",
sorter: true,
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
);
},
},
{
title: i18next.t("webhook:Single org only"),
dataIndex: "singleOrgOnly",
key: "singleOrgOnly",
width: "140px",
sorter: true,
render: (text, record, index) => {
return (
@ -183,6 +195,7 @@ class WebhookListPage extends BaseListPage {
key: "isEnabled",
width: "120px",
sorter: true,
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />

View File

@ -172,7 +172,7 @@ class AuthCallback extends React.Component {
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "link") {
const from = innerParams.get("from");
Setting.goToLinkSoft(this, from);
Setting.goToLinkSoftOrJumpSelf(this, from);
} else if (responseType === "saml") {
if (res.data2.method === "POST") {
this.setState({

View File

@ -156,7 +156,7 @@ class ForgetPage extends React.Component {
if (res.status === "ok") {
const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage);
Setting.goToLinkSoft(linkInStorage);
} else {
Setting.redirectToLoginPage(this.getApplicationObj(), this.props.history);
}

View File

@ -17,7 +17,6 @@ import i18next from "i18next";
import * as Provider from "./Provider";
import {getProviderLogoURL} from "../Setting";
import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons";
import {authViaMetaMask, authViaWeb3Onboard} from "./Web3Auth";
import QqLoginButton from "./QqLoginButton";
import FacebookLoginButton from "./FacebookLoginButton";
import WeiboLoginButton from "./WeiboLoginButton";
@ -43,9 +42,7 @@ import OktaLoginButton from "./OktaLoginButton";
import DouyinLoginButton from "./DouyinLoginButton";
import LoginButton from "./LoginButton";
import * as AuthBackend from "./AuthBackend";
import * as Setting from "../Setting";
import {getEvent} from "./Util";
import {Modal} from "antd";
import {WechatOfficialAccountModal} from "./Util";
function getSigninButton(provider) {
const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName !== "" ? provider.displayName : provider.type);
@ -124,9 +121,17 @@ function goToSamlUrl(provider, location) {
export function goToWeb3Url(application, provider, method) {
if (provider.type === "MetaMask") {
authViaMetaMask(application, provider, method);
import("./Web3Auth")
.then(module => {
const authViaMetaMask = module.authViaMetaMask;
authViaMetaMask(application, provider, method);
});
} else if (provider.type === "Web3Onboard") {
authViaWeb3Onboard(application, provider, method);
import("./Web3Auth")
.then(module => {
const authViaWeb3Onboard = module.authViaWeb3Onboard;
authViaWeb3Onboard(application, provider, method);
});
}
}
@ -134,32 +139,11 @@ export function renderProviderLogo(provider, application, width, margin, size, l
if (size === "small") {
if (provider.category === "OAuth") {
if (provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger")) {
const info = async() => {
AuthBackend.getWechatQRCode(`${provider.owner}/${provider.name}`).then(
async res => {
if (res.status !== "ok") {
Setting.showMessage("error", res?.msg);
return;
}
const t1 = setInterval(await getEvent, 1000, application, provider, res.data2);
{Modal.info({
title: i18next.t("provider:Please use WeChat to scan the QR code and follow the official account for sign in"),
content: (
<div style={{marginRight: "34px"}}>
<img src = {"data:image/png;base64," + res.data} alt="Wechat QR code" style={{width: "100%"}} />
</div>
),
onOk() {
window.clearInterval(t1);
},
});}
}
);
};
return (
<a key={provider.displayName} >
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={{margin: margin}} onClick={info} />
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={{margin: margin}} onClick={() => {
WechatOfficialAccountModal(application, provider, "signup");
}} />
</a>
);
} else {

View File

@ -92,7 +92,7 @@ class ResultPage extends React.Component {
<Button type="primary" key="login" onClick={() => {
const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage);
Setting.goToLinkSoft(this, linkInStorage);
} else {
Setting.redirectToLoginPage(application, this.props.history);
}

View File

@ -17,6 +17,10 @@ import LoginPage from "./LoginPage";
import {authConfig} from "./Auth";
class SelfLoginPage extends React.Component {
constructor(props) {
super(props);
import("../ManagementPage");
}
render() {
return (
<LoginPage type={"login"} mode={"signin"} applicationName={authConfig.appName} {...this.props} />

View File

@ -87,8 +87,8 @@ class SignupPage extends React.Component {
componentDidMount() {
const oAuthParams = Util.getOAuthGetParameters();
if (oAuthParams !== null) {
const signinUrl = window.location.href.replace("/signup/oauth/authorize", "/login/oauth/authorize");
sessionStorage.setItem("signinUrl", signinUrl);
const signinUrl = window.location.pathname.replace("/signup/oauth/authorize", "/login/oauth/authorize");
sessionStorage.setItem("signinUrl", signinUrl + window.location.search);
}
if (this.getApplicationObj() === undefined) {
@ -223,7 +223,7 @@ class SignupPage extends React.Component {
Setting.goToLinkSoft(this, this.getResultPath(application, values));
}
} else {
Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
Setting.showMessage("error", res.msg);
}
});
}
@ -639,7 +639,7 @@ class SignupPage extends React.Component {
<a onClick={() => {
const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage);
Setting.goToLinkSoft(this, linkInStorage);
} else {
Setting.redirectToLoginPage(application, this.props.history);
}

View File

@ -13,11 +13,12 @@
// limitations under the License.
import React from "react";
import {Alert, Button, Result} from "antd";
import {Alert, Button, Modal, Result} from "antd";
import i18next from "i18next";
import {getWechatMessageEvent} from "./AuthBackend";
import * as Setting from "../Setting";
import * as Provider from "./Provider";
import * as AuthBackend from "./AuthBackend";
export function renderMessage(msg) {
if (msg !== null) {
@ -188,12 +189,36 @@ export function getQueryParamsFromState(state) {
}
}
export function getEvent(application, provider, ticket) {
export function getEvent(application, provider, ticket, method) {
getWechatMessageEvent(ticket)
.then(res => {
if (res.data === "SCAN" || res.data === "subscribe") {
const code = res?.data2;
Setting.goToLink(Provider.getAuthUrl(application, provider, "signup", code));
Setting.goToLink(Provider.getAuthUrl(application, provider, method ?? "signup", code));
}
});
}
export async function WechatOfficialAccountModal(application, provider, method) {
AuthBackend.getWechatQRCode(`${provider.owner}/${provider.name}`).then(
async res => {
if (res.status !== "ok") {
Setting.showMessage("error", res?.msg);
return;
}
const t1 = setInterval(await getEvent, 1000, application, provider, res.data2, method);
{Modal.info({
title: i18next.t("provider:Please use WeChat to scan the QR code and follow the official account for sign in"),
content: (
<div style={{marginRight: "34px"}}>
<img src = {"data:image/png;base64," + res.data} alt="Wechat QR code" style={{width: "100%"}} />
</div>
),
onOk() {
window.clearInterval(t1);
},
});}
}
);
}

View File

@ -0,0 +1,25 @@
// Copyright 2021 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 * as Setting from "../Setting";
export function getRecords(organizationName, page, pageSize, field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-records?organizationName=${organizationName}&pageSize=${pageSize}&p=${page}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -58,6 +58,18 @@ export function addSyncer(syncer) {
}).then(res => res.json());
}
export function testSyncerDb(syncer) {
const newSyncer = Setting.deepCopy(syncer);
return fetch(`${Setting.ServerUrl}/api/test-syncer-db`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newSyncer),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function deleteSyncer(syncer) {
const newSyncer = Setting.deepCopy(syncer);
return fetch(`${Setting.ServerUrl}/api/delete-syncer`, {

View File

@ -0,0 +1,71 @@
// Copyright 2024 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 * as Setting from "../Setting";
export function getTransactions(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-transactions?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function getTransaction(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-transaction?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function updateTransaction(owner, name, transaction) {
const newTransaction = Setting.deepCopy(transaction);
return fetch(`${Setting.ServerUrl}/api/update-transaction?id=${owner}/${encodeURIComponent(name)}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newTransaction),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function addTransaction(transaction) {
const newTransaction = Setting.deepCopy(transaction);
return fetch(`${Setting.ServerUrl}/api/add-transaction`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newTransaction),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function deleteTransaction(transaction) {
const newTransaction = Setting.deepCopy(transaction);
return fetch(`${Setting.ServerUrl}/api/delete-transaction`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newTransaction),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -147,7 +147,7 @@ export function sendCode(captchaType, captchaToken, clientSecret, method, countr
Setting.showMessage("success", i18next.t("user:Verification code sent"));
return true;
} else {
Setting.showMessage("error", i18next.t("user:" + res.msg));
Setting.showMessage("error", res.msg);
return false;
}
});

View File

@ -0,0 +1,50 @@
// Copyright 2024 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 {useEffect} from "react";
let customHeadLoaded = false;
function CustomHead(props) {
useEffect(() => {
if (!customHeadLoaded) {
const suffix = new Date().getTime().toString();
if (!props.headerHtml) {return;}
const node = document.createElement("div");
node.innerHTML = props.headerHtml;
node.childNodes.forEach(el => {
if (el.nodeName === "#text") {
return;
}
let innerNode = el;
innerNode.setAttribute("app-custom-head" + suffix, "");
if (innerNode.localName === "script") {
const scriptNode = document.createElement("script");
Array.from(innerNode.attributes).forEach(attr => {
scriptNode.setAttribute(attr.name, attr.value);
});
scriptNode.text = innerNode.textContent;
innerNode = scriptNode;
}
document.head.appendChild(innerNode);
});
customHeadLoaded = true;
}
});
}
export default CustomHead;

View File

@ -20,8 +20,8 @@ import * as Setting from "../Setting";
import * as Provider from "../auth/Provider";
import * as AuthBackend from "../auth/AuthBackend";
import {goToWeb3Url} from "../auth/ProviderButton";
import {delWeb3AuthToken} from "../auth/Web3Auth";
import AccountAvatar from "../account/AccountAvatar";
import {WechatOfficialAccountModal} from "../auth/Util";
class OAuthWidget extends React.Component {
constructor(props) {
@ -98,7 +98,22 @@ class OAuthWidget extends React.Component {
user: this.props.user,
};
if (providerType === "MetaMask" || providerType === "Web3Onboard") {
delWeb3AuthToken(linkedValue);
import("../auth/Web3Auth")
.then(module => {
const delWeb3AuthToken = module.delWeb3AuthToken;
delWeb3AuthToken(linkedValue);
AuthBackend.unlink(body)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", "Unlinked successfully");
this.unlinked();
} else {
Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
}
});
});
return;
}
AuthBackend.unlink(body)
.then((res) => {
@ -183,9 +198,19 @@ class OAuthWidget extends React.Component {
provider.category === "Web3" ? (
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={() => goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}</Button>
) : (
<a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}>
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button>
</a>
provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger") ? (
<a key={provider.displayName}>
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={
() => {
WechatOfficialAccountModal(application, provider, "link");
}
}>{i18next.t("user:Link")}</Button>
</a>
) : (
<a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}>
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button>
</a>
)
)
) : (
<Button disabled={!providerItem.canUnlink && !Setting.isAdminUser(account)} style={{marginLeft: "20px", width: linkButtonWidth}} onClick={() => this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}</Button>

View File

@ -36,7 +36,7 @@ class OpenTour extends React.Component {
this.canTour() ?
<Tooltip title="Click to open tour">
<div className="select-box" style={{display: Setting.isMobile() ? "none" : null, ...this.props.style}} onClick={() => TourConfig.setIsTourVisible(true)} >
<QuestionCircleOutlined style={{fontSize: "24px", color: "#4d4d4d"}} />
<QuestionCircleOutlined style={{fontSize: "24px"}} />
</div>
</Tooltip>
:

View File

@ -56,7 +56,7 @@ class LanguageSelect extends React.Component {
return (
<Dropdown menu={{items: languageItems, onClick}} >
<div className="select-box" style={{display: languageItems.length === 0 ? "none" : null, ...this.props.style}} >
<GlobalOutlined style={{fontSize: "24px", color: "#4d4d4d"}} />
<GlobalOutlined style={{fontSize: "24px"}} />
</div>
</Dropdown>
);

View File

@ -21,9 +21,9 @@ import {CheckOutlined} from "@ant-design/icons";
import {CompactTheme, DarkTheme, Light} from "antd-token-previewer/es/icons";
export const Themes = [
{label: "Default", key: "default", icon: <Light style={{fontSize: "24px", color: "#4d4d4d"}} />}, // i18next.t("theme:Default")
{label: "Dark", key: "dark", icon: <DarkTheme style={{fontSize: "24px", color: "#4d4d4d"}} />}, // i18next.t("theme:Dark")
{label: "Compact", key: "compact", icon: <CompactTheme style={{fontSize: "24px", color: "#4d4d4d"}} />}, // i18next.t("theme:Compact")
{label: "Default", key: "default", icon: <Light style={{fontSize: "24px"}} />}, // i18next.t("theme:Default")
{label: "Dark", key: "dark", icon: <DarkTheme style={{fontSize: "24px"}} />}, // i18next.t("theme:Dark")
{label: "Compact", key: "compact", icon: <CompactTheme style={{fontSize: "24px"}} />}, // i18next.t("theme:Compact")
];
function getIcon(themeKey) {

View File

@ -13,57 +13,34 @@
// limitations under the License.
import i18n from "i18next";
import en from "./locales/en/data.json";
import zh from "./locales/zh/data.json";
import es from "./locales/es/data.json";
import fr from "./locales/fr/data.json";
import de from "./locales/de/data.json";
import id from "./locales/id/data.json";
import ja from "./locales/ja/data.json";
import ko from "./locales/ko/data.json";
import ru from "./locales/ru/data.json";
import vi from "./locales/vi/data.json";
import pt from "./locales/pt/data.json";
import it from "./locales/it/data.json";
import ms from "./locales/ms/data.json";
import tr from "./locales/tr/data.json";
import ar from "./locales/ar/data.json";
import he from "./locales/he/data.json";
import nl from "./locales/nl/data.json";
import pl from "./locales/pl/data.json";
import fi from "./locales/fi/data.json";
import sv from "./locales/sv/data.json";
import uk from "./locales/uk/data.json";
import kk from "./locales/kk/data.json";
import fa from "./locales/fa/data.json";
import * as Conf from "./Conf";
import {initReactI18next} from "react-i18next";
import en from "./locales/en/data.json";
const resources = {
en: en,
zh: zh,
es: es,
fr: fr,
de: de,
id: id,
ja: ja,
ko: ko,
ru: ru,
vi: vi,
pt: pt,
it: it,
ms: ms,
tr: tr,
ar: ar,
he: he,
nl: nl,
pl: pl,
fi: fi,
sv: sv,
uk: uk,
kk: kk,
fa: fa,
};
const resourcesToBackend = (res) => ({
type: "backend",
init(services, backendOptions, i18nextOptions) {/* use services and options */},
read(language, namespace, callback) {
if (typeof res === "function") {
if (res.length < 3) {
try {
const r = res(language, namespace);
if (r && typeof r.then === "function") {
r.then((data) => callback(null, (data && data.default) || data)).catch(callback);
} else {
callback(null, r);
}
} catch (err) {
callback(err);
}
return;
}
res(language, namespace, callback);
return;
}
callback(null, res && res[language] && res[language][namespace]);
},
});
function initLanguage() {
let language = localStorage.getItem("language");
@ -157,18 +134,23 @@ function initLanguage() {
return language;
}
i18n.use(initReactI18next).init({
lng: initLanguage(),
i18n.use(resourcesToBackend(async(language, namespace) => {
const res = await import(`./locales/${language}/data.json`);
return res.default[namespace];
}
))
.use(initReactI18next)
.init({
lng: initLanguage(),
ns: Object.keys(en),
fallbackLng: "en",
resources: resources,
keySeparator: false,
interpolation: {
escapeValue: true,
},
// debug: true,
saveMissing: true,
});
keySeparator: false,
interpolation: {
escapeValue: true,
},
// debug: true,
saveMissing: true,
});
export default i18n;

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "URL der Prompt-Seite kopieren",
"Copy signin page URL": "Kopieren Sie die URL der Anmeldeseite",
"Copy signup page URL": "URL der Anmeldeseite kopieren",
"Custom CSS": "Formular CSS",
"Custom CSS - Edit": "Custom CSS - Bearbeiten",
"Custom CSS - Tooltip": "CSS-Styling der Anmelde-, Registrierungs- und Passwort-vergessen-Seite (z. B. Hinzufügen von Rahmen und Schatten)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Bearbeitungsanwendung",
"Enable Email linking": "E-Mail-Verknüpfung aktivieren",
@ -52,16 +58,13 @@
"File uploaded successfully": "Datei erfolgreich hochgeladen",
"First, last": "First, last",
"Follow organization theme": "Folge dem Theme der Organisation",
"Form CSS": "Formular CSS",
"Form CSS - Edit": "Form CSS - Bearbeiten",
"Form CSS - Tooltip": "CSS-Styling der Anmelde-, Registrierungs- und Passwort-vergessen-Seite (z. B. Hinzufügen von Rahmen und Schatten)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Formposition",
"Form position - Tooltip": "Position der Anmelde-, Registrierungs- und Passwort-vergessen-Formulare",
"Grant types": "Grant-Typen",
"Grant types - Tooltip": "Wählen Sie aus, welche Grant-Typen im OAuth-Protokoll zulässig sind",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "Das Public-Key-Zertifikat, das vom Client-SDK, das mit dieser Anwendung korrespondiert, verifiziert werden muss",
"Certs": "Zertifikate",
"Click to Upload": "Klicken Sie zum Hochladen",
"Client IP": "Client IP",
"Close": "Schließen",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Provider, die konfiguriert werden müssen, einschließlich Drittanbieter-Logins, Objektspeicherung, Verifizierungscode usw.",
"Real name": "Echter Name",
"Records": "Datensätze",
"Request URI": "Request URI",
"Resources": "Ressourcen",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Speichern",
"Save & Exit": "Speichern und verlassen",
"Session ID": "Session-ID",
@ -357,7 +365,9 @@
"System Info": "Systeminformationen",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "Dies ist eine schreibgeschützte Demo-Seite!",
"Timestamp": "Timestamp",
"Tokens": "Token",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Gemeinsam)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Kopiere den Link",
"File name": "Dateiname",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Neuer Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Synchronisierungsintervall",
@ -1015,6 +1032,13 @@
"Token type": "Token-Typ",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Drittanbieter-Logins",
"3rd-party logins - Tooltip": "Drittanbieter-Anmeldungen, die mit dem Benutzer verknüpft sind",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Sollten die erweiterten Felder des Benutzers in das JSON inkludiert werden?",
"Method - Tooltip": "HTTP Methode",
"New Webhook": "Neue Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Wert"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "The SSL mode used when connecting to the database",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copiar URL de la página del prompt",
"Copy signin page URL": "Copiar la URL de la página de inicio de sesión",
"Copy signup page URL": "Copiar URL de la página de registro",
"Custom CSS": "Formulario CSS",
"Custom CSS - Edit": "Formulario CSS - Editar",
"Custom CSS - Tooltip": "Estilo CSS de los formularios de registro, inicio de sesión y olvido de contraseña (por ejemplo, agregar bordes y sombras)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Editar solicitud",
"Enable Email linking": "Habilitar enlace de correo electrónico",
@ -52,16 +58,13 @@
"File uploaded successfully": "Archivo subido exitosamente",
"First, last": "First, last",
"Follow organization theme": "Seguir el tema de la organización",
"Form CSS": "Formulario CSS",
"Form CSS - Edit": "Formulario CSS - Editar",
"Form CSS - Tooltip": "Estilo CSS de los formularios de registro, inicio de sesión y olvido de contraseña (por ejemplo, agregar bordes y sombras)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Posición de la Forma",
"Form position - Tooltip": "Ubicación de los formularios de registro, inicio de sesión y olvido de contraseña",
"Grant types": "Tipos de subvenciones",
"Grant types - Tooltip": "Selecciona cuáles tipos de subvenciones están permitidas en el protocolo OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "El certificado de clave pública que necesita ser verificado por el SDK del cliente correspondiente a esta aplicación",
"Certs": "Certificaciones",
"Click to Upload": "Haz clic para cargar",
"Client IP": "Client IP",
"Close": "Cerca",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Proveedores a configurar, incluyendo inicio de sesión de terceros, almacenamiento de objetos, código de verificación, etc.",
"Real name": "Nombre real",
"Records": "Registros",
"Request URI": "Request URI",
"Resources": "Recursos",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Guardar",
"Save & Exit": "Guardar y salir",
"Session ID": "ID de sesión",
@ -357,7 +365,9 @@
"System Info": "Información del Sistema",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "¡Este es un sitio de demostración solo de lectura!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "Dirección URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "administrador (compartido)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copiar enlace",
"File name": "Nombre del archivo",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Nuevo Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Intervalo de sincronización",
@ -1015,6 +1032,13 @@
"Token type": "Tipo de token",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Inicio de sesión de terceros",
"3rd-party logins - Tooltip": "Accesos sociales ligados por el usuario",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "¿Incluir los campos extendidos del usuario en el JSON?",
"Method - Tooltip": "Método HTTP",
"New Webhook": "Nuevo Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Valor"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copier l'URL de la page de l'invite",
"Copy signin page URL": "Copier l'URL de la page de connexion",
"Copy signup page URL": "Copiez l'URL de la page d'inscription",
"Custom CSS": "CSS du formulaire",
"Custom CSS - Edit": "CSS du formulaire - Modifier",
"Custom CSS - Tooltip": "Mise en forme CSS des formulaires d'inscription, de connexion et de récupération de mot de passe (par exemple, en ajoutant des bordures et des ombres)",
"Custom CSS Mobile": "CSS du formulaire sur téléphone",
"Custom CSS Mobile - Edit": "CSS du formulaire sur téléphone - Modifier",
"Custom CSS Mobile - Tooltip": "CSS du formulaire sur téléphone - Info-bulle",
"Dynamic": "Dynamique",
"Edit Application": "Modifier l'application",
"Enable Email linking": "Autoriser à lier l'e-mail",
@ -52,16 +58,13 @@
"File uploaded successfully": "Fichier téléchargé avec succès",
"First, last": "Prénom, nom",
"Follow organization theme": "Suivre le thème de l'organisation",
"Form CSS": "CSS du formulaire",
"Form CSS - Edit": "CSS du formulaire - Modifier",
"Form CSS - Tooltip": "Mise en forme CSS des formulaires d'inscription, de connexion et de récupération de mot de passe (par exemple, en ajoutant des bordures et des ombres)",
"Form CSS Mobile": "CSS du formulaire sur téléphone",
"Form CSS Mobile - Edit": "CSS du formulaire sur téléphone - Modifier",
"Form CSS Mobile - Tooltip": "CSS du formulaire sur téléphone - Info-bulle",
"Form position": "Position du formulaire",
"Form position - Tooltip": "Emplacement des formulaires d'inscription, de connexion et de récupération de mot de passe",
"Grant types": "Types d'autorisation",
"Grant types - Tooltip": "Sélectionnez les types d'autorisations autorisés dans le protocole OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incrémentale",
"Input": "Saisie",
"Invitation code": "Code d'invitation",
@ -196,6 +199,7 @@
"Cert - Tooltip": "La clé publique du certificat qui doit être vérifiée par le kit de développement client correspondant à cette application",
"Certs": "Certificats",
"Click to Upload": "Cliquer pour télécharger",
"Client IP": "Client IP",
"Close": "Fermer",
"Confirm": "Confirmer",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Les fournisseurs à configurer, tels que la connexion via un service tiers, le stockage d'objets, le code de vérification, etc.",
"Real name": "Nom complet",
"Records": "Enregistrements",
"Request URI": "Request URI",
"Resources": "Ressources",
"Role": "Rôle",
"Role - Tooltip": "Rôle - Infobulle",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Enregistrer",
"Save & Exit": "Enregistrer et quitter",
"Session ID": "Identifiant de session",
@ -357,7 +365,9 @@
"System Info": "Informations système",
"There was a problem signing you in..": "Un problème est survenu lors de votre connexion..",
"This is a read-only demo site!": "Ceci est un site de démonstration en lecture seule !",
"Timestamp": "Timestamp",
"Tokens": "Jetons",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Infobulle",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Portefeuille - Infobulle",
"admin (Shared)": "admin (Partagé)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copier le lien",
"File name": "Nom de fichier",
@ -958,6 +971,10 @@
"Is read-only": "Est en lecture seule",
"Is read-only - Tooltip": "En lecture seule - Infobulle",
"New Syncer": "Nouveau synchroniseur",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Intervalle de synchronisation",
@ -1015,6 +1032,13 @@
"Token type": "Type de jeton",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Services de connexions tiers",
"3rd-party logins - Tooltip": "Service de connexions tiers liés au compte",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Inclure les champs étendus du compte dans l'objet JSON",
"Method - Tooltip": "Méthode HTTP",
"New Webhook": "Nouveau webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Valeur"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Salin URL halaman prompt",
"Copy signin page URL": "Salin URL halaman masuk",
"Copy signup page URL": "Salin URL halaman pendaftaran",
"Custom CSS": "Formulir CSS",
"Custom CSS - Edit": "Formulir CSS - Edit",
"Custom CSS - Tooltip": "Pengaturan CSS dari formulir pendaftaran, masuk, dan lupa kata sandi (misalnya menambahkan batas dan bayangan)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Mengedit aplikasi",
"Enable Email linking": "Aktifkan pengaitan email",
@ -52,16 +58,13 @@
"File uploaded successfully": "Berkas telah diunggah dengan sukses",
"First, last": "First, last",
"Follow organization theme": "Ikuti tema organisasi",
"Form CSS": "Formulir CSS",
"Form CSS - Edit": "Formulir CSS - Edit",
"Form CSS - Tooltip": "Pengaturan CSS dari formulir pendaftaran, masuk, dan lupa kata sandi (misalnya menambahkan batas dan bayangan)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Posisi formulir",
"Form position - Tooltip": "Tempat pendaftaran, masuk, dan lupa kata sandi",
"Grant types": "Jenis-jenis hibah",
"Grant types - Tooltip": "Pilih jenis hibah apa yang diperbolehkan dalam protokol OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "Sertifikat kunci publik yang perlu diverifikasi oleh SDK klien yang sesuai dengan aplikasi ini",
"Certs": "Sertifikat",
"Click to Upload": "Klik untuk Mengunggah",
"Client IP": "Client IP",
"Close": "Tutup",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Penyedia harus dikonfigurasi, termasuk login pihak ketiga, penyimpanan objek, kode verifikasi, dan lain-lain.",
"Real name": "Nama asli",
"Records": "Catatan",
"Request URI": "Request URI",
"Resources": "Sumber daya",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Menyimpan",
"Save & Exit": "Simpan & Keluar",
"Session ID": "ID sesi",
@ -357,7 +365,9 @@
"System Info": "Informasi Sistem",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "Ini adalah situs demo hanya untuk dibaca saja!",
"Timestamp": "Timestamp",
"Tokens": "Token-token",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "Admin (Berbagi)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Salin Tautan",
"File name": "Nama file",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Sinkronisasi Baru",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Interval sinkronisasi",
@ -1015,6 +1032,13 @@
"Token type": "Jenis token",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Masuk pihak ketiga",
"3rd-party logins - Tooltip": "Masuk sosial yang terhubung oleh pengguna",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Apakah akan menyertakan bidang-bidang tambahan pengguna dalam JSON?",
"Method - Tooltip": "Metode HTTP",
"New Webhook": "Webhook Baru",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Nilai"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "プロンプトページのURLをコピーしてください",
"Copy signin page URL": "サインインページのURLをコピーしてください",
"Copy signup page URL": "サインアップページのURLをコピーしてください",
"Custom CSS": "フォームCSS",
"Custom CSS - Edit": "フォームのCSS - 編集",
"Custom CSS - Tooltip": "サインアップ、サインイン、パスワード忘れのフォームのCSSスタイリング境界線や影の追加",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "アプリケーションを編集する",
"Enable Email linking": "イーメールリンクの有効化",
@ -52,16 +58,13 @@
"File uploaded successfully": "ファイルが正常にアップロードされました",
"First, last": "First, last",
"Follow organization theme": "組織のテーマに従ってください",
"Form CSS": "フォームCSS",
"Form CSS - Edit": "フォームのCSS - 編集",
"Form CSS - Tooltip": "サインアップ、サインイン、パスワード忘れのフォームのCSSスタイリング境界線や影の追加",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "フォームのポジション",
"Form position - Tooltip": "登録、ログイン、パスワード忘れフォームの位置",
"Grant types": "グラント種類",
"Grant types - Tooltip": "OAuthプロトコルで許可されているグラントタイプを選択してください",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "このアプリケーションに対応するクライアントSDKによって検証する必要がある公開鍵証明書",
"Certs": "証明書",
"Click to Upload": "アップロードするにはクリックしてください",
"Client IP": "Client IP",
"Close": "閉じる",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "設定するプロバイダーには、サードパーティのログイン、オブジェクトストレージ、検証コードなどが含まれます。",
"Real name": "本名",
"Records": "記録",
"Request URI": "Request URI",
"Resources": "リソース",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "保存",
"Save & Exit": "保存して終了",
"Session ID": "セッションID",
@ -357,7 +365,9 @@
"System Info": "システム情報",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "これは読み取り専用のデモサイトです!",
"Timestamp": "Timestamp",
"Tokens": "トークン",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "管理者(共有)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "コピー リンク",
"File name": "ファイル名",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "新しいシンクロナイザー",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "同期の間隔",
@ -1015,6 +1032,13 @@
"Token type": "トークンタイプ",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "サードパーティログイン",
"3rd-party logins - Tooltip": "ユーザーによってリンクされたソーシャルログイン",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "ユーザーの拡張フィールドをJSONに含めるかどうか",
"Method - Tooltip": "HTTPメソッド",
"New Webhook": "新しいWebhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "値"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "프롬프트 페이지 URL을 복사하세요",
"Copy signin page URL": "사인인 페이지 URL 복사",
"Copy signup page URL": "가입 페이지 URL을 복사하세요",
"Custom CSS": "CSS 양식",
"Custom CSS - Edit": "폼 CSS - 편집",
"Custom CSS - Tooltip": "가입, 로그인 및 비밀번호를 잊어버린 양식의 CSS 스타일링 (예 : 테두리와 그림자 추가)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "앱 편집하기",
"Enable Email linking": "이메일 링크 사용 가능하도록 설정하기",
@ -52,16 +58,13 @@
"File uploaded successfully": "파일이 성공적으로 업로드되었습니다",
"First, last": "First, last",
"Follow organization theme": "조직의 주제를 따르세요",
"Form CSS": "CSS 양식",
"Form CSS - Edit": "폼 CSS - 편집",
"Form CSS - Tooltip": "가입, 로그인 및 비밀번호를 잊어버린 양식의 CSS 스타일링 (예 : 테두리와 그림자 추가)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "양식 위치",
"Form position - Tooltip": "가입, 로그인 및 비밀번호 재설정 양식의 위치",
"Grant types": "Grant types: 부여 유형",
"Grant types - Tooltip": "OAuth 프로토콜에서 허용되는 그란트 유형을 선택하십시오",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "이 응용 프로그램에 해당하는 클라이언트 SDK에서 확인해야 하는 공개 키 인증서",
"Certs": "증명서",
"Click to Upload": "클릭하여 업로드하세요",
"Client IP": "Client IP",
"Close": "닫다",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "공급 업체는 구성되어야합니다. 3rd-party 로그인, 객체 저장소, 검증 코드 등을 포함합니다.",
"Real name": "실명",
"Records": "기록",
"Request URI": "Request URI",
"Resources": "자원",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "저장하다",
"Save & Exit": "저장하고 종료하기",
"Session ID": "세션 ID",
@ -357,7 +365,9 @@
"System Info": "시스템 정보",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "이것은 읽기 전용 데모 사이트입니다!",
"Timestamp": "Timestamp",
"Tokens": "토큰",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "관리자 (공유)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "링크 복사하기",
"File name": "파일 이름",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "신규 싱크어",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "동기화 간격",
@ -1015,6 +1032,13 @@
"Token type": "토큰 유형",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "제3자 로그인",
"3rd-party logins - Tooltip": "사용자가 연결한 소셜 로그인",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "사용자의 확장 필드를 JSON에 포함할지 여부",
"Method - Tooltip": "HTTP 방법",
"New Webhook": "새로운 웹훅",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "가치"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copiar URL da página de prompt",
"Copy signin page URL": "Copiar URL da página de login",
"Copy signup page URL": "Copiar URL da página de registro",
"Custom CSS": "CSS do formulário",
"Custom CSS - Edit": "Editar CSS do formulário",
"Custom CSS - Tooltip": "Estilização CSS dos formulários de registro, login e recuperação de senha (por exemplo, adicionando bordas e sombras)",
"Custom CSS Mobile": "CSS do formulário em dispositivos móveis",
"Custom CSS Mobile - Edit": "Editar CSS do formulário em dispositivos móveis",
"Custom CSS Mobile - Tooltip": "CSS do formulário em dispositivos móveis - Dica",
"Dynamic": "Dinâmico",
"Edit Application": "Editar Aplicação",
"Enable Email linking": "Ativar vinculação de e-mail",
@ -52,16 +58,13 @@
"File uploaded successfully": "Arquivo enviado com sucesso",
"First, last": "Primeiro, último",
"Follow organization theme": "Seguir tema da organização",
"Form CSS": "CSS do formulário",
"Form CSS - Edit": "Editar CSS do formulário",
"Form CSS - Tooltip": "Estilização CSS dos formulários de registro, login e recuperação de senha (por exemplo, adicionando bordas e sombras)",
"Form CSS Mobile": "CSS do formulário em dispositivos móveis",
"Form CSS Mobile - Edit": "Editar CSS do formulário em dispositivos móveis",
"Form CSS Mobile - Tooltip": "CSS do formulário em dispositivos móveis - Dica",
"Form position": "Posição do formulário",
"Form position - Tooltip": "Localização dos formulários de registro, login e recuperação de senha",
"Grant types": "Tipos de concessão",
"Grant types - Tooltip": "Selecione quais tipos de concessão são permitidos no protocolo OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Código de convite",
@ -196,6 +199,7 @@
"Cert - Tooltip": "O certificado da chave pública que precisa ser verificado pelo SDK do cliente correspondente a esta aplicação",
"Certs": "Certificados",
"Click to Upload": "Clique para Enviar",
"Client IP": "Client IP",
"Close": "Fechar",
"Confirm": "Confirmar",
"Copied to clipboard successfully": "Copiado para a área de transferência com sucesso",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Provedores a serem configurados, incluindo login de terceiros, armazenamento de objetos, código de verificação, etc.",
"Real name": "Nome real",
"Records": "Registros",
"Request URI": "Request URI",
"Resources": "Recursos",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Salvar",
"Save & Exit": "Salvar e Sair",
"Session ID": "ID da sessão",
@ -357,7 +365,9 @@
"System Info": "Informações do Sistema",
"There was a problem signing you in..": "Ocorreu um problema ao fazer o login.",
"This is a read-only demo site!": "Este é um site de demonstração apenas para leitura!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Tipo",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Compartilhado)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copiar Link",
"File name": "Nome do arquivo",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Novo Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Intervalo de sincronização",
@ -1015,6 +1032,13 @@
"Token type": "Tipo de Token",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Logins de terceiros",
"3rd-party logins - Tooltip": "Logins sociais vinculados pelo usuário",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Se incluir os campos estendidos do usuário no JSON",
"Method - Tooltip": "Método HTTP",
"New Webhook": "Novo Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Valor"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Скопируйте URL страницы предложения",
"Copy signin page URL": "Скопируйте URL-адрес страницы входа",
"Copy signup page URL": "Скопируйте URL страницы регистрации",
"Custom CSS": "Форма CSS",
"Custom CSS - Edit": "Форма CSS - Редактирование",
"Custom CSS - Tooltip": "CSS-оформление форм регистрации, входа и восстановления пароля (например, добавление границ и теней)",
"Custom CSS Mobile": "CSS формы для мобильных",
"Custom CSS Mobile - Edit": "CSS формы для мобильный - редактировать",
"Custom CSS Mobile - Tooltip": "Редактирование CSS кода для мобильных устройств",
"Dynamic": "Динамическое",
"Edit Application": "Изменить приложение",
"Enable Email linking": "Включить связывание электронной почты",
@ -52,16 +58,13 @@
"File uploaded successfully": "Файл успешно загружен",
"First, last": "Имя, Фамилия",
"Follow organization theme": "Cледуйте теме организации",
"Form CSS": "Форма CSS",
"Form CSS - Edit": "Форма CSS - Редактирование",
"Form CSS - Tooltip": "CSS-оформление форм регистрации, входа и восстановления пароля (например, добавление границ и теней)",
"Form CSS Mobile": "CSS формы для мобильных",
"Form CSS Mobile - Edit": "CSS формы для мобильный - редактировать",
"Form CSS Mobile - Tooltip": "Редактирование CSS кода для мобильных устройств",
"Form position": "Позиция формы",
"Form position - Tooltip": "Местоположение форм регистрации, входа и восстановления пароля",
"Grant types": "Типы грантов",
"Grant types - Tooltip": "Выберите, какие типы грантов разрешены в протоколе OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Последовательный",
"Input": "Input",
"Invitation code": "Код приглашения",
@ -196,6 +199,7 @@
"Cert - Tooltip": "Сертификат открытого ключа, который требуется проверить клиентским SDK, соответствующим этому приложению",
"Certs": "сертификаты",
"Click to Upload": "Нажмите, чтобы загрузить",
"Client IP": "Client IP",
"Close": "Близко",
"Confirm": "Подтвердить",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Провайдеры должны быть настроены, включая вход с помощью сторонних сервисов, объектное хранилище, код подтверждения и т.д.",
"Real name": "Реальное имя",
"Records": "Записи",
"Request URI": "Request URI",
"Resources": "Ресурсы",
"Role": "Роль",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Сохранить",
"Save & Exit": "Сохранить и выйти",
"Session ID": "Идентификатор сессии",
@ -357,7 +365,9 @@
"System Info": "Системная информация",
"There was a problem signing you in..": "Возникла проблема с регистрацией..",
"This is a read-only demo site!": "Это демонстрационный сайт только для чтения!",
"Timestamp": "Timestamp",
"Tokens": "Токены",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "администратор (общий)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Копировать ссылку",
"File name": "Имя файла",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Новый синхронизатор",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Интервал синхронизации",
@ -1015,6 +1032,13 @@
"Token type": "Тип токена",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Авторизация сторонних участников",
"3rd-party logins - Tooltip": "Социальные логины, связанные пользователем",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Нужно ли включать расширенные поля пользователя в формате JSON?",
"Method - Tooltip": "Метод HTTP",
"New Webhook": "Новый вебхук",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Значение"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Prompt Page URL 'ini kopyala",
"Copy signin page URL": "Giriş sayfası URL 'ini kopyala",
"Copy signup page URL": "Kayıt sayfası URL 'ini kopyala",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dinamik",
"Edit Application": "Uygulamayı düzenle",
"Enable Email linking": "Eposta bağlantısı aktif",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "Adı, Soyadı",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Davet Kodu",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Yüklemek için tıklayın",
"Client IP": "Client IP",
"Close": "Kapat",
"Confirm": "Onayla",
"Copied to clipboard successfully": "Başarıyla kopyalandı",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Gerçek isim",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Kaydet",
"Save & Exit": "Kaydet ve Çık",
"Session ID": "Oturum ID",
@ -357,7 +365,9 @@
"System Info": "Sistem Bilgisi",
"There was a problem signing you in..": "Oturumunuz açılırken bir sorun oluştu..",
"This is a read-only demo site!": "Bu site sadece görüntüleme amaçlıdır!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Custom CSS": "Custom CSS",
"Custom CSS - Edit": "Custom CSS - Edit",
"Custom CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Edit Application",
"Enable Email linking": "Enable Email linking",
@ -52,16 +58,13 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
"Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Incremental",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
@ -357,7 +365,9 @@
"System Info": "System Info",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Copy Link",
"File name": "File name",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Sync interval",
@ -1015,6 +1032,13 @@
"Token type": "Token type",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Social logins linked by the user",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method - Tooltip": "HTTP method",
"New Webhook": "New Webhook",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Value"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "Sao chép URL của trang nhắc nhở",
"Copy signin page URL": "Sao chép URL trang đăng nhập",
"Copy signup page URL": "Sao chép URL trang đăng ký",
"Custom CSS": "Mẫu CSS",
"Custom CSS - Edit": "Biểu mẫu CSS - Sửa",
"Custom CSS - Tooltip": "Phong cách CSS của các biểu mẫu đăng ký, đăng nhập và quên mật khẩu (ví dụ: thêm đường viền và bóng)",
"Custom CSS Mobile": "Custom CSS Mobile",
"Custom CSS Mobile - Edit": "Custom CSS Mobile - Edit",
"Custom CSS Mobile - Tooltip": "Custom CSS Mobile - Tooltip",
"Dynamic": "Dynamic",
"Edit Application": "Sửa ứng dụng",
"Enable Email linking": "Cho phép liên kết Email",
@ -52,16 +58,13 @@
"File uploaded successfully": "Tệp được tải lên thành công",
"First, last": "Tên, Họ",
"Follow organization theme": "Theo giao diện tổ chức",
"Form CSS": "Mẫu CSS",
"Form CSS - Edit": "Biểu mẫu CSS - Sửa",
"Form CSS - Tooltip": "Phong cách CSS của các biểu mẫu đăng ký, đăng nhập và quên mật khẩu (ví dụ: thêm đường viền và bóng)",
"Form CSS Mobile": "Form CSS Mobile",
"Form CSS Mobile - Edit": "Form CSS Mobile - Edit",
"Form CSS Mobile - Tooltip": "Form CSS Mobile - Tooltip",
"Form position": "Vị trí của hình thức",
"Form position - Tooltip": "Vị trí của các biểu mẫu đăng ký, đăng nhập và quên mật khẩu",
"Grant types": "Loại hỗ trợ",
"Grant types - Tooltip": "Chọn loại hỗ trợ được cho phép trong giao thức OAuth",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - Edit",
"Header HTML - Tooltip": "Custom the head tag of your application entry page",
"Incremental": "Tăng",
"Input": "Input",
"Invitation code": "Invitation code",
@ -196,6 +199,7 @@
"Cert - Tooltip": "Chứng chỉ khóa công khai cần được xác minh bởi SDK khách hàng tương ứng với ứng dụng này",
"Certs": "Chứng chỉ",
"Click to Upload": "Nhấp để tải lên",
"Client IP": "Client IP",
"Close": "Đóng lại",
"Confirm": "Confirm",
"Copied to clipboard successfully": "Copied to clipboard successfully",
@ -313,6 +317,7 @@
"Providers - Tooltip": "Các nhà cung cấp phải được cấu hình, bao gồm đăng nhập bên thứ ba, lưu trữ đối tượng, mã xác minh, v.v.",
"Real name": "Tên thật",
"Records": "Hồ sơ",
"Request URI": "Request URI",
"Resources": "Tài nguyên",
"Role": "Role",
"Role - Tooltip": "Role - Tooltip",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "Root cert - Tooltip",
"SAML attributes": "SAML attributes",
"SAML attributes - Tooltip": "SAML attributes - Tooltip",
"SSH cert": "SSH cert",
"SSH type": "SSH type",
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Lưu",
"Save & Exit": "Lưu và Thoát",
"Session ID": "ID phiên làm việc",
@ -357,7 +365,9 @@
"System Info": "Thông tin hệ thống",
"There was a problem signing you in..": "There was a problem signing you in..",
"This is a read-only demo site!": "Đây là trang web giới thiệu chỉ có chức năng đọc!",
"Timestamp": "Timestamp",
"Tokens": "Mã thông báo",
"Transactions": "Transactions",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"URL": "URL",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "quản trị viên (Chung)"
},
"record": {
"Is triggered": "Is triggered"
},
"resource": {
"Copy Link": "Sao chép liên kết",
"File name": "Tên tập tin",
@ -958,6 +971,10 @@
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer: Đồng bộ mới",
"SSH host": "SSH host",
"SSH password": "SSH password",
"SSH port": "SSH port",
"SSH user": "SSH user",
"SSL mode": "SSL mode",
"SSL mode - Tooltip": "SSL mode - Tooltip",
"Sync interval": "Khoảng thời gian đồng bộ hóa",
@ -1015,6 +1032,13 @@
"Token type": "Loại mã thông báo",
"Token type - Tooltip": "Token type - Tooltip"
},
"transaction": {
"Amount": "Amount",
"Amount - Tooltip": "The amount of traded products",
"Edit Transaction": "Edit Transaction",
"New Transaction": "New Transaction",
"Tag - Tooltip": "The tag of the transaction"
},
"user": {
"3rd-party logins": "Đăng nhập bên thứ ba",
"3rd-party logins - Tooltip": "Đăng nhập xã hội liên kết bởi người dùng",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "Có nên bao gồm các trường mở rộng của người dùng trong định dạng JSON không?",
"Method - Tooltip": "Phương thức HTTP",
"New Webhook": "Webhook mới",
"Single org only": "Single org only",
"Single org only - Tooltip": "Triggered only in the organization that the webhook belongs to",
"Value": "Giá trị"
}
}

View File

@ -30,6 +30,12 @@
"Copy prompt page URL": "复制提醒页面URL",
"Copy signin page URL": "复制登录页面URL",
"Copy signup page URL": "复制注册页面URL",
"Custom CSS": "表单CSS",
"Custom CSS - Edit": "编辑表单CSS",
"Custom CSS - Tooltip": "注册、登录、忘记密码等表单的CSS样式如增加边框和阴影",
"Custom CSS Mobile": "表单CSS移动端",
"Custom CSS Mobile - Edit": "编辑表单CSS移动端",
"Custom CSS Mobile - Tooltip": "注册、登录、忘记密码等表单的CSS样式如增加边框和阴影移动端",
"Dynamic": "动态开启",
"Edit Application": "编辑应用",
"Enable Email linking": "自动关联邮箱相同的账号",
@ -52,16 +58,13 @@
"File uploaded successfully": "文件上传成功",
"First, last": "名字, 姓氏",
"Follow organization theme": "使用组织主题",
"Form CSS": "表单CSS",
"Form CSS - Edit": "编辑表单CSS",
"Form CSS - Tooltip": "注册、登录、忘记密码等表单的CSS样式如增加边框和阴影",
"Form CSS Mobile": "表单CSS移动端",
"Form CSS Mobile - Edit": "编辑表单CSS移动端",
"Form CSS Mobile - Tooltip": "注册、登录、忘记密码等表单的CSS样式如增加边框和阴影移动端",
"Form position": "表单位置",
"Form position - Tooltip": "注册、登录、忘记密码等表单的位置",
"Grant types": "OAuth授权类型",
"Grant types - Tooltip": "选择允许哪些OAuth协议中的grant types",
"Header HTML": "Header HTML",
"Header HTML - Edit": "Header HTML - 编辑",
"Header HTML - Tooltip": "自定义应用页面的head标签",
"Incremental": "递增",
"Input": "输入",
"Invitation code": "邀请码",
@ -196,6 +199,7 @@
"Cert - Tooltip": "该应用所对应的客户端SDK需要验证的公钥证书",
"Certs": "证书",
"Click to Upload": "点击上传",
"Client IP": "客户端IP",
"Close": "关闭",
"Confirm": "确认",
"Copied to clipboard successfully": "已成功复制到剪贴板",
@ -313,6 +317,7 @@
"Providers - Tooltip": "需要配置的提供商,包括第三方登录、对象存储、验证码等",
"Real name": "姓名",
"Records": "日志",
"Request URI": "请求URI",
"Resources": "资源",
"Role": "角色",
"Role - Tooltip": "所对应的角色",
@ -322,6 +327,9 @@
"Root cert - Tooltip": "根证书",
"SAML attributes": "SAML属性",
"SAML attributes - Tooltip": "Casdoor作为SAML IdP时所返回的SAML响应的属性",
"SSH cert": "SSH证书",
"SSH type": "SSH类型",
"SSH type - Tooltip": "SSH连接的认证类型",
"Save": "保存",
"Save & Exit": "保存 & 退出",
"Session ID": "会话ID",
@ -357,7 +365,9 @@
"System Info": "系统信息",
"There was a problem signing you in..": "登录时遇到问题..",
"This is a read-only demo site!": "这是一个只读演示站点!",
"Timestamp": "时间",
"Tokens": "令牌",
"Transactions": "交易",
"Type": "类型",
"Type - Tooltip": "类型",
"URL": "链接",
@ -858,6 +868,9 @@
"Wallets - Tooltip": "钱包 - 工具提示",
"admin (Shared)": "admin共享"
},
"record": {
"Is triggered": "是否触发"
},
"resource": {
"Copy Link": "复制链接",
"File name": "文件名",
@ -958,6 +971,10 @@
"Is read-only": "是否只读",
"Is read-only - Tooltip": "只读",
"New Syncer": "添加同步器",
"SSH host": "SSH主机",
"SSH password": "SSH密码",
"SSH port": "SSH端口",
"SSH user": "SSH用户",
"SSL mode": "SSL模式",
"SSL mode - Tooltip": "连接数据库采用哪种SSL模式",
"Sync interval": "同步间隔",
@ -1015,6 +1032,13 @@
"Token type": "令牌类型",
"Token type - Tooltip": "令牌类型"
},
"transaction": {
"Amount": "数量",
"Amount - Tooltip": "交易产品的数量",
"Edit Transaction": "编辑交易",
"New Transaction": "添加交易",
"Tag - Tooltip": "交易的标签"
},
"user": {
"3rd-party logins": "第三方登录",
"3rd-party logins - Tooltip": "用户所绑定的社会化登录",
@ -1126,6 +1150,8 @@
"Is user extended - Tooltip": "是否在JSON里加入用户的扩展字段",
"Method - Tooltip": "HTTP方法",
"New Webhook": "添加Webhook",
"Single org only": "仅本组织",
"Single org only - Tooltip": "仅在Webhook所在组织触发",
"Value": "值"
}
}

View File

@ -76,6 +76,11 @@ class ProviderTable extends React.Component {
this.updateField(table, index, "name", value);
const provider = Setting.getArrayItem(this.props.providers, "name", value);
this.updateField(table, index, "provider", provider);
// If the provider is email or SMS, set the rule to "all" instead of the default "None"
if (provider.category === "Email" || provider.category === "SMS") {
this.updateField(table, index, "rule", "all");
}
}} >
{
Setting.getDeduplicatedArray(this.props.providers, table, "name").map((provider, index) => <Option key={index} value={provider.name}>{provider.name}</Option>)
@ -223,6 +228,26 @@ class ProviderTable extends React.Component {
<Option key="Always" value="Always">{i18next.t("application:Always")}</Option>
</Select>
);
} else if (record.provider?.category === "SMS" || record.provider?.category === "Email") {
if (text === "None") {
text = "all";
}
return (
<Select virtual={false} style={{width: "100%"}}
value={text}
defaultValue="all"
onChange={value => {
this.updateField(table, index, "rule", value);
}}>
<Option key="all" value="all">{"All"}</Option>
<Option key="signup" value="signup">{"Signup"}</Option>
<Option key="login" value="login">{"Login"}</Option>
<Option key="forget" value="forget">{"Forget Password"}</Option>
<Option key="reset" value="reset">{"Reset Password"}</Option>
<Option key="mfaSetup" value="mfaSetup">{"Set MFA"}</Option>
<Option key="mfaAuth" value="mfaAuth">{"MFA Auth"}</Option>
</Select>
);
} else {
return null;
}

View File

@ -178,7 +178,7 @@ class SigninTable extends React.Component {
},
},
{
title: i18next.t("application:Form CSS"),
title: i18next.t("application:Custom CSS"),
dataIndex: "label",
key: "label",
width: "200px",