Compare commits

...

23 Commits

Author SHA1 Message Date
4a3bd84f84 feat: fix the problem of abnormal tour when refreshing (#3054)
* fix: fix the problem of abnormal tour when refreshing

* fix: change the way enableTour configuration is stored
2024-07-12 19:27:55 +08:00
7f2869cecb feat: link transaction with balance and payment (#3052)
* feat: add and update transaction when recharging

* feat: add pay with balance

* feat: improve code format

* feat: update icon url for balance
2024-07-12 15:48:37 +08:00
cef2ab213b feat: add JWT-Standard format to fix oidc address type problem (#3050)
* feat: add JWT-Standard option to return standard OIDC UserInfo

* fix: fix error occurs by different claim type

* feat: improve code format and add missing return
2024-07-12 09:36:50 +08:00
cc979c310e feat: OAuth provider lark supports getting phone number (#3047) 2024-07-11 08:56:28 +08:00
13d73732ce fix: improve initBuiltInOrganization() 2024-07-10 14:18:30 +08:00
5686fe5d22 feat: use orgnization logo as tour logo and allow to configure whether to enable tour in organization edit page (#3046) 2024-07-10 14:18:04 +08:00
d8cb82f67a feat: upgrade CI Node.js version to 20 2024-07-09 13:09:40 +08:00
cad2e1bcc3 feat: don't drop empty table for adapters (#3043)
* fix: solve the problem of update operation returning 'unaffected'

* feat: remove the action for Dropping empty adapter data table
2024-07-09 11:35:22 +08:00
52cc2e4fa7 feat: fix bug in permission's owner edit (#3041) 2024-07-06 11:24:08 +08:00
8077a2ccba feat: fix bug for access key and secret login (#3022)
* fix: get username for keys

* chore: move user nil check
2024-06-27 21:24:54 +08:00
4cb8e4a514 feat: Revert "feat: fix OIDC address field" (#3020)
This reverts commit 2f48d45773.
2024-06-25 16:14:26 +08:00
2f48d45773 feat: fix OIDC address field (#3013)
* feat:add fields of sync-database

* feat:add fields of sync-database

* feat: add several fields related to the OIDC specification address

* feat: add the field Address to Address structure in UserWithoutThirdIdp

* fix: delete redundant fields

* fix: add Address struct and delete redundant fields
2024-06-25 11:54:34 +08:00
cff0c7a273 feat: support "Use Email as username" in org (#3002)
Signed-off-by: Grégoire Bélorgey <gregoire@jianda.fr>
2024-06-22 16:52:11 +08:00
793a7d6cda feat: add free charge price mode for product buy page (#3015)
* feat: add free charge price mode for product buy page

* fix: improve code format
2024-06-22 14:05:53 +08:00
4cc2120fed feat: fix the top Navbar UI is broken issue (#3000) 2024-06-09 17:05:04 +08:00
93b0f52f26 feat: Revert "feat: fix cannot create "/files" folder issue in local file storage provider in Docker" (#2997)
This reverts commit e228045e37.
2024-06-06 11:09:02 +08:00
e228045e37 feat: fix cannot create "/files" folder issue in local file storage provider in Docker (#2994) 2024-06-06 10:49:56 +08:00
6b8c24e1f0 feat: fix password not encrypted issue in SetPassword() API (#2990)
* fix: fix password not encrypted in set password and password type not changed

* Update user.go

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-06-04 13:32:13 +08:00
8a79bb64dd feat: test SMTP connection with browser parameters (#2986) 2024-06-04 01:34:36 +08:00
e5f9aab28f feat: support resetting password on first login (#2980)
* feat: support reset password in first login

* feat: disable needUpdatePassword when user haven't email and phone and mfa
2024-06-02 01:00:55 +08:00
7d05b69aac feat: remove useless code 2024-05-28 20:33:55 +08:00
868e66e866 feat: fix QQ login error when using mobile browser (#2971) 2024-05-27 01:07:15 +08:00
40ad3c9234 feat: support MFA fields in syncer (#2966)
* feat:add fields of sync-database

* feat:add fields of sync-database
2024-05-27 01:06:59 +08:00
72 changed files with 961 additions and 142 deletions

View File

@ -35,7 +35,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
cache: 'yarn'
cache-dependency-path: ./web/yarn.lock
- run: yarn install && CI=false yarn run build
@ -101,7 +101,7 @@ jobs:
working-directory: ./
- uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
cache: 'yarn'
cache-dependency-path: ./web/yarn.lock
- run: yarn install
@ -138,7 +138,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- name: Fetch Previous version
id: get-previous-tag

View File

@ -169,7 +169,11 @@ func (c *ApiController) Signup() {
username := authForm.Username
if !application.IsSignupItemVisible("Username") {
username = id
if organization.UseEmailAsUsername && application.IsSignupItemVisible("Email") {
username = authForm.Email
} else {
username = id
}
}
initScore, err := organization.GetInitScore()

View File

@ -117,7 +117,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
if form.Type == ResponseTypeLogin {
c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
resp = &Response{Status: "ok", Msg: "", Data: userId}
resp = &Response{Status: "ok", Msg: "", Data: userId, Data2: user.NeedUpdatePassword}
} else if form.Type == ResponseTypeCode {
clientId := c.Input().Get("clientId")
responseType := c.Input().Get("responseType")
@ -139,7 +139,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}
resp = codeToResponse(code)
resp.Data2 = user.NeedUpdatePassword
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
@ -152,6 +152,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
nonce := c.Input().Get("nonce")
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
resp = tokenToResponse(token)
resp.Data2 = user.NeedUpdatePassword
}
} else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, method, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
@ -159,7 +161,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
c.ResponseError(err.Error(), nil)
return
}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]interface{}{"redirectUrl": redirectUrl, "method": method, "needUpdatePassword": user.NeedUpdatePassword}}
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in

View File

@ -17,6 +17,7 @@ package controllers
import (
"encoding/json"
"fmt"
"strconv"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
@ -164,6 +165,16 @@ func (c *ApiController) BuyProduct() {
host := c.Ctx.Request.Host
providerName := c.Input().Get("providerName")
paymentEnv := c.Input().Get("paymentEnv")
customPriceStr := c.Input().Get("customPrice")
if customPriceStr == "" {
customPriceStr = "0"
}
customPrice, err := strconv.ParseFloat(customPriceStr, 64)
if err != nil {
c.ResponseError(err.Error())
return
}
// buy `pricingName/planName` for `paidUserName`
pricingName := c.Input().Get("pricingName")
@ -189,7 +200,7 @@ func (c *ApiController) BuyProduct() {
return
}
payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv)
payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv, customPrice)
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -27,11 +27,12 @@ import (
)
type EmailForm struct {
Title string `json:"title"`
Content string `json:"content"`
Sender string `json:"sender"`
Receivers []string `json:"receivers"`
Provider string `json:"provider"`
Title string `json:"title"`
Content string `json:"content"`
Sender string `json:"sender"`
Receivers []string `json:"receivers"`
Provider string `json:"provider"`
ProviderObject object.Provider `json:"providerObject"`
}
type SmsForm struct {
@ -74,7 +75,6 @@ func (c *ApiController) SendEmail() {
c.ResponseError(err.Error())
return
}
} else {
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
provider, err = c.GetProviderFromContext("Email")
@ -84,6 +84,13 @@ func (c *ApiController) SendEmail() {
}
}
if emailForm.ProviderObject.Name != "" {
if emailForm.ProviderObject.ClientSecret == "***" {
emailForm.ProviderObject.ClientSecret = provider.ClientSecret
}
provider = &emailForm.ProviderObject
}
// when receiver is the reserved keyword: "TestSmtpServer", it means to test the SMTP server instead of sending a real Email
if len(emailForm.Receivers) == 1 && emailForm.Receivers[0] == "TestSmtpServer" {
err = object.DailSmtpServer(provider)

View File

@ -333,6 +333,35 @@ func (c *ApiController) IntrospectToken() {
return
}
if application.TokenFormat == "JWT-Standard" {
jwtToken, err := object.ParseStandardJwtTokenByApplication(tokenValue, application)
if err != nil || jwtToken.Valid() != nil {
// and token revoked case. but we not implement
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
// refs: https://tools.ietf.org/html/rfc7009
c.Data["json"] = &object.IntrospectionResponse{Active: false}
c.ServeJSON()
return
}
c.Data["json"] = &object.IntrospectionResponse{
Active: true,
Scope: jwtToken.Scope,
ClientId: clientId,
Username: token.User,
TokenType: token.TokenType,
Exp: jwtToken.ExpiresAt.Unix(),
Iat: jwtToken.IssuedAt.Unix(),
Nbf: jwtToken.NotBefore.Unix(),
Sub: jwtToken.Subject,
Aud: jwtToken.Audience,
Iss: jwtToken.Issuer,
Jti: jwtToken.ID,
}
c.ServeJSON()
return
}
jwtToken, err := object.ParseJwtTokenByApplication(tokenValue, application)
if err != nil || jwtToken.Valid() != nil {
// and token revoked case. but we not implement

View File

@ -509,8 +509,21 @@ func (c *ApiController) SetPassword() {
return
}
organization, err := object.GetOrganizationByUser(targetUser)
if err != nil {
c.ResponseError(err.Error())
return
}
if organization == nil {
c.ResponseError(fmt.Sprintf(c.T("the organization: %s is not found"), targetUser.Owner))
return
}
targetUser.Password = newPassword
_, err = object.SetUserField(targetUser, "password", targetUser.Password)
targetUser.UpdateUserPassword(organization)
targetUser.NeedUpdatePassword = false
_, err = object.UpdateUser(userId, targetUser, []string{"password", "need_update_password", "password_type"}, false)
if err != nil {
c.ResponseError(err.Error())
return

2
go.mod
View File

@ -9,7 +9,7 @@ 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.23.0
github.com/casdoor/go-sms-sender v0.24.0
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.6.0

4
go.sum
View File

@ -1085,8 +1085,8 @@ 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.23.0 h1:N8+By4JNwyilEcx7cp0QGOepafefM88VwV+o3UEFZio=
github.com/casdoor/go-sms-sender v0.23.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
github.com/casdoor/go-sms-sender v0.24.0 h1:LNLsce3EG/87I3JS6UiajF3LlQmdIiCgebEu0IE4wSM=
github.com/casdoor/go-sms-sender v0.24.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
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=

View File

@ -22,6 +22,7 @@ import (
"strings"
"time"
"github.com/nyaruka/phonenumbers"
"golang.org/x/oauth2"
)
@ -199,12 +200,25 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
return nil, err
}
var phoneNumber string
var countryCode string
if len(larkUserInfo.Data.Mobile) != 0 {
phoneNumberParsed, err := phonenumbers.Parse(larkUserInfo.Data.Mobile, "")
if err != nil {
return nil, err
}
countryCode = phonenumbers.GetRegionCodeForNumber(phoneNumberParsed)
phoneNumber = fmt.Sprintf("%d", phoneNumberParsed.GetNationalNumber())
}
userInfo := UserInfo{
Id: larkUserInfo.Data.OpenId,
DisplayName: larkUserInfo.Data.EnName,
Username: larkUserInfo.Data.Name,
Email: larkUserInfo.Data.Email,
AvatarUrl: larkUserInfo.Data.AvatarUrl,
Phone: phoneNumber,
CountryCode: countryCode,
}
return &userInfo, nil
}

View File

@ -108,6 +108,8 @@ func initBuiltInOrganization() bool {
AccountItems: getBuiltInAccountItems(),
EnableSoftDeletion: false,
IsProfilePublic: false,
UseEmailAsUsername: false,
EnableTour: true,
}
_, err = AddOrganization(organization)
if err != nil {

View File

@ -72,6 +72,8 @@ type Organization struct {
InitScore int `json:"initScore"`
EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
UseEmailAsUsername bool `json:"useEmailAsUsername"`
EnableTour bool `json:"enableTour"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`

View File

@ -39,6 +39,8 @@ type Payment struct {
Currency string `xorm:"varchar(100)" json:"currency"`
Price float64 `json:"price"`
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
IsRecharge bool `xorm:"bool" json:"isRecharge"`
// Payer Info
User string `xorm:"varchar(100)" json:"user"`
PersonName string `xorm:"varchar(100)" json:"personName"`
@ -193,11 +195,16 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp
return payment, nil, err
}
if notifyResult.Price != product.Price {
if notifyResult.Price != product.Price && !product.IsRecharge {
err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", notifyResult.Price, product.Price)
return payment, nil, err
}
if payment.IsRecharge {
err = UpdateUserBalance(payment.Owner, payment.User, payment.Price)
return payment, notifyResult, err
}
return payment, notifyResult, nil
}
@ -215,6 +222,19 @@ func NotifyPayment(body []byte, owner string, paymentName string) (*Payment, err
if err != nil {
return nil, err
}
transaction, err := GetTransaction(payment.GetId())
if err != nil {
return nil, err
}
if transaction != nil {
transaction.State = payment.State
_, err = UpdateTransaction(transaction.GetId(), transaction)
if err != nil {
return nil, err
}
}
}
return payment, nil

View File

@ -181,15 +181,15 @@ func UpdatePermission(id string, permission *Permission) (bool, error) {
return false, err
}
if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
isEmpty, _ := ormer.Engine.IsTableEmpty(oldPermission.Adapter)
if isEmpty {
err = ormer.Engine.DropTables(oldPermission.Adapter)
if err != nil {
return false, err
}
}
}
// if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
// isEmpty, _ := ormer.Engine.IsTableEmpty(oldPermission.Adapter)
// if isEmpty {
// err = ormer.Engine.DropTables(oldPermission.Adapter)
// if err != nil {
// return false, err
// }
// }
// }
err = addGroupingPolicies(permission)
if err != nil {
@ -312,15 +312,15 @@ func DeletePermission(permission *Permission) (bool, error) {
return false, err
}
if permission.Adapter != "" && permission.Adapter != "permission_rule" {
isEmpty, _ := ormer.Engine.IsTableEmpty(permission.Adapter)
if isEmpty {
err = ormer.Engine.DropTables(permission.Adapter)
if err != nil {
return false, err
}
}
}
// if permission.Adapter != "" && permission.Adapter != "permission_rule" {
// isEmpty, _ := ormer.Engine.IsTableEmpty(permission.Adapter)
// if isEmpty {
// err = ormer.Engine.DropTables(permission.Adapter)
// if err != nil {
// return false, err
// }
// }
// }
}
return affected, nil

View File

@ -39,6 +39,7 @@ type Product struct {
Price float64 `json:"price"`
Quantity int `json:"quantity"`
Sold int `json:"sold"`
IsRecharge bool `json:"isRecharge"`
Providers []string `xorm:"varchar(255)" json:"providers"`
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
@ -160,7 +161,7 @@ func (product *Product) getProvider(providerName string) (*Provider, error) {
return provider, nil
}
func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string) (payment *Payment, attachInfo map[string]interface{}, err error) {
func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string, customPrice float64) (payment *Payment, attachInfo map[string]interface{}, err error) {
product, err := GetProduct(id)
if err != nil {
return nil, nil, err
@ -169,6 +170,14 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
return nil, nil, fmt.Errorf("the product: %s does not exist", id)
}
if product.IsRecharge {
if customPrice <= 0 {
return nil, nil, fmt.Errorf("the custom price should bigger than zero")
} else {
product.Price = customPrice
}
}
provider, err := product.getProvider(providerName)
if err != nil {
return nil, nil, err
@ -218,13 +227,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
NotifyUrl: notifyUrl,
PaymentEnv: paymentEnv,
}
// custom process for WeChat & WeChat Pay
if provider.Type == "WeChat Pay" {
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
if err != nil {
return nil, nil, err
}
} else if provider.Type == "Balance" {
payReq.PayerId = user.GetId()
}
payResp, err := pProvider.Pay(payReq)
if err != nil {
return nil, nil, err
@ -246,6 +259,7 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
Currency: product.Currency,
Price: product.Price,
ReturnUrl: product.ReturnUrl,
IsRecharge: product.IsRecharge,
User: user.Name,
PayUrl: payResp.PayUrl,
@ -254,8 +268,46 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
OutOrderId: payResp.OrderId,
}
transaction := &Transaction{
Owner: payment.Owner,
Name: payment.Name,
DisplayName: payment.DisplayName,
Provider: provider.Name,
Category: provider.Category,
Type: provider.Type,
ProductName: product.Name,
ProductDisplayName: product.DisplayName,
Detail: product.Detail,
Tag: product.Tag,
Currency: product.Currency,
Amount: payment.Price,
ReturnUrl: payment.ReturnUrl,
User: payment.User,
Application: owner,
Payment: payment.GetId(),
State: pp.PaymentStateCreated,
}
if provider.Type == "Dummy" {
payment.State = pp.PaymentStatePaid
err = UpdateUserBalance(user.Owner, user.Name, payment.Price)
if err != nil {
return nil, nil, err
}
} else if provider.Type == "Balance" {
if product.Price > user.Balance {
return nil, nil, fmt.Errorf("insufficient user balance")
}
transaction.Amount = -transaction.Amount
err = UpdateUserBalance(user.Owner, user.Name, -product.Price)
if err != nil {
return nil, nil, err
}
payment.State = pp.PaymentStatePaid
transaction.State = pp.PaymentStatePaid
}
affected, err := AddPayment(payment)
@ -266,6 +318,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
if !affected {
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
}
if product.IsRecharge || provider.Type == "Balance" {
affected, err = AddTransaction(transaction)
if err != nil {
return nil, nil, err
}
if !affected {
return nil, nil, fmt.Errorf("failed to add transaction: %s", util.StructToJson(payment))
}
}
return payment, payResp.AttachInfo, nil
}
@ -304,8 +367,9 @@ func CreateProductForPlan(plan *Plan) *Product {
Price: plan.Price,
Currency: plan.Currency,
Quantity: 999,
Sold: 0,
Quantity: 999,
Sold: 0,
IsRecharge: false,
Providers: plan.PaymentProviders,
State: "Published",

View File

@ -309,6 +309,12 @@ func GetPaymentProvider(p *Provider) (pp.PaymentProvider, error) {
return nil, err
}
return pp, nil
} else if typ == "Balance" {
pp, err := pp.NewBalancePaymentProvider()
if err != nil {
return nil, err
}
return pp, nil
} else {
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
}

View File

@ -48,7 +48,7 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
if provider.AppId != "" {
phoneNumbers = append([]string{provider.AppId}, phoneNumbers...)
}
} else if provider.Type == sender.Aliyun || provider.Type == sender.SendCloud {
} else if provider.Type == sender.Aliyun {
for i, number := range phoneNumbers {
phoneNumbers[i] = strings.TrimPrefix(number, "+86")
}

View File

@ -169,6 +169,12 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
user.TotpSecret = value
case "SignupApplication":
user.SignupApplication = value
case "MfaPhoneEnabled":
user.MfaPhoneEnabled = util.ParseBool(value)
case "MfaEmailEnabled":
user.MfaEmailEnabled = util.ParseBool(value)
case "RecoveryCodes":
user.RecoveryCodes = strings.Split(value, ",")
}
}
@ -303,6 +309,9 @@ func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]stri
m["PreferredMfaType"] = user.PreferredMfaType
m["TotpSecret"] = user.TotpSecret
m["SignupApplication"] = user.SignupApplication
m["MfaPhoneEnabled"] = util.BoolToString(user.MfaPhoneEnabled)
m["MfaEmailEnabled"] = util.BoolToString(user.MfaEmailEnabled)
m["RecoveryCodes"] = strings.Join(user.RecoveryCodes, ",")
m2 := map[string]string{}
for _, tableColumn := range syncer.TableColumns {

View File

@ -139,6 +139,15 @@ type ClaimsShort struct {
jwt.RegisteredClaims
}
type OIDCAddress struct {
Formatted string `json:"formatted"`
StreetAddress string `json:"street_address"`
Locality string `json:"locality"`
Region string `json:"region"`
PostalCode string `json:"postal_code"`
Country string `json:"country"`
}
type ClaimsWithoutThirdIdp struct {
*UserWithoutThirdIdp
TokenType string `json:"tokenType,omitempty"`
@ -386,6 +395,13 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
refreshClaims["TokenType"] = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, refreshClaims)
} else if application.TokenFormat == "JWT-Standard" {
claimsStandard := getStandardClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
claimsStandard.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsStandard.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
} else {
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
}

View File

@ -309,12 +309,22 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
}, nil
}
_, err = ParseJwtToken(refreshToken, cert)
if err != nil {
return &TokenError{
Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
}, nil
if application.TokenFormat == "JWT-Standard" {
_, err = ParseStandardJwtToken(refreshToken, cert)
if err != nil {
return &TokenError{
Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
}, nil
}
} else {
_, err = ParseJwtToken(refreshToken, cert)
if err != nil {
return &TokenError{
Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
}, nil
}
}
// generate a new token

View File

@ -0,0 +1,106 @@
// 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"
"strings"
"github.com/golang-jwt/jwt/v4"
)
type ClaimsStandard struct {
*UserShort
Gender string `json:"gender,omitempty"`
TokenType string `json:"tokenType,omitempty"`
Nonce string `json:"nonce,omitempty"`
Scope string `json:"scope,omitempty"`
Address OIDCAddress `json:"address,omitempty"`
jwt.RegisteredClaims
}
func getStreetAddress(user *User) string {
var addrs string
for _, addr := range user.Address {
addrs += addr + "\n"
}
return addrs
}
func getStandardClaims(claims Claims) ClaimsStandard {
res := ClaimsStandard{
UserShort: getShortUser(claims.User),
TokenType: claims.TokenType,
Nonce: claims.Nonce,
Scope: claims.Scope,
RegisteredClaims: claims.RegisteredClaims,
}
var scopes []string
if strings.Contains(claims.Scope, ",") {
scopes = strings.Split(claims.Scope, ",")
} else {
scopes = strings.Split(claims.Scope, " ")
}
for _, scope := range scopes {
if scope == "address" {
res.Address = OIDCAddress{StreetAddress: getStreetAddress(claims.User)}
} else if scope == "profile" {
res.Gender = claims.User.Gender
}
}
return res
}
func ParseStandardJwtToken(token string, cert *Cert) (*ClaimsStandard, error) {
t, err := jwt.ParseWithClaims(token, &ClaimsStandard{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
if cert.Certificate == "" {
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
}
// RSA certificate
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
if err != nil {
return nil, err
}
return certificate, nil
})
if t != nil {
if claims, ok := t.Claims.(*ClaimsStandard); ok && t.Valid {
return claims, nil
}
}
return nil, err
}
func ParseStandardJwtTokenByApplication(token string, application *Application) (*ClaimsStandard, error) {
cert, err := getCertByApplication(application)
if err != nil {
return nil, err
}
return ParseStandardJwtToken(token, cert)
}

View File

@ -17,6 +17,7 @@ package object
import (
"fmt"
"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
)
@ -43,7 +44,7 @@ type Transaction struct {
Application string `xorm:"varchar(100)" json:"application"`
Payment string `xorm:"varchar(100)" json:"payment"`
State string `xorm:"varchar(100)" json:"state"`
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
}
func GetTransactionCount(owner, field, value string) (int64, error) {

View File

@ -203,7 +203,8 @@ type User struct {
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
NeedUpdatePassword bool `json:"needUpdatePassword"`
}
type Userinfo struct {
@ -682,11 +683,11 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom",
"yammer", "yandex", "zoom", "custom", "need_update_password",
}
}
if isAdmin {
columns = append(columns, "name", "id", "email", "phone", "country_code", "type")
columns = append(columns, "name", "id", "email", "phone", "country_code", "type", "balance")
}
columns = append(columns, "updated_time")
@ -1156,3 +1157,13 @@ func GenerateIdForNewUser(application *Application) (string, error) {
res := strconv.Itoa(lastUserId + 1)
return res, nil
}
func UpdateUserBalance(owner string, name string, balance float64) error {
user, err := getUser(owner, name)
if err != nil {
return err
}
user.Balance += balance
_, err = UpdateUser(user.GetId(), user, []string{"balance"}, true)
return err
}

View File

@ -411,6 +411,15 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
item := GetAccountItemByName("Is deleted", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.NeedUpdatePassword != newUser.NeedUpdatePassword {
item := GetAccountItemByName("Need update password", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Balance != newUser.Balance {
item := GetAccountItemByName("Balance", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Score != newUser.Score {
item := GetAccountItemByName("Score", organization)

50
pp/balance.go Normal file
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.
package pp
import (
"fmt"
"github.com/casdoor/casdoor/util"
)
type BalancePaymentProvider struct{}
func NewBalancePaymentProvider() (*BalancePaymentProvider, error) {
pp := &BalancePaymentProvider{}
return pp, nil
}
func (pp *BalancePaymentProvider) Pay(r *PayReq) (*PayResp, error) {
owner, _ := util.GetOwnerAndNameFromId(r.PayerId)
return &PayResp{
PayUrl: r.ReturnUrl,
OrderId: fmt.Sprintf("%s/%s", owner, r.PaymentName),
}, nil
}
func (pp *BalancePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
return &NotifyResult{
PaymentStatus: PaymentStatePaid,
}, nil
}
func (pp *BalancePaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
return "", nil
}
func (pp *BalancePaymentProvider) GetResponseError(err error) string {
return ""
}

View File

@ -35,20 +35,13 @@ type Object struct {
}
func getUsername(ctx *context.Context) (username string) {
defer func() {
if r := recover(); r != nil {
username, _ = getUsernameByClientIdSecret(ctx)
}
}()
username = ctx.Input.Session("username").(string)
if username == "" {
username, ok := ctx.Input.Session("username").(string)
if !ok || username == "" {
username, _ = getUsernameByClientIdSecret(ctx)
}
if username == "" {
username = getUsernameByKeys(ctx)
username, _ = getUsernameByKeys(ctx)
}
return
}

View File

@ -91,17 +91,22 @@ func getUsernameByClientIdSecret(ctx *context.Context) (string, error) {
return fmt.Sprintf("app/%s", application.Name), nil
}
func getUsernameByKeys(ctx *context.Context) string {
func getUsernameByKeys(ctx *context.Context) (string, error) {
accessKey, accessSecret := getKeys(ctx)
user, err := object.GetUserByAccessKey(accessKey)
if err != nil {
panic(err)
return "", err
}
if user != nil && accessSecret == user.AccessSecret {
return user.GetId()
if user == nil {
return "", fmt.Errorf("user not found for access key: %s", accessKey)
}
return ""
if accessSecret != user.AccessSecret {
return "", fmt.Errorf("incorrect access secret for user: %s", user.Name)
}
return user.GetId(), nil
}
func getSessionUser(ctx *context.Context) string {

View File

@ -16,6 +16,7 @@ import React, {Component, Suspense, lazy} from "react";
import "./App.less";
import {Helmet} from "react-helmet";
import * as Setting from "./Setting";
import {setOrgIsTourVisible, setTourLogo} from "./TourConfig";
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
import {GithubOutlined, InfoCircleFilled, ShareAltOutlined} from "@ant-design/icons";
import {Alert, Button, ConfigProvider, Drawer, FloatButton, Layout, Result, Tooltip} from "antd";
@ -247,6 +248,8 @@ class App extends Component {
this.setLanguage(account);
this.setTheme(Setting.getThemeData(account.organization), Conf.InitThemeAlgorithm);
setTourLogo(account.organization.logo);
setOrgIsTourVisible(account.organization.enableTour);
} else {
if (res.data !== "Please login first") {
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
@ -414,6 +417,7 @@ class App extends Component {
<Layout id="parent-area">
<ManagementPage
account={this.state.account}
application={this.state.application}
uri={this.state.uri}
themeData={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}

View File

@ -384,7 +384,7 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}
options={["JWT", "JWT-Empty", "JWT-Custom"].map((item) => Setting.getOption(item, item))}
options={["JWT", "JWT-Empty", "JWT-Custom", "JWT-Standard"].map((item) => Setting.getOption(item, item))}
/>
</Col>
</Row>

View File

@ -108,8 +108,8 @@ class EntryPage extends React.Component {
<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="/forget" render={(props) => <SelfForgetPage {...this.props} account={this.props.account} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget/:applicationName" render={(props) => <ForgetPage {...this.props} account={this.props.account} 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} />)} />

View File

@ -328,6 +328,8 @@ function ManagementPage(props) {
return <Redirect to="/login" />;
} else if (props.account === undefined) {
return null;
} else if (props.account.needUpdatePassword) {
return <Redirect to={"/forget/" + props.application.name} />;
} else {
return component;
}
@ -409,7 +411,7 @@ function ManagementPage(props) {
return Setting.isMobile() || window.location.pathname.startsWith("/trees");
}
const menuStyleRight = Setting.isAdminUser(props.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "280px";
const menuStyleRight = Setting.isAdminUser(props.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "320px";
const onClose = () => {
setMenuVisible(false);

View File

@ -436,6 +436,26 @@ class OrganizationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Use Email as username"), i18next.t("organization:Use Email as username - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.organization.useEmailAsUsername} onChange={checked => {
this.updateOrganizationField("useEmailAsUsername", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Enable tour"), i18next.t("general:Enable tour - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.organization.enableTour} onChange={checked => {
this.updateOrganizationField("enableTour", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Account items"), i18next.t("organization:Account items - Tooltip"))} :

View File

@ -44,6 +44,7 @@ class OrganizationListPage extends BaseListPage {
defaultPassword: "",
enableSoftDeletion: false,
isProfilePublic: true,
enableTour: true,
accountItems: [
{name: "Organization", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "ID", visible: true, viewRule: "Public", modifyRule: "Immutable"},

View File

@ -17,6 +17,7 @@ import {Button, Result, Spin} from "antd";
import * as PaymentBackend from "./backend/PaymentBackend";
import * as PricingBackend from "./backend/PricingBackend";
import * as SubscriptionBackend from "./backend/SubscriptionBackend";
import * as UserBackend from "./backend/UserBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
@ -34,6 +35,7 @@ class PaymentResultPage extends React.Component {
pricing: props.pricing ?? null,
subscription: props.subscription ?? null,
timeout: null,
user: null,
};
}
@ -41,6 +43,25 @@ class PaymentResultPage extends React.Component {
this.getPayment();
}
getUser() {
UserBackend.getUser(this.props.account.owner, this.props.account.name)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
if (res.status === "error") {
Setting.showMessage("error", res.msg);
return;
}
this.setState({
user: res.data,
});
});
}
componentWillUnmount() {
if (this.state.timeout !== null) {
clearTimeout(this.state.timeout);
@ -101,7 +122,7 @@ class PaymentResultPage extends React.Component {
payment: payment,
});
if (payment.state === "Created") {
if (["PayPal", "Stripe", "Alipay", "WeChat Pay"].includes(payment.type)) {
if (["PayPal", "Stripe", "Alipay", "WeChat Pay", "Balance"].includes(payment.type)) {
this.setState({
timeout: setTimeout(async() => {
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
@ -114,6 +135,12 @@ class PaymentResultPage extends React.Component {
});
}
}
if (payment.state === "Paid") {
if (this.props.account) {
this.getUser();
}
}
} catch (err) {
Setting.showMessage("error", err.message);
return;
@ -136,6 +163,27 @@ class PaymentResultPage extends React.Component {
}
if (payment.state === "Paid") {
if (payment.isRecharge) {
return (
<div className="login-content">
{
Setting.renderHelmet(payment)
}
<Result
status="success"
title={`${i18next.t("payment:Recharged successfully")}`}
subTitle={`${i18next.t("payment:You have successfully recharged")} ${payment.price} ${Setting.getCurrencyText(payment)}, ${i18next.t("payment:Your current balance is")} ${this.state.user?.balance} ${Setting.getCurrencyText(payment)}`}
extra={[
<Button type="primary" key="returnUrl" onClick={() => {
this.goToPaymentUrl(payment);
}}>
{i18next.t("payment:Return to Website")}
</Button>,
]}
/>
</div>
);
}
return (
<div className="login-content">
{

View File

@ -487,6 +487,7 @@ class PermissionEditPage extends React.Component {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully saved"));
this.setState({
organizationName: this.state.permission.owner,
permissionName: this.state.permission.name,
});

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Descriptions, Spin} from "antd";
import {Button, Descriptions, InputNumber, Space, Spin} from "antd";
import i18next from "i18next";
import * as ProductBackend from "./backend/ProductBackend";
import * as PlanBackend from "./backend/PlanBackend";
@ -36,6 +36,7 @@ class ProductBuyPage extends React.Component {
pricing: props?.pricing ?? null,
plan: null,
isPlacingOrder: false,
customPrice: 0,
};
}
@ -127,18 +128,8 @@ class ProductBuyPage extends React.Component {
}
}
getCurrencyText(product) {
if (product?.currency === "USD") {
return i18next.t("product:USD");
} else if (product?.currency === "CNY") {
return i18next.t("product:CNY");
} else {
return "(Unknown currency)";
}
}
getPrice(product) {
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
return `${this.getCurrencySymbol(product)}${product?.price} (${Setting.getCurrencyText(product)})`;
}
// Call Weechat Pay via jsapi
@ -192,7 +183,7 @@ class ProductBuyPage extends React.Component {
isPlacingOrder: true,
});
ProductBackend.buyProduct(product.owner, product.name, provider.name, this.state.pricingName ?? "", this.state.planName ?? "", this.state.userName ?? "", this.state.paymentEnv)
ProductBackend.buyProduct(product.owner, product.name, provider.name, this.state.pricingName ?? "", this.state.planName ?? "", this.state.userName ?? "", this.state.paymentEnv, this.state.customPrice)
.then((res) => {
if (res.status === "ok") {
const payment = res.data;
@ -295,15 +286,27 @@ class ProductBuyPage extends React.Component {
<Descriptions.Item label={i18next.t("product:Image")} span={3}>
<img src={product?.image} alt={product?.name} height={90} style={{marginBottom: "20px"}} />
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Price")}>
<span style={{fontSize: 28, color: "red", fontWeight: "bold"}}>
{
this.getPrice(product)
}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Quantity")}><span style={{fontSize: 16}}>{product?.quantity}</span></Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Sold")}><span style={{fontSize: 16}}>{product?.sold}</span></Descriptions.Item>
{
product.isRecharge ? (
<Descriptions.Item span={3} label={i18next.t("product:Price")}>
<Space>
<InputNumber min={0} value={this.state.customPrice} onChange={(e) => {this.setState({customPrice: e});}} /> {Setting.getCurrencyText(product)}
</Space>
</Descriptions.Item>
) : (
<React.Fragment>
<Descriptions.Item label={i18next.t("product:Price")}>
<span style={{fontSize: 28, color: "red", fontWeight: "bold"}}>
{
this.getPrice(product)
}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Quantity")}><span style={{fontSize: 16}}>{product?.quantity}</span></Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Sold")}><span style={{fontSize: 16}}>{product?.sold}</span></Descriptions.Item>
</React.Fragment>
)
}
<Descriptions.Item label={i18next.t("product:Pay")} span={3}>
{
this.renderPay(product)

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import * as ProductBackend from "./backend/ProductBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
@ -216,14 +216,27 @@ class ProductEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Price"), i18next.t("product:Price - Tooltip"))} :
{Setting.getLabel(i18next.t("product:Is recharge"), i18next.t("product:Is recharge - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber value={this.state.product.price} disabled={isCreatedByPlan} onChange={value => {
this.updateProductField("price", value);
<Switch checked={this.state.product.isRecharge} onChange={value => {
this.updateProductField("isRecharge", value);
}} />
</Col>
</Row>
{
this.state.product.isRecharge ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Price"), i18next.t("product:Price - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber value={this.state.product.price} disabled={isCreatedByPlan} onChange={value => {
this.updateProductField("price", value);
}} />
</Col>
</Row>
)}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Quantity"), i18next.t("product:Quantity - Tooltip"))} :

View File

@ -38,6 +38,7 @@ class ProductListPage extends BaseListPage {
price: 300,
quantity: 99,
sold: 10,
isRecharge: false,
providers: [],
state: "Published",
};

View File

@ -209,8 +209,6 @@ class ProviderEditPage extends React.Component {
return Setting.getLabel(i18next.t("provider:Public key"), i18next.t("provider:Public key - Tooltip"));
} 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 if (provider.type === "SendCloud SMS") {
return "SMS_USER";
} else {
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
}
@ -262,8 +260,6 @@ class ProviderEditPage extends React.Component {
return Setting.getLabel(i18next.t("provider:Auth Key"), i18next.t("provider:Auth Key - Tooltip"));
} else if (provider.type === "Infobip SMS") {
return Setting.getLabel(i18next.t("provider:Api Key"), i18next.t("provider:Api Key - Tooltip"));
} else if (provider.type === "SendCloud SMS") {
return "SMS_KEY";
} else {
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
}
@ -729,7 +725,7 @@ class ProviderEditPage extends React.Component {
(this.state.provider.category === "Web3") ||
(this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ||
(this.state.provider.category === "SMS" && this.state.provider.type === "Custom HTTP SMS") ||
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP")) ? null : (
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP") || this.state.provider.type === "Balance") ? null : (
<React.Fragment>
{
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
@ -1107,7 +1103,7 @@ class ProviderEditPage extends React.Component {
</React.Fragment>
) : this.state.provider.category === "SMS" ? (
<React.Fragment>
{["Custom HTTP SMS", "Twilio SMS", "Amazon SNS", "Azure ACS", "Msg91 SMS", "Infobip SMS", "SendCloud SMS"].includes(this.state.provider.type) ?
{["Custom HTTP SMS", "Twilio SMS", "Amazon SNS", "Azure ACS", "Msg91 SMS", "Infobip SMS"].includes(this.state.provider.type) ?
null :
(<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>

View File

@ -139,10 +139,6 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_twilio.svg`,
url: "https://www.twilio.com/messaging",
},
"SendCloud SMS": {
logo: `${StaticBaseUrl}/img/sms_sendcloud.png`,
url: "https://www.sendcloud.net/",
},
"SmsBao SMS": {
logo: `${StaticBaseUrl}/img/social_smsbao.png`,
url: "https://www.smsbao.com/",
@ -251,6 +247,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/payment_paypal.png`,
url: "",
},
"Balance": {
logo: `${StaticBaseUrl}/img/payment_balance.svg`,
url: "",
},
"Alipay": {
logo: `${StaticBaseUrl}/img/payment_alipay.png`,
url: "https://www.alipay.com/",
@ -1043,7 +1043,6 @@ export function getProviderTypeOptions(category) {
{id: "Huawei Cloud SMS", name: "Huawei Cloud SMS"},
{id: "UCloud SMS", name: "UCloud SMS"},
{id: "Twilio SMS", name: "Twilio SMS"},
{id: "SendCloud SMS", name: "SendCloud SMS"},
{id: "SmsBao SMS", name: "SmsBao SMS"},
{id: "SUBMAIL SMS", name: "SUBMAIL SMS"},
{id: "Msg91 SMS", name: "Msg91 SMS"},
@ -1072,6 +1071,7 @@ export function getProviderTypeOptions(category) {
} else if (category === "Payment") {
return ([
{id: "Dummy", name: "Dummy"},
{id: "Balance", name: "Balance"},
{id: "Alipay", name: "Alipay"},
{id: "WeChat Pay", name: "WeChat Pay"},
{id: "PayPal", name: "PayPal"},
@ -1468,7 +1468,7 @@ export function getUserCommonFields() {
return ["Owner", "Name", "CreatedTime", "UpdatedTime", "DeletedTime", "Id", "Type", "Password", "PasswordSalt", "DisplayName", "FirstName", "LastName", "Avatar", "PermanentAvatar",
"Email", "EmailVerified", "Phone", "Location", "Address", "Affiliation", "Title", "IdCardType", "IdCard", "Homepage", "Bio", "Tag", "Region",
"Language", "Gender", "Birthday", "Education", "Score", "Ranking", "IsDefaultAvatar", "IsOnline", "IsAdmin", "IsForbidden", "IsDeleted", "CreatedIp",
"PreferredMfaType", "TotpSecret", "SignupApplication"];
"PreferredMfaType", "TotpSecret", "SignupApplication", "RecoveryCodes", "MfaPhoneEnabled", "MfaEmailEnabled"];
}
export function getDefaultFooterContent() {
@ -1521,3 +1521,13 @@ export function getDefaultHtmlEmailContent() {
</body>
</html>`;
}
export function getCurrencyText(product) {
if (product?.currency === "USD") {
return i18next.t("product:USD");
} else if (product?.currency === "CNY") {
return i18next.t("product:CNY");
} else {
return "(Unknown currency)";
}
}

View File

@ -203,13 +203,24 @@ export function getNextUrl(pathName = window.location.pathname) {
return TourUrlList[TourUrlList.indexOf(pathName.replace("/", "")) + 1] || "";
}
let orgIsTourVisible = true;
export function setOrgIsTourVisible(visible) {
orgIsTourVisible = visible;
}
export function setIsTourVisible(visible) {
localStorage.setItem("isTourVisible", visible);
window.dispatchEvent(new Event("storageTourChanged"));
}
export function setTourLogo(tourLogoSrc) {
if (tourLogoSrc !== "") {
TourObj["home"][0]["cover"] = (<img alt="casdoor.png" src={tourLogoSrc} />);
}
}
export function getTourVisible() {
return localStorage.getItem("isTourVisible") !== "false";
return localStorage.getItem("isTourVisible") !== "false" && orgIsTourVisible;
}
export function getNextButtonChild(nextPathName) {

View File

@ -707,6 +707,19 @@ class UserEditPage extends React.Component {
</Col>
</Row>
);
} else if (accountItem.name === "Balance") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Balance"), i18next.t("user:Balance - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber value={this.state.user.balance} onChange={value => {
this.updateUserField("balance", value);
}} />
</Col>
</Row>
);
} else if (accountItem.name === "Score") {
return (
<Row style={{marginTop: "20px"}} >
@ -1026,6 +1039,19 @@ class UserEditPage extends React.Component {
</Col>
</Row>
);
} else if (accountItem.name === "Need update password") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Need update password"), i18next.t("user:Need update password - Tooltip"))} :
</Col>
<Col span={(Setting.isMobile()) ? 22 : 2} >
<Switch disabled={(!this.state.user.phone) && (!this.state.user.email) && (!this.state.user.mfaProps)} checked={this.state.user.needUpdatePassword} onChange={checked => {
this.updateUserField("needUpdatePassword", checked);
}} />
</Col>
</Row>
);
}
}

View File

@ -153,21 +153,37 @@ class AuthCallback extends React.Component {
// OAuth
const oAuthParams = Util.getOAuthGetParameters(innerParams);
const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?";
const signinUrl = localStorage.getItem("signinUrl");
AuthBackend.login(body, oAuthParams)
.then((res) => {
if (res.status === "ok") {
const responseType = this.getResponseType();
if (responseType === "login") {
if (res.data2) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
Setting.showMessage("success", "Logged in successfully");
// Setting.goToLinkSoft(this, "/");
const link = Setting.getFromLink();
Setting.goToLink(link);
} else if (responseType === "code") {
if (res.data2) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
const code = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
// Setting.showMessage("success", `Authorization code: ${res.data}`);
} else if (responseType === "token" || responseType === "id_token") {
if (res.data2) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
const token = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "link") {
@ -181,6 +197,11 @@ class AuthCallback extends React.Component {
relayState: oAuthParams.relayState,
});
} else {
if (res.data2.needUpdatePassword) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);

View File

@ -35,8 +35,8 @@ class ForgetPage extends React.Component {
classes: props,
applicationName: props.applicationName ?? props.match.params?.applicationName,
msg: null,
name: "",
username: "",
name: props.account ? props.account.name : "",
username: props.account ? props.account.name : "",
phone: "",
email: "",
dest: "",
@ -44,7 +44,6 @@ class ForgetPage extends React.Component {
verifyType: "", // "email", "phone"
current: 0,
};
this.form = React.createRef();
}
@ -205,6 +204,7 @@ class ForgetPage extends React.Component {
initialValues={{
application: application.name,
organization: application.organization,
username: this.state.name,
}}
style={{width: "300px"}}
size="large"
@ -488,7 +488,7 @@ class ForgetPage extends React.Component {
<Row>
<Col span={24}>
<div style={{textAlign: "center", fontSize: "28px"}}>
{i18next.t("forget:Retrieve password")}
{i18next.t("forget:Reset password")}
</div>
</Col>
</Row>

View File

@ -68,6 +68,8 @@ class LoginPage extends React.Component {
this.state.applicationName = props.match?.params?.casApplicationName;
}
localStorage.setItem("signinUrl", window.location.href);
this.form = React.createRef();
}
@ -300,6 +302,12 @@ class LoginPage extends React.Component {
return;
}
if (resp.data2) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLinkSoft(ths, `/forget/${application.name}`);
return;
}
if (Setting.hasPromptPage(application)) {
AuthBackend.getAccount()
.then((res) => {
@ -442,15 +450,27 @@ class LoginPage extends React.Component {
const responseType = values["type"];
if (responseType === "login") {
if (res.data2) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
}
Setting.showMessage("success", i18next.t("application:Logged in successfully"));
this.props.onLoginSuccess();
} else if (responseType === "code") {
this.postCodeLoginAction(res);
} else if (responseType === "token" || responseType === "id_token") {
if (res.data2) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
}
const amendatoryResponseType = responseType === "token" ? "access_token" : responseType;
const accessToken = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}#${amendatoryResponseType}=${accessToken}&state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "saml") {
if (res.data2.needUpdatePassword) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
}
if (res.data2.method === "POST") {
this.setState({
samlResponse: res.data,

View File

@ -399,7 +399,7 @@ export function getAuthUrl(application, provider, method, code) {
scope += "+https://www.googleapis.com/auth/user.phonenumbers.read";
}
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook"
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "Facebook"
|| provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD"
|| provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet"
|| provider.type === "Bitbucket" || provider.type === "Box" || provider.type === "CloudFoundry" || provider.type === "Dailymotion"
@ -411,6 +411,8 @@ export function getAuthUrl(application, provider, method, code) {
|| provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay"
|| provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") {
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
} else if (provider.type === "QQ") {
return `${endpoint}?response_type=code&client_id=${provider.clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(scope)}`;
} else if (provider.type === "AzureADB2C") {
return `https://${provider.domain}.b2clogin.com/${provider.domain}.onmicrosoft.com/${provider.appId}/oauth2/v2.0/authorize?client_id=${provider.clientId}&nonce=defaultNonce&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}&prompt=login`;
} else if (provider.type === "DingTalk") {

View File

@ -70,8 +70,8 @@ export function deleteProduct(product) {
}).then(res => res.json());
}
export function buyProduct(owner, name, providerName, pricingName = "", planName = "", userName = "", paymentEnv = "") {
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerName}&pricingName=${pricingName}&planName=${planName}&userName=${userName}&paymentEnv=${paymentEnv}`, {
export function buyProduct(owner, name, providerName, pricingName = "", planName = "", userName = "", paymentEnv = "", customPrice = 0) {
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerName}&pricingName=${pricingName}&planName=${planName}&userName=${userName}&paymentEnv=${paymentEnv}&customPrice=${customPrice}`, {
method: "POST",
credentials: "include",
headers: {

View File

@ -50,6 +50,7 @@ function testEmailProvider(provider, email = "") {
sender: provider.displayName,
receivers: email === "" ? ["TestSmtpServer"] : [email],
provider: provider.name,
providerObject: provider,
};
return fetch(`${Setting.ServerUrl}/api/send-email`, {

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Nächster Schritt",
"Please input your username!": "Bitte gib deinen Benutzernamen ein!",
"Reset": "Zurücksetzen",
"Retrieve password": "Passwort abrufen",
"Reset password": "Passwort abrufen",
"Unknown forget type": "Unbekannter Vergesslichkeitstyp",
"Verify": "überprüfen"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Wenn aktiviert, werden gelöschte Benutzer nicht vollständig aus der Datenbank entfernt. Stattdessen werden sie als gelöscht markiert",
"Tags": "Tags",
"Tags - Tooltip": "Sammlung von Tags, die für Benutzer zur Auswahl zur Verfügung stehen",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Ansichtsregel",
"Visible": "Sichtbar",
"Website URL": "Website-URL",
@ -619,6 +621,7 @@
"Processing...": "In Bearbeitung...",
"Product": "Produkt",
"Product - Tooltip": "Produktname",
"Recharged successfully": "Recharged successfully",
"Result": "Ergebnis",
"Return to Website": "Zurück zur Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "Die Zahlung wird immer noch bearbeitet",
"Type - Tooltip": "Zahlungsmethode, die beim Kauf des Produkts verwendet wurde",
"You have successfully completed the payment": "Sie haben die Zahlung erfolgreich abgeschlossen",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "Bitte warten Sie ein paar Sekunden...",
"the current state is": "der aktuelle Zustand ist"
},
@ -689,6 +694,8 @@
"Edit Product": "Produkt bearbeiten",
"Image": "Bild",
"Image - Tooltip": "Bild des Produkts",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Neues Produkt",
"Pay": "Zahlen",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Zugehörigkeit",
"Affiliation - Tooltip": "Arbeitgeber, wie Firmenname oder Organisationsname",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Selbstvorstellung des Nutzers",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Verwaltete Konten",
"Modify password...": "Passwort ändern...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Neue E-Mail",
"New Password": "Neues Passwort",
"New User": "Neuer Benutzer",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Siguiente paso",
"Please input your username!": "¡Por favor, ingrese su nombre de usuario!",
"Reset": "Restablecer",
"Retrieve password": "Recuperar contraseña",
"Reset password": "Reset password",
"Unknown forget type": "Tipo de olvido desconocido",
"Verify": "Verificar"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Cuando se habilita, la eliminación de usuarios no los eliminará por completo de la base de datos. En su lugar, se marcarán como eliminados",
"Tags": "Etiquetas",
"Tags - Tooltip": "Colección de etiquetas disponibles para que los usuarios elijan",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Regla de visualización",
"Visible": "Visible - Visible",
"Website URL": "URL del sitio web",
@ -619,6 +621,7 @@
"Processing...": "Procesando...",
"Product": "Producto",
"Product - Tooltip": "Nombre del producto",
"Recharged successfully": "Recharged successfully",
"Result": "Resultado",
"Return to Website": "Regresar al sitio web",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "El pago aún está en proceso",
"Type - Tooltip": "Método de pago utilizado al comprar el producto",
"You have successfully completed the payment": "Has completado el pago exitosamente",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "Por favor espera unos segundos...",
"the current state is": "el estado actual es"
},
@ -689,6 +694,8 @@
"Edit Product": "Editar Producto",
"Image": "Imagen",
"Image - Tooltip": "Imagen del producto",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Nuevo producto",
"Pay": "Pagar",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Afiliación",
"Affiliation - Tooltip": "Empleador, como el nombre de una empresa u organización",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio - Biografía",
"Bio - Tooltip": "Introducción personal del usuario",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Cuentas gestionadas",
"Modify password...": "Modificar contraseña...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Nuevo correo electrónico",
"New Password": "Nueva contraseña",
"New User": "Nuevo Usuario",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Étape suivante",
"Please input your username!": "Veuillez saisir votre identifiant !",
"Reset": "Réinitialiser",
"Retrieve password": "Récupérer le mot de passe",
"Reset password": "Récupérer le mot de passe",
"Unknown forget type": "Type d'oubli inconnu",
"Verify": "Vérifier"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Lorsque c'est activée, la suppression de compte ne les retirera pas complètement de la base de données. Au lieu de cela, ils seront marqués comme supprimés",
"Tags": "Étiquettes",
"Tags - Tooltip": "Collection d'étiquettes disponibles pour les comptes",
"Use Email as username": "Utiliser l'e-mail comme identifiant",
"Use Email as username - Tooltip": "Utiliser l'adresse e-mail comme identifiant pour les comptes lorsque l'identifiant ne fait pas partie des champs d'inscription",
"View rule": "Règle de visibilité",
"Visible": "Visible",
"Website URL": "URL du site web",
@ -619,6 +621,7 @@
"Processing...": "Traitement...",
"Product": "Produit",
"Product - Tooltip": "Nom du produit",
"Recharged successfully": "Recharged successfully",
"Result": "Résultat",
"Return to Website": "Retourner sur le site web",
"The payment has been canceled": "Le paiement a été annulé",
@ -627,6 +630,8 @@
"The payment is still under processing": "Le paiement est encore en cours de traitement",
"Type - Tooltip": "Méthode de paiement utilisée lors de l'achat du produit",
"You have successfully completed the payment": "Vous avez effectué le paiement avec succès",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "Veuillez patienter quelques secondes...",
"the current state is": "l'état actuel est"
},
@ -689,6 +694,8 @@
"Edit Product": "Modifier le produit",
"Image": "Image",
"Image - Tooltip": "Image du produit",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Nouveau produit",
"Pay": "Payer",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employeur, tel que le nom de l'entreprise ou de l'organisation",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Biographie du compte",
"Birthday": "Date de naissance",
@ -1130,6 +1139,8 @@
"Managed accounts": "Comptes gérés",
"Modify password...": "Modifier le mot de passe...",
"Multi-factor authentication": "Authentification multifacteur",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Nouvelle adresse e-mail",
"New Password": "Nouveau mot de passe",
"New User": "Nouveau compte",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Langkah selanjutnya",
"Please input your username!": "Silakan masukkan nama pengguna Anda!",
"Reset": "Menyetel-ulang",
"Retrieve password": "Mengambil password",
"Reset password": "Mengambil password",
"Unknown forget type": "Tipe yang tidak diketahui terlupakan",
"Verify": "Memverifikasi"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Ketika diaktifkan, menghapus pengguna tidak akan sepenuhnya menghapus mereka dari database. Sebaliknya, mereka akan ditandai sebagai dihapus",
"Tags": "Tag-tag",
"Tags - Tooltip": "Kumpulan tag yang tersedia bagi pengguna untuk dipilih",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Aturan tampilan",
"Visible": "Terlihat",
"Website URL": "URL situs web",
@ -619,6 +621,7 @@
"Processing...": "Pemrosesan...",
"Product": "Produk",
"Product - Tooltip": "Nama Produk",
"Recharged successfully": "Recharged successfully",
"Result": "Hasil",
"Return to Website": "Kembali ke Situs Web",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "Pembayaran masih dalam proses",
"Type - Tooltip": "Metode pembayaran yang digunakan saat membeli produk",
"You have successfully completed the payment": "Anda telah berhasil menyelesaikan pembayaran",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "Mohon tunggu beberapa detik...",
"the current state is": "keadaan saat ini adalah"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Produk",
"Image": "Gambar",
"Image - Tooltip": "Gambar produk",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Produk Baru",
"Pay": "Bayar",
"PayPal": "Paypal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Afiliasi",
"Affiliation - Tooltip": "Pemberi Kerja, seperti nama perusahaan atau nama organisasi",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio: Biografi",
"Bio - Tooltip": "Pengenalan diri dari pengguna",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Akun yang dikelola",
"Modify password...": "Mengubah kata sandi...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Email baru",
"New Password": "Kata Sandi Baru",
"New User": "Pengguna Baru",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "次のステップ",
"Please input your username!": "ユーザー名を入力してください!",
"Reset": "リセット",
"Retrieve password": "パスワードの取得",
"Reset password": "パスワードの取得",
"Unknown forget type": "未知の忘却タイプ",
"Verify": "検証"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "有効になっている場合、ユーザーを削除しても完全にデータベースから削除されません。代わりに、削除されたとマークされます",
"Tags": "タグ",
"Tags - Tooltip": "ユーザーが選択できるタグのコレクション",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "ビュールール",
"Visible": "見える",
"Website URL": "ウェブサイトのURL",
@ -619,6 +621,7 @@
"Processing...": "処理中... ",
"Product": "製品",
"Product - Tooltip": "製品名",
"Recharged successfully": "Recharged successfully",
"Result": "結果",
"Return to Website": "ウェブサイトに戻る",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "支払いはまだ処理中です",
"Type - Tooltip": "製品を購入する際に使用される支払方法",
"You have successfully completed the payment": "あなたは支払いを正常に完了しました",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "数秒お待ちください...",
"the current state is": "現在の状態は"
},
@ -689,6 +694,8 @@
"Edit Product": "製品を編集",
"Image": "画像",
"Image - Tooltip": "製品のイメージ",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "新製品",
"Pay": "支払う",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "所属",
"Affiliation - Tooltip": "企業名や団体名などの雇用主",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "バイオ技術",
"Bio - Tooltip": "ユーザーの自己紹介\n\n私は○○です。私は○○国、都市、職業など出身で、現在は○○国、都市、職業などに住んでいます。私は○○趣味、特技、興味などが好きで、空き時間にはよくそれをしています。よろしくお願いします",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "管理アカウント",
"Modify password...": "パスワードを変更する...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "新しいメール",
"New Password": "新しいパスワード",
"New User": "新しいユーザー",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "다음 단계",
"Please input your username!": "사용자 이름을 입력하세요!",
"Reset": "리셋",
"Retrieve password": "비밀번호를 복구하세요",
"Reset password": "비밀번호를 복구하세요",
"Unknown forget type": "미지의 잊혀진 유형",
"Verify": "검증하다"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "사용 가능한 경우, 사용자 삭제 시 데이터베이스에서 완전히 삭제되지 않습니다. 대신 삭제됨으로 표시됩니다",
"Tags": "태그",
"Tags - Tooltip": "사용자가 선택할 수 있는 태그 컬렉션",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "보기 규칙",
"Visible": "보이는",
"Website URL": "웹사이트 URL",
@ -619,6 +621,7 @@
"Processing...": "처리 중...",
"Product": "제품",
"Product - Tooltip": "제품 이름",
"Recharged successfully": "Recharged successfully",
"Result": "결과",
"Return to Website": "웹 사이트로 돌아가기",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "지불은 아직 처리 중입니다",
"Type - Tooltip": "제품을 구매할 때 사용되는 결제 방법",
"You have successfully completed the payment": "당신은 결제를 성공적으로 완료하셨습니다",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "잠시만 기다려주세요...",
"the current state is": "현재 상태입니다"
},
@ -689,6 +694,8 @@
"Edit Product": "제품 편집",
"Image": "이미지",
"Image - Tooltip": "제품 이미지",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "새로운 제품",
"Pay": "결제하다",
"PayPal": "페이팔",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "소속",
"Affiliation - Tooltip": "고용주, 회사명 또는 조직명",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "바이오",
"Bio - Tooltip": "사용자의 자기소개\n\n안녕하세요, 저는 [이름]입니다. 한국을 포함한 여러 나라에서 살아본 적이 있습니다. 저는 [직업/전공]을 공부하고 있으며 [취미/관심사]에 대해 깊게 알고 있습니다. 이 채팅 서비스를 사용하여 새로운 사람들과 함께 대화를 나누기를 원합니다. 감사합니다",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "관리 계정",
"Modify password...": "비밀번호 수정하기...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "새 이메일",
"New Password": "새로운 비밀번호",
"New User": "새로운 사용자",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Próxima Etapa",
"Please input your username!": "Por favor, insira seu nome de usuário!",
"Reset": "Redefinir",
"Retrieve password": "Recuperar senha",
"Reset password": "Recuperar senha",
"Unknown forget type": "Tipo de recuperação desconhecido",
"Verify": "Verificar"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Quando ativada, a exclusão de usuários não os removerá completamente do banco de dados. Em vez disso, eles serão marcados como excluídos",
"Tags": "Tags",
"Tags - Tooltip": "Coleção de tags disponíveis para os usuários escolherem",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Ver regra",
"Visible": "Visível",
"Website URL": "URL do website",
@ -619,6 +621,7 @@
"Processing...": "Processando...",
"Product": "Produto",
"Product - Tooltip": "Nome do Produto",
"Recharged successfully": "Recharged successfully",
"Result": "Resultado",
"Return to Website": "Retornar ao Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "O pagamento ainda está sendo processado",
"Type - Tooltip": "Método de pagamento utilizado ao comprar o produto",
"You have successfully completed the payment": "Você concluiu o pagamento com sucesso",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "por favor, aguarde alguns segundos...",
"the current state is": "o estado atual é"
},
@ -689,6 +694,8 @@
"Edit Product": "Editar Produto",
"Image": "Imagem",
"Image - Tooltip": "Imagem do produto",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Novo Produto",
"Pay": "Pagar",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Afiliação",
"Affiliation - Tooltip": "Empregador, como nome da empresa ou organização",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Biografia",
"Bio - Tooltip": "Autoapresentação do usuário",
"Birthday": "Aniversário",
@ -1130,6 +1139,8 @@
"Managed accounts": "Contas gerenciadas",
"Modify password...": "Modificar senha...",
"Multi-factor authentication": "Autenticação de vários fatores",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Novo E-mail",
"New Password": "Nova Senha",
"New User": "Novo Usuário",

View File

@ -166,7 +166,7 @@
"Next Step": "Следующий шаг",
"Please input your username!": "Пожалуйста, введите своё имя пользователя!",
"Reset": "Сбросить",
"Retrieve password": "Восстановить пароль",
"Reset password": "Восстановить пароль",
"Unknown forget type": "Неизвестный забытый тип",
"Verify": "Проверить"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Когда включено, удаление пользователей не полностью удаляет их из базы данных. Вместо этого они будут помечены как удаленные",
"Tags": "Теги",
"Tags - Tooltip": "Коллекция тегов, доступных для выбора пользователями",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Правило просмотра",
"Visible": "Видимый",
"Website URL": "Веб-адрес сайта",
@ -619,6 +621,7 @@
"Processing...": "Обработка...",
"Product": "Продукт",
"Product - Tooltip": "Название продукта",
"Recharged successfully": "Recharged successfully",
"Result": "Результат",
"Return to Website": "Вернуться на веб-сайт",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "Оплата все еще обрабатывается",
"Type - Tooltip": "Способ оплаты, используемый при покупке товара",
"You have successfully completed the payment": "Вы успешно произвели платеж",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "Пожалуйста, подождите несколько секунд...",
"the current state is": "текущее состояние"
},
@ -689,6 +694,8 @@
"Edit Product": "Редактировать продукт",
"Image": "Изображение",
"Image - Tooltip": "Изображение продукта",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Новый продукт",
"Pay": "Заплатить",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Принадлежность",
"Affiliation - Tooltip": "Работодатель, такой как название компании или организации",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Био",
"Bio - Tooltip": "Само представление пользователя",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Управляемые аккаунты",
"Modify password...": "Изменить пароль...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Новое электронное письмо",
"New Password": "Новый пароль",
"New User": "Новый пользователь",

View File

@ -166,7 +166,7 @@
"Next Step": "Next Step",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Reset password": "Reset password",
"Unknown forget type": "Unknown forget type",
"Verify": "Verify"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Sonraki adım",
"Please input your username!": "Lütfen kullanıcı adınızı girin",
"Reset": "Sıfırla",
"Retrieve password": "Şifre kurtar",
"Reset password": "Şifre kurtar",
"Unknown forget type": "Unknown forget type",
"Verify": "Doğrula"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Collection of tags available for users to choose from",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "View rule",
"Visible": "Görünür",
"Website URL": "Web Sitesi URL'si",
@ -619,6 +621,7 @@
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product Name",
"Recharged successfully": "Recharged successfully",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
@ -689,6 +694,8 @@
"Edit Product": "Edit Product",
"Image": "Image",
"Image - Tooltip": "Image of product",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "New Product",
"Pay": "Pay",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "Bio",
"Bio - Tooltip": "Self introduction of the user",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",

View File

@ -166,7 +166,7 @@
"Next Step": "Наступний крок",
"Please input your username!": "Будь ласка, введіть своє ім'я користувача!",
"Reset": "Скинути",
"Retrieve password": "Отримати пароль",
"Reset password": "Отримати пароль",
"Unknown forget type": "Невідомий забутий тип",
"Verify": "Підтвердити"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Якщо ввімкнено, видалення користувачів не призведе до їх повного видалення з бази даних. ",
"Tags": "Теги",
"Tags - Tooltip": "Колекція тегів, доступна для вибору користувачами",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Переглянути правило",
"Visible": "Видно",
"Website URL": "адреса вебсайту",
@ -619,6 +621,7 @@
"Processing...": "Обробка...",
"Product": "Продукт",
"Product - Tooltip": "Назва продукту",
"Recharged successfully": "Recharged successfully",
"Result": "Результат",
"Return to Website": "Повернутися на сайт",
"The payment has been canceled": "Платіж скасовано",
@ -627,6 +630,8 @@
"The payment is still under processing": "Платіж ще обробляється",
"Type - Tooltip": "Спосіб оплати, який використовується при покупці товару",
"You have successfully completed the payment": "Ви успішно завершили оплату",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "зачекайте кілька секунд...",
"the current state is": "поточний стан є"
},
@ -689,6 +694,8 @@
"Edit Product": "Редагувати товар",
"Image": "Зображення",
"Image - Tooltip": "Зображення товару",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Новий продукт",
"Pay": "платити",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Приналежність",
"Affiliation - Tooltip": "Роботодавець, наприклад назва компанії чи організації",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "біографія",
"Bio - Tooltip": "Самостійне представлення користувача",
"Birthday": "день народження",
@ -1130,6 +1139,8 @@
"Managed accounts": "Керовані облікові записи",
"Modify password...": "Змінити пароль...",
"Multi-factor authentication": "Багатофакторна аутентифікація",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Нова електронна пошта",
"New Password": "Новий пароль",
"New User": "Новий користувач",

View File

@ -166,7 +166,7 @@
"Next Step": "Bước tiếp theo",
"Please input your username!": "Vui lòng nhập tên đăng nhập của bạn!",
"Reset": "Đặt lại",
"Retrieve password": "Truy xuất mật khẩu",
"Reset password": "Truy xuất mật khẩu",
"Unknown forget type": "Loại quên chưa biết",
"Verify": "Xác thực"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "Khi được bật, việc xóa người dùng sẽ không hoàn toàn loại bỏ họ khỏi cơ sở dữ liệu. Thay vào đó, họ sẽ được đánh dấu là đã bị xóa",
"Tags": "Thẻ",
"Tags - Tooltip": "Bộ sưu tập các thẻ có sẵn cho người dùng lựa chọn",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "Xem quy tắc",
"Visible": "Rõ ràng",
"Website URL": "Địa chỉ trang web",
@ -619,6 +621,7 @@
"Processing...": "Đang xử lý...",
"Product": "Sản phẩm",
"Product - Tooltip": "Tên sản phẩm",
"Recharged successfully": "Recharged successfully",
"Result": "Kết quả",
"Return to Website": "Trở lại trang web",
"The payment has been canceled": "The payment has been canceled",
@ -627,6 +630,8 @@
"The payment is still under processing": "Thanh toán vẫn đang được xử lý",
"Type - Tooltip": "Phương thức thanh toán được sử dụng khi mua sản phẩm",
"You have successfully completed the payment": "Bạn đã hoàn thành thanh toán thành công",
"You have successfully recharged": "You have successfully recharged",
"Your current balance is": "Your current balance is",
"please wait for a few seconds...": "Vui lòng đợi trong vài giây...",
"the current state is": "tình trạng hiện tại là"
},
@ -689,6 +694,8 @@
"Edit Product": "Sửa sản phẩm",
"Image": "Ảnh",
"Image - Tooltip": "Hình ảnh sản phẩm",
"Is recharge": "Is recharge",
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
"New Product": "Sản phẩm mới",
"Pay": "Trả tiền",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "Address line",
"Affiliation": "Liên kết",
"Affiliation - Tooltip": "Nhà tuyển dụng, chẳng hạn như tên công ty hoặc tổ chức",
"Balance": "Balance",
"Balance - Tooltip": "User's balance",
"Bio": "bản vẻ đời sống",
"Bio - Tooltip": "Tự giới thiệu của người dùng",
"Birthday": "Birthday",
@ -1130,6 +1139,8 @@
"Managed accounts": "Quản lý tài khoản",
"Modify password...": "Sửa đổi mật khẩu...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Email mới",
"New Password": "Mật khẩu mới",
"New User": "Người dùng mới",

View File

@ -166,7 +166,7 @@
"Next Step": "下一步",
"Please input your username!": "请输入您的用户名!",
"Reset": "重置",
"Retrieve password": "找回密码",
"Reset password": "重置密码",
"Unknown forget type": "未知的忘记密码类型",
"Verify": "验证"
},
@ -576,6 +576,8 @@
"Soft deletion - Tooltip": "启用后,删除一个用户时不会在数据库彻底清除,只会标记为已删除状态",
"Tags": "标签集合",
"Tags - Tooltip": "可供用户选择的标签集合",
"Use Email as username": "Use Email as username",
"Use Email as username - Tooltip": "Use Email as username if the username field is not visible at signup",
"View rule": "查看规则",
"Visible": "是否可见",
"Website URL": "主页地址",
@ -619,6 +621,7 @@
"Processing...": "正在处理...",
"Product": "商品",
"Product - Tooltip": "商品名称",
"Recharged successfully": "充值成功",
"Result": "结果",
"Return to Website": "返回原网站",
"The payment has been canceled": "付款已取消",
@ -627,6 +630,8 @@
"The payment is still under processing": "支付正在处理",
"Type - Tooltip": "商品购买时的支付方式",
"You have successfully completed the payment": "支付成功",
"You have successfully recharged": "您已成功充值",
"Your current balance is": "您现在的余额为",
"please wait for a few seconds...": "请稍后...",
"the current state is": "当前状态为"
},
@ -689,6 +694,8 @@
"Edit Product": "编辑商品",
"Image": "图片",
"Image - Tooltip": "商品图片",
"Is recharge": "充值",
"Is recharge - Tooltip": "当前商品是否为充值商品",
"New Product": "添加商品",
"Pay": "支付方式",
"PayPal": "PayPal",
@ -1080,6 +1087,8 @@
"Address line": "地址",
"Affiliation": "工作单位",
"Affiliation - Tooltip": "工作单位,如公司、组织名称",
"Balance": "余额",
"Balance - Tooltip": "用户的余额",
"Bio": "自我介绍",
"Bio - Tooltip": "用户的自我介绍",
"Birthday": "生日",
@ -1130,6 +1139,8 @@
"Managed accounts": "托管账户",
"Modify password...": "编辑密码...",
"Multi-factor authentication": "多因素认证",
"Need update password": "需要更新密码",
"Need update password - Tooltip": "强制用户在登录后更新密码",
"New Email": "新邮箱",
"New Password": "新密码",
"New User": "添加用户",

View File

@ -88,6 +88,7 @@ class AccountTable extends React.Component {
{name: "Gender", label: i18next.t("user:Gender")},
{name: "Birthday", label: i18next.t("user:Birthday")},
{name: "Education", label: i18next.t("user:Education")},
{name: "Balance", label: i18next.t("user:Balance")},
{name: "Score", label: i18next.t("user:Score")},
{name: "Karma", label: i18next.t("user:Karma")},
{name: "Ranking", label: i18next.t("user:Ranking")},
@ -102,6 +103,7 @@ class AccountTable extends React.Component {
{name: "Is admin", label: i18next.t("user:Is admin")},
{name: "Is forbidden", label: i18next.t("user:Is forbidden")},
{name: "Is deleted", label: i18next.t("user:Is deleted")},
{name: "Need update password", label: i18next.t("user:Need update password")},
{name: "Multi-factor authentication", label: i18next.t("user:Multi-factor authentication")},
{name: "WebAuthn credentials", label: i18next.t("user:WebAuthn credentials")},
{name: "Managed accounts", label: i18next.t("user:Managed accounts")},