mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-22 05:53:50 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
98fb9f25b0 | |||
cc456f265f | |||
7058a34f87 | |||
8e6755845f | |||
967fa4be68 | |||
805cf20d04 |
@ -143,5 +143,6 @@ func (c *ApiController) DeleteGroup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(wrapActionResponse(object.DeleteGroup(&group)))
|
c.Data["json"] = wrapActionResponse(object.DeleteGroup(&group))
|
||||||
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,15 @@ func (c *ApiController) GetOrganizations() {
|
|||||||
|
|
||||||
c.Data["json"] = maskedOrganizations
|
c.Data["json"] = maskedOrganizations
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
isGlobalAdmin := c.IsGlobalAdmin()
|
||||||
|
if !isGlobalAdmin {
|
||||||
|
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.ResponseOk(maskedOrganizations)
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetOrganizationCount(owner, field, value)
|
count, err := object.GetOrganizationCount(owner, field, value)
|
||||||
@ -63,6 +72,7 @@ func (c *ApiController) GetOrganizations() {
|
|||||||
|
|
||||||
c.ResponseOk(organizations, paginator.Nums())
|
c.ResponseOk(organizations, paginator.Nums())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrganization ...
|
// GetOrganization ...
|
||||||
@ -74,14 +84,13 @@ func (c *ApiController) GetOrganizations() {
|
|||||||
// @router /get-organization [get]
|
// @router /get-organization [get]
|
||||||
func (c *ApiController) GetOrganization() {
|
func (c *ApiController) GetOrganization() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
maskedOrganization, err := object.GetMaskedOrganization(object.GetOrganization(id))
|
maskedOrganization, err := object.GetMaskedOrganization(object.GetOrganization(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = maskedOrganization
|
c.ResponseOk(maskedOrganization)
|
||||||
c.ServeJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrganization ...
|
// UpdateOrganization ...
|
||||||
|
@ -90,7 +90,7 @@ func (c *ApiController) GetUsers() {
|
|||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
if groupId != "" {
|
if groupId != "" {
|
||||||
maskedUsers, err := object.GetMaskedUsers(object.GetUsersByGroup(groupId))
|
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -528,3 +528,34 @@ func (c *ApiController) GetUserCount() {
|
|||||||
c.Data["json"] = count
|
c.Data["json"] = count
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddUserkeys
|
||||||
|
// @Title AddUserkeys
|
||||||
|
// @router /add-user-keys [post]
|
||||||
|
// @Tag User API
|
||||||
|
func (c *ApiController) AddUserkeys() {
|
||||||
|
var user object.User
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdmin := c.IsAdmin()
|
||||||
|
affected, err := object.AddUserkeys(&user, isAdmin)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(affected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) RemoveUserFromGroup() {
|
||||||
|
owner := c.Ctx.Request.Form.Get("owner")
|
||||||
|
name := c.Ctx.Request.Form.Get("name")
|
||||||
|
groupId := c.Ctx.Request.Form.Get("groupId")
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupId))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -59,6 +59,7 @@ require (
|
|||||||
github.com/tealeg/xlsx v1.0.5
|
github.com/tealeg/xlsx v1.0.5
|
||||||
github.com/thanhpk/randstr v1.0.4
|
github.com/thanhpk/randstr v1.0.4
|
||||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||||
|
github.com/xorm-io/builder v0.3.13 // indirect
|
||||||
github.com/xorm-io/core v0.7.4
|
github.com/xorm-io/core v0.7.4
|
||||||
github.com/xorm-io/xorm v1.1.6
|
github.com/xorm-io/xorm v1.1.6
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
@ -353,7 +353,7 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
|
|||||||
|
|
||||||
allowed := true
|
allowed := true
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
if !permission.IsEnabled || len(permission.Users) == 0 {
|
if !permission.IsEnabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -32,7 +33,7 @@ type Group struct {
|
|||||||
Manager string `xorm:"varchar(100)" json:"manager"`
|
Manager string `xorm:"varchar(100)" json:"manager"`
|
||||||
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
|
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
ParentGroupId string `xorm:"varchar(100)" json:"parentGroupId"`
|
ParentId string `xorm:"varchar(100)" json:"parentId"`
|
||||||
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
|
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
|
||||||
Users *[]string `xorm:"-" json:"users"`
|
Users *[]string `xorm:"-" json:"users"`
|
||||||
|
|
||||||
@ -158,11 +159,45 @@ func AddGroups(groups []*Group) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteGroup(group *Group) (bool, error) {
|
func DeleteGroup(group *Group) (bool, error) {
|
||||||
affected, err := adapter.Engine.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
|
_, err := adapter.Engine.Get(group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if count, err := adapter.Engine.Where("parent_id = ?", group.Id).Count(&Group{}); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if count > 0 {
|
||||||
|
return false, errors.New("group has children group")
|
||||||
|
}
|
||||||
|
|
||||||
|
if count, err := GetGroupUserCount(group.GetId(), "", ""); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if count > 0 {
|
||||||
|
return false, errors.New("group has users")
|
||||||
|
}
|
||||||
|
|
||||||
|
session := adapter.Engine.NewSession()
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
if err := session.Begin(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := session.Delete(&UserGroupRelation{GroupId: group.Id}); err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := session.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := session.Commit(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +205,11 @@ func (group *Group) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", group.Owner, group.Name)
|
return fmt.Sprintf("%s/%s", group.Owner, group.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertToTreeData(groups []*Group, parentGroupId string) []*Group {
|
func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||||
treeData := []*Group{}
|
treeData := []*Group{}
|
||||||
|
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
if group.ParentGroupId == parentGroupId {
|
if group.ParentId == parentId {
|
||||||
node := &Group{
|
node := &Group{
|
||||||
Title: group.DisplayName,
|
Title: group.DisplayName,
|
||||||
Key: group.Name,
|
Key: group.Name,
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/cred"
|
"github.com/casdoor/casdoor/cred"
|
||||||
"github.com/casdoor/casdoor/i18n"
|
"github.com/casdoor/casdoor/i18n"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/xorm-io/builder"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -75,12 +76,19 @@ func GetOrganizationCount(owner, field, value string) (int64, error) {
|
|||||||
return session.Count(&Organization{})
|
return session.Count(&Organization{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOrganizations(owner string) ([]*Organization, error) {
|
func GetOrganizations(owner string, name ...string) ([]*Organization, error) {
|
||||||
organizations := []*Organization{}
|
organizations := []*Organization{}
|
||||||
|
if name != nil && len(name) > 0 {
|
||||||
|
err := adapter.Engine.Desc("created_time").Where(builder.In("name", name)).Find(&organizations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
err := adapter.Engine.Desc("created_time").Find(&organizations, &Organization{Owner: owner})
|
err := adapter.Engine.Desc("created_time").Find(&organizations, &Organization{Owner: owner})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return organizations, nil
|
return organizations, nil
|
||||||
}
|
}
|
||||||
|
@ -628,7 +628,12 @@ func GetPasswordToken(application *Application, username string, password string
|
|||||||
ErrorDescription: "the user does not exist",
|
ErrorDescription: "the user does not exist",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
msg := CheckPassword(user, password, "en")
|
var msg string
|
||||||
|
if user.Ldap != "" {
|
||||||
|
msg = checkLdapUserPassword(user, password, "en")
|
||||||
|
} else {
|
||||||
|
msg = CheckPassword(user, password, "en")
|
||||||
|
}
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
|
@ -78,6 +78,8 @@ type User struct {
|
|||||||
Hash string `xorm:"varchar(100)" json:"hash"`
|
Hash string `xorm:"varchar(100)" json:"hash"`
|
||||||
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
||||||
Groups []string `xorm:"varchar(1000)" json:"groups"`
|
Groups []string `xorm:"varchar(1000)" json:"groups"`
|
||||||
|
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
|
||||||
|
SecretKey string `xorm:"varchar(100)" json:"secretKey"`
|
||||||
|
|
||||||
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
||||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||||
@ -223,14 +225,7 @@ func GetUserCount(owner, field, value string, groupId string) (int64, error) {
|
|||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
|
|
||||||
if groupId != "" {
|
if groupId != "" {
|
||||||
group, err := GetGroup(groupId)
|
return GetGroupUserCount(groupId, field, value)
|
||||||
if group == nil || err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
// users count in group
|
|
||||||
return adapter.Engine.Table("user_group_relation").Join("INNER", "user AS u", "user_group_relation.user_id = u.id").
|
|
||||||
Where("user_group_relation.group_id = ?", group.Id).
|
|
||||||
Count(&UserGroupRelation{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return session.Count(&User{})
|
return session.Count(&User{})
|
||||||
@ -274,20 +269,7 @@ func GetPaginationUsers(owner string, offset, limit int, field, value, sortField
|
|||||||
users := []*User{}
|
users := []*User{}
|
||||||
|
|
||||||
if groupId != "" {
|
if groupId != "" {
|
||||||
group, err := GetGroup(groupId)
|
return GetPaginationGroupUsers(groupId, offset, limit, field, value, sortField, sortOrder)
|
||||||
if group == nil || err != nil {
|
|
||||||
return []*User{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
session := adapter.Engine.Prepare()
|
|
||||||
if offset != -1 && limit != -1 {
|
|
||||||
session.Limit(limit, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = session.Table("user_group_relation").Join("INNER", "user AS u", "user_group_relation.user_id = u.id").
|
|
||||||
Where("user_group_relation.group_id = ?", group.Id).
|
|
||||||
Find(&users)
|
|
||||||
return users, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
@ -298,23 +280,6 @@ func GetPaginationUsers(owner string, offset, limit int, field, value, sortField
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUsersByGroup(groupId string) ([]*User, error) {
|
|
||||||
group, err := GetGroup(groupId)
|
|
||||||
if group == nil || err != nil {
|
|
||||||
return []*User{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
users := []*User{}
|
|
||||||
err = adapter.Engine.Table("user_group_relation").Join("INNER", "user AS u", "user_group_relation.user_id = u.id").
|
|
||||||
Where("user_group_relation.group_id = ?", group.Id).
|
|
||||||
Find(&users)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUser(owner string, name string) (*User, error) {
|
func getUser(owner string, name string) (*User, error) {
|
||||||
if owner == "" || name == "" {
|
if owner == "" || name == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -422,6 +387,23 @@ func GetUserByUserId(owner string, userId string) (*User, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserByAccessKey(accessKey string) (*User, error) {
|
||||||
|
if accessKey == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
user := User{AccessKey: accessKey}
|
||||||
|
existed, err := adapter.Engine.Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &user, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetUser(id string) (*User, error) {
|
func GetUser(id string) (*User, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
return getUser(owner, name)
|
return getUser(owner, name)
|
||||||
@ -526,7 +508,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
"owner", "display_name", "avatar",
|
"owner", "display_name", "avatar",
|
||||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||||
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
||||||
"signin_wrong_times", "last_signin_wrong_time", "groups",
|
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "secret_key",
|
||||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||||
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
||||||
@ -555,7 +537,7 @@ func updateUser(oldUser, user *User, columns []string) (int64, error) {
|
|||||||
session.Begin()
|
session.Begin()
|
||||||
|
|
||||||
if util.ContainsString(columns, "groups") {
|
if util.ContainsString(columns, "groups") {
|
||||||
affected, err := updateGroupRelation(session, user)
|
affected, err := updateUserGroupRelation(session, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.Rollback()
|
session.Rollback()
|
||||||
return affected, err
|
return affected, err
|
||||||
@ -744,6 +726,11 @@ func DeleteUser(user *User) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
affected, err = deleteRelationByUser(user.Id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -928,3 +915,14 @@ func (user *User) GetPreferMfa(masked bool) *MfaProps {
|
|||||||
return user.MultiFactorAuths[0]
|
return user.MultiFactorAuths[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddUserkeys(user *User, isAdmin bool) (bool, error) {
|
||||||
|
if user == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user.AccessKey = util.GenerateId()
|
||||||
|
user.SecretKey = util.GenerateId()
|
||||||
|
|
||||||
|
return UpdateUser(user.GetId(), user, []string{}, isAdmin)
|
||||||
|
}
|
||||||
|
@ -2,7 +2,10 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/xorm-io/core"
|
||||||
"github.com/xorm-io/xorm"
|
"github.com/xorm-io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,9 +17,7 @@ type UserGroupRelation struct {
|
|||||||
UpdatedTime string `xorm:"updated" json:"updatedTime"`
|
UpdatedTime string `xorm:"updated" json:"updatedTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
func updateUserGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
||||||
groupIds := user.Groups
|
|
||||||
|
|
||||||
physicalGroupCount, err := session.Where("type = ?", "Physical").In("id", user.Groups).Count(Group{})
|
physicalGroupCount, err := session.Where("type = ?", "Physical").In("id", user.Groups).Count(Group{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -26,12 +27,12 @@ func updateGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
groups := []*Group{}
|
groups := []*Group{}
|
||||||
err = session.In("id", groupIds).Find(&groups)
|
err = session.In("id", user.Groups).Find(&groups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if len(groups) == 0 || len(groups) != len(groupIds) {
|
if len(groups) != len(user.Groups) {
|
||||||
return 0, nil
|
return 0, errors.New("group not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = session.Delete(&UserGroupRelation{UserId: user.Id})
|
_, err = session.Delete(&UserGroupRelation{UserId: user.Id})
|
||||||
@ -43,6 +44,9 @@ func updateGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
|||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
relations = append(relations, &UserGroupRelation{UserId: user.Id, GroupId: group.Id})
|
relations = append(relations, &UserGroupRelation{UserId: user.Id, GroupId: group.Id})
|
||||||
}
|
}
|
||||||
|
if len(relations) == 0 {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
_, err = session.Insert(relations)
|
_, err = session.Insert(relations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -50,3 +54,104 @@ func updateGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
|||||||
|
|
||||||
return 1, nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
|
||||||
|
user, err := getUser(owner, name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := []string{}
|
||||||
|
for _, group := range user.Groups {
|
||||||
|
if group != groupId {
|
||||||
|
groups = append(groups, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user.Groups = groups
|
||||||
|
|
||||||
|
_, err = UpdateUser(util.GetId(owner, name), user, []string{"groups"}, false)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteUserGroupRelation(session *xorm.Session, userId, groupId string) (int64, error) {
|
||||||
|
affected, err := session.ID(core.PK{userId, groupId}).Delete(&UserGroupRelation{})
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteRelationByUser(id string) (int64, error) {
|
||||||
|
affected, err := adapter.Engine.Delete(&UserGroupRelation{UserId: id})
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupUserCount(id string, field, value string) (int64, error) {
|
||||||
|
group, err := GetGroup(id)
|
||||||
|
if group == nil || err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if field == "" && value == "" {
|
||||||
|
return adapter.Engine.Count(UserGroupRelation{GroupId: group.Id})
|
||||||
|
} else {
|
||||||
|
return adapter.Engine.Table("user").
|
||||||
|
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
|
||||||
|
Where("r.group_id = ?", group.Id).
|
||||||
|
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||||
|
Count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationGroupUsers(id string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||||
|
group, err := GetGroup(id)
|
||||||
|
if group == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []*User{}
|
||||||
|
session := adapter.Engine.Table("user").
|
||||||
|
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
|
||||||
|
Where("r.group_id = ?", group.Id)
|
||||||
|
|
||||||
|
if offset != -1 && limit != -1 {
|
||||||
|
session.Limit(limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field != "" && value != "" {
|
||||||
|
session = session.And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sortField == "" || sortOrder == "" {
|
||||||
|
sortField = "created_time"
|
||||||
|
}
|
||||||
|
if sortOrder == "ascend" {
|
||||||
|
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||||
|
} else {
|
||||||
|
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupUsers(id string) ([]*User, error) {
|
||||||
|
group, err := GetGroup(id)
|
||||||
|
if group == nil || err != nil {
|
||||||
|
return []*User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []*User{}
|
||||||
|
err = adapter.Engine.Table("user_group_relation").Join("INNER", []string{"user", "u"}, "user_group_relation.user_id = u.id").
|
||||||
|
Where("user_group_relation.group_id = ?", group.Id).
|
||||||
|
Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
@ -224,7 +224,7 @@ func GetVerifyType(username string) (verificationCodeType string) {
|
|||||||
if strings.Contains(username, "@") {
|
if strings.Contains(username, "@") {
|
||||||
return VerifyTypeEmail
|
return VerifyTypeEmail
|
||||||
} else {
|
} else {
|
||||||
return VerifyTypeEmail
|
return VerifyTypePhone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
pp/paypal.go
10
pp/paypal.go
@ -59,12 +59,12 @@ func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
bm := make(gopay.BodyMap)
|
bm := make(gopay.BodyMap)
|
||||||
bm.Set("intent", "CAPTURE")
|
bm.Set("intent", "CAPTURE")
|
||||||
bm.Set("purchase_units", pus)
|
bm.Set("purchase_units", pus)
|
||||||
bm.SetBodyMap("payment_source", func(b1 gopay.BodyMap) {
|
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
||||||
b1.SetBodyMap("paypal", func(b2 gopay.BodyMap) {
|
b.Set("brand_name", "Casdoor")
|
||||||
b2.Set("brand_name", "Casdoor")
|
b.Set("locale", "en-PT")
|
||||||
b2.Set("return_url", returnUrl)
|
b.Set("return_url", returnUrl)
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
|
@ -28,6 +28,8 @@ import (
|
|||||||
type Object struct {
|
type Object struct {
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUsername(ctx *context.Context) (username string) {
|
func getUsername(ctx *context.Context) (username string) {
|
||||||
@ -43,6 +45,9 @@ func getUsername(ctx *context.Context) (username string) {
|
|||||||
username = getUsernameByClientIdSecret(ctx)
|
username = getUsernameByClientIdSecret(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if username == "" {
|
||||||
|
username = getUsernameByKeys(ctx)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +103,30 @@ func getObject(ctx *context.Context) (string, string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getKeys(ctx *context.Context) (string, string) {
|
||||||
|
method := ctx.Request.Method
|
||||||
|
|
||||||
|
if method == http.MethodGet {
|
||||||
|
accessKey := ctx.Input.Query("accesskey")
|
||||||
|
secretKey := ctx.Input.Query("secretkey")
|
||||||
|
return accessKey, secretKey
|
||||||
|
} else {
|
||||||
|
body := ctx.Input.RequestBody
|
||||||
|
|
||||||
|
if len(body) == 0 {
|
||||||
|
return ctx.Request.Form.Get("accesskey"), ctx.Request.Form.Get("secretkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj Object
|
||||||
|
err := json.Unmarshal(body, &obj)
|
||||||
|
if err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj.AccessKey, obj.SecretKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func willLog(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
func willLog(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||||
if subOwner == "anonymous" && subName == "anonymous" && method == "GET" && (urlPath == "/api/get-account" || urlPath == "/api/get-app-login") && objOwner == "" && objName == "" {
|
if subOwner == "anonymous" && subName == "anonymous" && method == "GET" && (urlPath == "/api/get-account" || urlPath == "/api/get-app-login") && objOwner == "" && objName == "" {
|
||||||
return false
|
return false
|
||||||
|
@ -84,6 +84,18 @@ func getUsernameByClientIdSecret(ctx *context.Context) string {
|
|||||||
return fmt.Sprintf("app/%s", application.Name)
|
return fmt.Sprintf("app/%s", application.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUsernameByKeys(ctx *context.Context) string {
|
||||||
|
accessKey, secretKey := getKeys(ctx)
|
||||||
|
user, err := object.GetUserByAccessKey(accessKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if user != nil && secretKey == user.SecretKey {
|
||||||
|
return user.GetId()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func getSessionUser(ctx *context.Context) string {
|
func getSessionUser(ctx *context.Context) string {
|
||||||
user := ctx.Input.CruSession.Get("username")
|
user := ctx.Input.CruSession.Get("username")
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
@ -73,9 +73,11 @@ func initAPI() {
|
|||||||
beego.Router("/api/get-user-count", &controllers.ApiController{}, "GET:GetUserCount")
|
beego.Router("/api/get-user-count", &controllers.ApiController{}, "GET:GetUserCount")
|
||||||
beego.Router("/api/get-user", &controllers.ApiController{}, "GET:GetUser")
|
beego.Router("/api/get-user", &controllers.ApiController{}, "GET:GetUser")
|
||||||
beego.Router("/api/update-user", &controllers.ApiController{}, "POST:UpdateUser")
|
beego.Router("/api/update-user", &controllers.ApiController{}, "POST:UpdateUser")
|
||||||
|
beego.Router("/api/add-user-keys", &controllers.ApiController{}, "POST:AddUserkeys")
|
||||||
beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser")
|
beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser")
|
||||||
beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser")
|
beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser")
|
||||||
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
|
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
|
||||||
|
beego.Router("/api/remove-user-from-group", &controllers.ApiController{}, "POST:RemoveUserFromGroup")
|
||||||
|
|
||||||
beego.Router("/api/get-groups", &controllers.ApiController{}, "GET:GetGroups")
|
beego.Router("/api/get-groups", &controllers.ApiController{}, "GET:GetGroups")
|
||||||
beego.Router("/api/get-group", &controllers.ApiController{}, "GET:GetGroup")
|
beego.Router("/api/get-group", &controllers.ApiController{}, "GET:GetGroup")
|
||||||
|
@ -131,7 +131,7 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
if (uri === "/") {
|
if (uri === "/") {
|
||||||
this.setState({selectedMenuKey: "/"});
|
this.setState({selectedMenuKey: "/"});
|
||||||
} else if (uri.includes("/organizations")) {
|
} else if (uri.includes("/organizations") || uri.includes("/trees")) {
|
||||||
this.setState({selectedMenuKey: "/organizations"});
|
this.setState({selectedMenuKey: "/organizations"});
|
||||||
} else if (uri.includes("/users")) {
|
} else if (uri.includes("/users")) {
|
||||||
this.setState({selectedMenuKey: "/users"});
|
this.setState({selectedMenuKey: "/users"});
|
||||||
@ -410,15 +410,13 @@ class App extends Component {
|
|||||||
|
|
||||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
||||||
|
|
||||||
if (Setting.isAdminUser(this.state.account)) {
|
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||||
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
||||||
"/organizations"));
|
"/organizations"));
|
||||||
|
|
||||||
res.push(Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>,
|
res.push(Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>,
|
||||||
"/groups"));
|
"/groups"));
|
||||||
}
|
|
||||||
|
|
||||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
|
||||||
res.push(Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>,
|
res.push(Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>,
|
||||||
"/users"
|
"/users"
|
||||||
));
|
));
|
||||||
@ -560,8 +558,8 @@ class App extends Component {
|
|||||||
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} onChangeTheme={this.setTheme} {...props} />)} />
|
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} onChangeTheme={this.setTheme} {...props} />)} />
|
||||||
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/group-tree/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
|
<Route exact path="/trees/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/group-tree/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
|
<Route exact path="/trees/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/groups" render={(props) => this.renderLoginIfNotLoggedIn(<GroupListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/groups" render={(props) => this.renderLoginIfNotLoggedIn(<GroupListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
||||||
@ -632,7 +630,7 @@ class App extends Component {
|
|||||||
|
|
||||||
isWithoutCard() {
|
isWithoutCard() {
|
||||||
return Setting.isMobile() || window.location.pathname === "/chat" ||
|
return Setting.isMobile() || window.location.pathname === "/chat" ||
|
||||||
window.location.pathname.startsWith("/group-tree");
|
window.location.pathname.startsWith("/trees");
|
||||||
}
|
}
|
||||||
|
|
||||||
renderContent() {
|
renderContent() {
|
||||||
|
@ -44,6 +44,11 @@ class GroupEditPage extends React.Component {
|
|||||||
GroupBackend.getGroup(this.state.organizationName, this.state.groupName)
|
GroupBackend.getGroup(this.state.organizationName, this.state.groupName)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
if (res.data === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
group: res.data,
|
group: res.data,
|
||||||
});
|
});
|
||||||
@ -171,8 +176,8 @@ class GroupEditPage extends React.Component {
|
|||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select style={{width: "100%"}}
|
<Select style={{width: "100%"}}
|
||||||
options={this.getParentIdOptions()}
|
options={this.getParentIdOptions()}
|
||||||
value={this.state.group.parentGroupId} onChange={(value => {
|
value={this.state.group.parentId} onChange={(value => {
|
||||||
this.updateGroupField("parentGroupId", value);
|
this.updateGroupField("parentId", value);
|
||||||
}
|
}
|
||||||
)} />
|
)} />
|
||||||
</Col>
|
</Col>
|
||||||
@ -193,7 +198,7 @@ class GroupEditPage extends React.Component {
|
|||||||
|
|
||||||
submitGroupEdit(willExist) {
|
submitGroupEdit(willExist) {
|
||||||
const group = Setting.deepCopy(this.state.group);
|
const group = Setting.deepCopy(this.state.group);
|
||||||
group["isTopGroup"] = this.state.organizations.some((organization) => organization.name === group.parentGroupId);
|
group["isTopGroup"] = this.state.organizations.some((organization) => organization.name === group.parentId);
|
||||||
|
|
||||||
GroupBackend.updateGroup(this.state.organizationName, this.state.groupName, group)
|
GroupBackend.updateGroup(this.state.organizationName, this.state.groupName, group)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -56,7 +56,7 @@ class GroupListPage extends BaseListPage {
|
|||||||
updatedTime: moment().format(),
|
updatedTime: moment().format(),
|
||||||
displayName: `New Group - ${randomName}`,
|
displayName: `New Group - ${randomName}`,
|
||||||
type: "Virtual",
|
type: "Virtual",
|
||||||
parentGroupId: this.props.account.owner,
|
parentId: this.props.account.owner,
|
||||||
isTopGroup: true,
|
isTopGroup: true,
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
};
|
};
|
||||||
@ -96,7 +96,7 @@ class GroupListPage extends BaseListPage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTable(groups) {
|
renderTable(data) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Name"),
|
title: i18next.t("general:Name"),
|
||||||
@ -174,15 +174,15 @@ class GroupListPage extends BaseListPage {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("group:Parent group"),
|
title: i18next.t("group:Parent group"),
|
||||||
dataIndex: "parentGroupId",
|
dataIndex: "parentId",
|
||||||
key: "parentGroupId",
|
key: "parentId",
|
||||||
width: "110px",
|
width: "110px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("parentGroupId"),
|
...this.getColumnSearchProps("parentId"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
if (record.isTopGroup) {
|
if (record.isTopGroup) {
|
||||||
return <Link to={`/organizations/${record.parentGroupId}`}>
|
return <Link to={`/organizations/${record.parentId}`}>
|
||||||
{record.parentGroupId}
|
{record.parentId}
|
||||||
</Link>;
|
</Link>;
|
||||||
}
|
}
|
||||||
const parentGroup = this.state.groups.find((group) => group.id === text);
|
const parentGroup = this.state.groups.find((group) => group.id === text);
|
||||||
@ -201,10 +201,12 @@ class GroupListPage extends BaseListPage {
|
|||||||
width: "170px",
|
width: "170px",
|
||||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
|
const haveChildren = this.state.groups.find((group) => group.parentId === record.id) !== undefined;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/groups/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/groups/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<PopconfirmModal
|
<PopconfirmModal
|
||||||
|
disabled={haveChildren}
|
||||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteGroup(index)}
|
onConfirm={() => this.deleteGroup(index)}
|
||||||
>
|
>
|
||||||
@ -224,7 +226,7 @@ class GroupListPage extends BaseListPage {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={groups} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
<Table scroll={{x: "max-content"}} columns={columns} dataSource={data} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||||
title={() => (
|
title={() => (
|
||||||
<div>
|
<div>
|
||||||
{i18next.t("general:Groups")}
|
{i18next.t("general:Groups")}
|
||||||
|
@ -27,11 +27,12 @@ class GroupTreePage extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
|
owner: Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner,
|
||||||
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
groupName: this.props.match?.params.groupName,
|
groupName: this.props.match?.params.groupName,
|
||||||
groupId: "",
|
groupId: undefined,
|
||||||
treeData: [],
|
treeData: [],
|
||||||
selectedKeys: [],
|
selectedKeys: [this.props.match?.params.groupName],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,9 +53,9 @@ class GroupTreePage extends React.Component {
|
|||||||
getTreeData() {
|
getTreeData() {
|
||||||
GroupBackend.getGroups(this.state.organizationName, true).then((res) => {
|
GroupBackend.getGroups(this.state.organizationName, true).then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const tree = res.data;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
treeData: tree,
|
treeData: res.data,
|
||||||
|
groupId: this.findNodeId({children: res.data}, this.state.groupName),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", res.msg);
|
||||||
@ -62,6 +63,21 @@ class GroupTreePage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findNodeId(node, targetName) {
|
||||||
|
if (node.key === targetName) {
|
||||||
|
return node.id;
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
|
const result = this.findNodeId(node.children[i], targetName);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
setTreeTitle(treeData) {
|
setTreeTitle(treeData) {
|
||||||
const haveChildren = Array.isArray(treeData.children) && treeData.children.length > 0;
|
const haveChildren = Array.isArray(treeData.children) && treeData.children.length > 0;
|
||||||
const isSelected = this.state.groupName === treeData.key;
|
const isSelected = this.state.groupName === treeData.key;
|
||||||
@ -121,6 +137,7 @@ class GroupTreePage extends React.Component {
|
|||||||
this.props.history.push(`/groups/${this.state.organizationName}/${treeData.key}`);
|
this.props.history.push(`/groups/${this.state.organizationName}/${treeData.key}`);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{!haveChildren &&
|
||||||
<DeleteOutlined
|
<DeleteOutlined
|
||||||
style={{
|
style={{
|
||||||
visibility: "visible",
|
visibility: "visible",
|
||||||
@ -155,6 +172,7 @@ class GroupTreePage extends React.Component {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</Space>,
|
</Space>,
|
||||||
@ -185,7 +203,7 @@ class GroupTreePage extends React.Component {
|
|||||||
groupName: info.node.key,
|
groupName: info.node.key,
|
||||||
groupId: info.node.id,
|
groupId: info.node.id,
|
||||||
});
|
});
|
||||||
this.props.history.push(`/group-tree/${this.state.organizationName}/${info.node.key}`);
|
this.props.history.push(`/trees/${this.state.organizationName}/${info.node.key}`);
|
||||||
};
|
};
|
||||||
const onExpand = (expandedKeysValue) => {
|
const onExpand = (expandedKeysValue) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -203,6 +221,7 @@ class GroupTreePage extends React.Component {
|
|||||||
blockNode={true}
|
blockNode={true}
|
||||||
defaultSelectedKeys={[this.state.groupName]}
|
defaultSelectedKeys={[this.state.groupName]}
|
||||||
defaultExpandAll={true}
|
defaultExpandAll={true}
|
||||||
|
selectedKeys={this.state.selectedKeys}
|
||||||
expandedKeys={this.state.expandedKeys}
|
expandedKeys={this.state.expandedKeys}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
onExpand={onExpand}
|
onExpand={onExpand}
|
||||||
@ -213,16 +232,20 @@ class GroupTreePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderOrganizationSelect() {
|
renderOrganizationSelect() {
|
||||||
return <OrganizationSelect
|
if (Setting.isAdminUser(this.props.account)) {
|
||||||
|
return (
|
||||||
|
<OrganizationSelect
|
||||||
initValue={this.state.organizationName}
|
initValue={this.state.organizationName}
|
||||||
style={{width: "100%"}}
|
style={{width: "100%"}}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
organizationName: value,
|
organizationName: value,
|
||||||
});
|
});
|
||||||
this.props.history.push(`/group-tree/${value}`);
|
this.props.history.push(`/trees/${value}`);
|
||||||
}}
|
}}
|
||||||
/>;
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newGroup(isRoot) {
|
newGroup(isRoot) {
|
||||||
@ -234,7 +257,7 @@ class GroupTreePage extends React.Component {
|
|||||||
updatedTime: moment().format(),
|
updatedTime: moment().format(),
|
||||||
displayName: `New Group - ${randomName}`,
|
displayName: `New Group - ${randomName}`,
|
||||||
type: "Virtual",
|
type: "Virtual",
|
||||||
parentGroupId: isRoot ? this.state.organizationName : this.state.groupId,
|
parentId: isRoot ? this.state.organizationName : this.state.groupId,
|
||||||
isTopGroup: isRoot,
|
isTopGroup: isRoot,
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
};
|
};
|
||||||
@ -267,25 +290,25 @@ class GroupTreePage extends React.Component {
|
|||||||
<Row>
|
<Row>
|
||||||
<Col span={5}>
|
<Col span={5}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} style={{textAlign: "left"}}>
|
<Col span={24} style={{textAlign: "center"}}>
|
||||||
{this.renderOrganizationSelect()}
|
{this.renderOrganizationSelect()}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} style={{marginTop: "10px", textAlign: "left"}}>
|
<Col span={24} style={{marginTop: "10px"}}>
|
||||||
<Button
|
<Button size={"small"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
selectedKeys: [],
|
||||||
groupName: null,
|
groupName: null,
|
||||||
groupId: null,
|
groupId: undefined,
|
||||||
});
|
});
|
||||||
this.props.history.push(`/group-tree/${this.state.organizationName}`);
|
this.props.history.push(`/trees/${this.state.organizationName}`);
|
||||||
}}>
|
}}
|
||||||
{i18next.t("group:Show organization users")}
|
|
||||||
</Button>
|
|
||||||
<Button size={"small"} type={"primary"} style={{marginLeft: "10px"}}
|
|
||||||
onClick={() => this.addGroup(true)}
|
|
||||||
>
|
>
|
||||||
|
{i18next.t("group:Show all")}
|
||||||
|
</Button>
|
||||||
|
<Button size={"small"} type={"primary"} style={{marginLeft: "10px"}} onClick={() => this.addGroup(true)}>
|
||||||
{i18next.t("general:Add")}
|
{i18next.t("general:Add")}
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
@ -301,7 +324,8 @@ class GroupTreePage extends React.Component {
|
|||||||
organizationName={this.state.organizationName}
|
organizationName={this.state.organizationName}
|
||||||
groupName={this.state.groupName}
|
groupName={this.state.groupName}
|
||||||
groupId={this.state.groupId}
|
groupId={this.state.groupId}
|
||||||
{...this.props} />
|
{...this.props}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +49,9 @@ class OrganizationEditPage extends React.Component {
|
|||||||
|
|
||||||
getOrganization() {
|
getOrganization() {
|
||||||
OrganizationBackend.getOrganization("admin", this.state.organizationName)
|
OrganizationBackend.getOrganization("admin", this.state.organizationName)
|
||||||
.then((organization) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
const organization = res.data;
|
||||||
if (organization === null) {
|
if (organization === null) {
|
||||||
this.props.history.push("/404");
|
this.props.history.push("/404");
|
||||||
return;
|
return;
|
||||||
@ -58,6 +60,9 @@ class OrganizationEditPage extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
organization: organization,
|
organization: organization,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
|
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
|
||||||
{name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
{name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||||
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||||
|
{name: "API key", label: i18next.t("general:API key")},
|
||||||
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
{name: "Groups", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
{name: "Groups", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
@ -227,7 +228,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/group-tree/${record.name}`)}>{i18next.t("general:Groups")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/trees/${record.name}`)}>{i18next.t("general:Groups")}</Button>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}/users`)}>{i18next.t("general:Users")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}/users`)}>{i18next.t("general:Users")}</Button>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/organizations/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/organizations/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<PopconfirmModal
|
<PopconfirmModal
|
||||||
@ -255,7 +256,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
title={() => (
|
title={() => (
|
||||||
<div>
|
<div>
|
||||||
{i18next.t("general:Organizations")}
|
{i18next.t("general:Organizations")}
|
||||||
<Button type="primary" size="small" onClick={this.addOrganization.bind(this)}>{i18next.t("general:Add")}</Button>
|
<Button type="primary" size="small" disabled={!Setting.isAdminUser(this.props.account)} onClick={this.addOrganization.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
loading={this.state.loading}
|
loading={this.state.loading}
|
||||||
|
@ -91,6 +91,17 @@ class UserEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addUserKeys() {
|
||||||
|
UserBackend.addUserKeys(this.state.user)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.getUser();
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getOrganizations() {
|
getOrganizations() {
|
||||||
OrganizationBackend.getOrganizations("admin")
|
OrganizationBackend.getOrganizations("admin")
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -266,6 +277,11 @@ class UserEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isKeysGenerated = false;
|
||||||
|
if (this.state.user.accessKey !== "" && this.state.user.accessKey !== "") {
|
||||||
|
isKeysGenerated = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (accountItem.name === "Organization") {
|
if (accountItem.name === "Organization") {
|
||||||
return (
|
return (
|
||||||
<Row style={{marginTop: "10px"}} >
|
<Row style={{marginTop: "10px"}} >
|
||||||
@ -691,6 +707,37 @@ class UserEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
} else if (accountItem.name === "API key") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:API key"), i18next.t("general:API key - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Access key"), i18next.t("general:Access key - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.accessKey} disabled={true} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Secret key"), i18next.t("general:Secret key - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.user.secretKey} disabled={true} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col span={22} >
|
||||||
|
<Button onClick={() => this.addUserKeys()}>{i18next.t(isKeysGenerated ? "general:update" : "general:generate")}</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
} else if (accountItem.name === "Roles") {
|
} else if (accountItem.name === "Roles") {
|
||||||
return (
|
return (
|
||||||
<Row style={{marginTop: "20px", alignItems: "center"}} >
|
<Row style={{marginTop: "20px", alignItems: "center"}} >
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import {Button, Switch, Table, Upload} from "antd";
|
import {Button, Space, Switch, Table, Upload} from "antd";
|
||||||
import {UploadOutlined} from "@ant-design/icons";
|
import {UploadOutlined} from "@ant-design/icons";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
@ -75,7 +75,7 @@ class UserListPage extends BaseListPage {
|
|||||||
phone: Setting.getRandomNumber(),
|
phone: Setting.getRandomNumber(),
|
||||||
countryCode: this.state.organization.countryCodes?.length > 0 ? this.state.organization.countryCodes[0] : "",
|
countryCode: this.state.organization.countryCodes?.length > 0 ? this.state.organization.countryCodes[0] : "",
|
||||||
address: [],
|
address: [],
|
||||||
groups: this.props.groupId !== undefined ? [this.props.groupId] : [],
|
groups: this.props.groupId ? [this.props.groupId] : [],
|
||||||
affiliation: "Example Inc.",
|
affiliation: "Example Inc.",
|
||||||
tag: "staff",
|
tag: "staff",
|
||||||
region: "",
|
region: "",
|
||||||
@ -124,6 +124,26 @@ class UserListPage extends BaseListPage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeUserFromGroup(i) {
|
||||||
|
const user = this.state.data[i];
|
||||||
|
const group = this.props.groupId;
|
||||||
|
UserBackend.removeUserFromGroup({groupId: group, owner: user.owner, name: user.name})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully removed"));
|
||||||
|
this.setState({
|
||||||
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to remove")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
uploadFile(info) {
|
uploadFile(info) {
|
||||||
const {status, response: res} = info.file;
|
const {status, response: res} = info.file;
|
||||||
if (status === "done") {
|
if (status === "done") {
|
||||||
@ -142,10 +162,14 @@ class UserListPage extends BaseListPage {
|
|||||||
|
|
||||||
getOrganization(organizationName) {
|
getOrganization(organizationName) {
|
||||||
OrganizationBackend.getOrganization("admin", organizationName)
|
OrganizationBackend.getOrganization("admin", organizationName)
|
||||||
.then((organization) => {
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
organization: organization,
|
organization: res.data,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `Failed to get organization: ${res.msg}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,20 +396,30 @@ class UserListPage extends BaseListPage {
|
|||||||
width: "190px",
|
width: "190px",
|
||||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
|
const isTreePage = this.props.groupId !== undefined;
|
||||||
const disabled = (record.owner === this.props.account.owner && record.name === this.props.account.name) || (record.owner === "built-in" && record.name === "admin");
|
const disabled = (record.owner === this.props.account.owner && record.name === this.props.account.name) || (record.owner === "built-in" && record.name === "admin");
|
||||||
return (
|
return (
|
||||||
<div>
|
<Space>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => {
|
<Button size={isTreePage ? "small" : "middle"} type="primary" onClick={() => {
|
||||||
sessionStorage.setItem("userListUrl", window.location.pathname);
|
sessionStorage.setItem("userListUrl", window.location.pathname);
|
||||||
this.props.history.push(`/users/${record.owner}/${record.name}`);
|
this.props.history.push(`/users/${record.owner}/${record.name}`);
|
||||||
}}>{i18next.t("general:Edit")}</Button>
|
}}>{i18next.t("general:Edit")}
|
||||||
|
</Button>
|
||||||
|
{isTreePage ?
|
||||||
|
<PopconfirmModal
|
||||||
|
text={i18next.t("general:remove")}
|
||||||
|
title={i18next.t("general:Sure to remove") + `: ${record.name} ?`}
|
||||||
|
onConfirm={() => this.removeUserFromGroup(index)}
|
||||||
|
disabled={disabled}
|
||||||
|
size="small"
|
||||||
|
/> : null}
|
||||||
<PopconfirmModal
|
<PopconfirmModal
|
||||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||||
onConfirm={() => this.deleteUser(index)}
|
onConfirm={() => this.deleteUser(index)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
size={isTreePage ? "small" : "default"}
|
||||||
</PopconfirmModal>
|
/>
|
||||||
</div>
|
</Space>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -45,6 +45,17 @@ export function getUser(owner, name) {
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addUserKeys(user) {
|
||||||
|
return fetch(`${Setting.ServerUrl}/api/add-user-keys`, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
body: JSON.stringify(user),
|
||||||
|
headers: {
|
||||||
|
"Accept-Language": Setting.getAcceptLanguage(),
|
||||||
|
},
|
||||||
|
}).then(res => res.json());
|
||||||
|
}
|
||||||
|
|
||||||
export function updateUser(owner, name, user) {
|
export function updateUser(owner, name, user) {
|
||||||
const newUser = Setting.deepCopy(user);
|
const newUser = Setting.deepCopy(user);
|
||||||
return fetch(`${Setting.ServerUrl}/api/update-user?id=${owner}/${encodeURIComponent(name)}`, {
|
return fetch(`${Setting.ServerUrl}/api/update-user?id=${owner}/${encodeURIComponent(name)}`, {
|
||||||
@ -211,3 +222,18 @@ export function checkUserPassword(values) {
|
|||||||
body: JSON.stringify(values),
|
body: JSON.stringify(values),
|
||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeUserFromGroup({owner, name, groupId}) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("owner", owner);
|
||||||
|
formData.append("name", name);
|
||||||
|
formData.append("groupId", groupId);
|
||||||
|
return fetch(`${Setting.ServerUrl}/api/remove-user-from-group`, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
"Accept-Language": Setting.getAcceptLanguage(),
|
||||||
|
},
|
||||||
|
}).then(res => res.json());
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@ import i18next from "i18next";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export const PopconfirmModal = (props) => {
|
export const PopconfirmModal = (props) => {
|
||||||
|
const text = props.text ? props.text : i18next.t("general:Delete");
|
||||||
|
const size = props.size ? props.size : "middle";
|
||||||
return (
|
return (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={props.title}
|
title={props.title}
|
||||||
@ -25,7 +27,7 @@ export const PopconfirmModal = (props) => {
|
|||||||
okText={i18next.t("general:OK")}
|
okText={i18next.t("general:OK")}
|
||||||
cancelText={i18next.t("general:Cancel")}
|
cancelText={i18next.t("general:Cancel")}
|
||||||
>
|
>
|
||||||
<Button style={{marginBottom: "10px"}} disabled={props.disabled} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
<Button style={{...props.style}} size={size} disabled={props.disabled} type="primary" danger>{text}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Schließen",
|
"Close": "Schließen",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Erstellte Zeit",
|
"Created time": "Erstellte Zeit",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Standard Anwendung",
|
"Default application": "Standard Anwendung",
|
||||||
"Default application - Tooltip": "Standard-Anwendung für Benutzer, die direkt von der Organisationsseite registriert wurden",
|
"Default application - Tooltip": "Standard-Anwendung für Benutzer, die direkt von der Organisationsseite registriert wurden",
|
||||||
"Default avatar": "Standard-Avatar",
|
"Default avatar": "Standard-Avatar",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Konnte nicht gelöscht werden",
|
"Failed to delete": "Konnte nicht gelöscht werden",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Konnte nicht gespeichert werden",
|
"Failed to save": "Konnte nicht gespeichert werden",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Abonnements",
|
"Subscriptions": "Abonnements",
|
||||||
"Successfully added": "Erfolgreich hinzugefügt",
|
"Successfully added": "Erfolgreich hinzugefügt",
|
||||||
"Successfully deleted": "Erfolgreich gelöscht",
|
"Successfully deleted": "Erfolgreich gelöscht",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Erfolgreich gespeichert",
|
"Successfully saved": "Erfolgreich gespeichert",
|
||||||
"Supported country codes": "Unterstützte Ländercodes",
|
"Supported country codes": "Unterstützte Ländercodes",
|
||||||
"Supported country codes - Tooltip": "Ländercodes, die von der Organisation unterstützt werden. Diese Codes können als Präfix ausgewählt werden, wenn SMS-Verifizierungscodes gesendet werden",
|
"Supported country codes - Tooltip": "Ländercodes, die von der Organisation unterstützt werden. Diese Codes können als Präfix ausgewählt werden, wenn SMS-Verifizierungscodes gesendet werden",
|
||||||
"Sure to delete": "Sicher zu löschen",
|
"Sure to delete": "Sicher zu löschen",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Synchronisieren",
|
"Sync": "Synchronisieren",
|
||||||
"Syncers": "Syncers",
|
"Syncers": "Syncers",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "leere",
|
"empty": "leere",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "Insgesamt {total}"
|
"{total} in total": "Insgesamt {total}"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Close",
|
"Close": "Close",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Created time",
|
"Created time": "Created time",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Default application",
|
"Default application": "Default application",
|
||||||
"Default application - Tooltip": "Default application for users registered directly from the organization page",
|
"Default application - Tooltip": "Default application for users registered directly from the organization page",
|
||||||
"Default avatar": "Default avatar",
|
"Default avatar": "Default avatar",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Failed to delete",
|
"Failed to delete": "Failed to delete",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Failed to save",
|
"Failed to save": "Failed to save",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Subscriptions",
|
"Subscriptions": "Subscriptions",
|
||||||
"Successfully added": "Successfully added",
|
"Successfully added": "Successfully added",
|
||||||
"Successfully deleted": "Successfully deleted",
|
"Successfully deleted": "Successfully deleted",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Successfully saved",
|
"Successfully saved": "Successfully saved",
|
||||||
"Supported country codes": "Supported country codes",
|
"Supported country codes": "Supported country codes",
|
||||||
"Supported country codes - Tooltip": "Country codes supported by the organization. These codes can be selected as a prefix when sending SMS verification codes",
|
"Supported country codes - Tooltip": "Country codes supported by the organization. These codes can be selected as a prefix when sending SMS verification codes",
|
||||||
"Sure to delete": "Sure to delete",
|
"Sure to delete": "Sure to delete",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Sync",
|
"Sync": "Sync",
|
||||||
"Syncers": "Syncers",
|
"Syncers": "Syncers",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "empty",
|
"empty": "empty",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "{total} in total"
|
"{total} in total": "{total} in total"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Cerca",
|
"Close": "Cerca",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Tiempo creado",
|
"Created time": "Tiempo creado",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Aplicación predeterminada",
|
"Default application": "Aplicación predeterminada",
|
||||||
"Default application - Tooltip": "Aplicación predeterminada para usuarios registrados directamente desde la página de la organización",
|
"Default application - Tooltip": "Aplicación predeterminada para usuarios registrados directamente desde la página de la organización",
|
||||||
"Default avatar": "Avatar predeterminado",
|
"Default avatar": "Avatar predeterminado",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "No se pudo eliminar",
|
"Failed to delete": "No se pudo eliminar",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "No se pudo guardar",
|
"Failed to save": "No se pudo guardar",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Suscripciones",
|
"Subscriptions": "Suscripciones",
|
||||||
"Successfully added": "Éxito al agregar",
|
"Successfully added": "Éxito al agregar",
|
||||||
"Successfully deleted": "Éxito en la eliminación",
|
"Successfully deleted": "Éxito en la eliminación",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Guardado exitosamente",
|
"Successfully saved": "Guardado exitosamente",
|
||||||
"Supported country codes": "Códigos de país admitidos",
|
"Supported country codes": "Códigos de país admitidos",
|
||||||
"Supported country codes - Tooltip": "Códigos de país compatibles con la organización. Estos códigos se pueden seleccionar como prefijo al enviar códigos de verificación SMS",
|
"Supported country codes - Tooltip": "Códigos de país compatibles con la organización. Estos códigos se pueden seleccionar como prefijo al enviar códigos de verificación SMS",
|
||||||
"Sure to delete": "Seguro que eliminar",
|
"Sure to delete": "Seguro que eliminar",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Sincronización",
|
"Sync": "Sincronización",
|
||||||
"Syncers": "Sincronizadores",
|
"Syncers": "Sincronizadores",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "vacío",
|
"empty": "vacío",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "{total} en total"
|
"{total} in total": "{total} en total"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Fermer",
|
"Close": "Fermer",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Temps créé",
|
"Created time": "Temps créé",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Application par défaut",
|
"Default application": "Application par défaut",
|
||||||
"Default application - Tooltip": "Application par défaut pour les utilisateurs enregistrés directement depuis la page de l'organisation",
|
"Default application - Tooltip": "Application par défaut pour les utilisateurs enregistrés directement depuis la page de l'organisation",
|
||||||
"Default avatar": "Avatar par défaut",
|
"Default avatar": "Avatar par défaut",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Échec de la suppression",
|
"Failed to delete": "Échec de la suppression",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Échec de sauvegarde",
|
"Failed to save": "Échec de sauvegarde",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Abonnements",
|
"Subscriptions": "Abonnements",
|
||||||
"Successfully added": "Ajouté avec succès",
|
"Successfully added": "Ajouté avec succès",
|
||||||
"Successfully deleted": "Supprimé avec succès",
|
"Successfully deleted": "Supprimé avec succès",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Succès enregistré",
|
"Successfully saved": "Succès enregistré",
|
||||||
"Supported country codes": "Codes de pays pris en charge",
|
"Supported country codes": "Codes de pays pris en charge",
|
||||||
"Supported country codes - Tooltip": "Codes de pays pris en charge par l'organisation. Ces codes peuvent être sélectionnés comme préfixe lors de l'envoi de codes de vérification SMS",
|
"Supported country codes - Tooltip": "Codes de pays pris en charge par l'organisation. Ces codes peuvent être sélectionnés comme préfixe lors de l'envoi de codes de vérification SMS",
|
||||||
"Sure to delete": "Sûr de supprimer",
|
"Sure to delete": "Sûr de supprimer",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Synchronisation",
|
"Sync": "Synchronisation",
|
||||||
"Syncers": "Synchroniseurs",
|
"Syncers": "Synchroniseurs",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "vide",
|
"empty": "vide",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "{total} au total"
|
"{total} in total": "{total} au total"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Tutup",
|
"Close": "Tutup",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Waktu dibuat",
|
"Created time": "Waktu dibuat",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Aplikasi default",
|
"Default application": "Aplikasi default",
|
||||||
"Default application - Tooltip": "Aplikasi default untuk pengguna yang terdaftar langsung dari halaman organisasi",
|
"Default application - Tooltip": "Aplikasi default untuk pengguna yang terdaftar langsung dari halaman organisasi",
|
||||||
"Default avatar": "Avatar default",
|
"Default avatar": "Avatar default",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Gagal menghapus",
|
"Failed to delete": "Gagal menghapus",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Gagal menyimpan",
|
"Failed to save": "Gagal menyimpan",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Langganan",
|
"Subscriptions": "Langganan",
|
||||||
"Successfully added": "Berhasil ditambahkan",
|
"Successfully added": "Berhasil ditambahkan",
|
||||||
"Successfully deleted": "Berhasil dihapus",
|
"Successfully deleted": "Berhasil dihapus",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Berhasil disimpan",
|
"Successfully saved": "Berhasil disimpan",
|
||||||
"Supported country codes": "Kode negara yang didukung",
|
"Supported country codes": "Kode negara yang didukung",
|
||||||
"Supported country codes - Tooltip": "Kode negara yang didukung oleh organisasi. Kode-kode ini dapat dipilih sebagai awalan saat mengirim kode verifikasi SMS",
|
"Supported country codes - Tooltip": "Kode negara yang didukung oleh organisasi. Kode-kode ini dapat dipilih sebagai awalan saat mengirim kode verifikasi SMS",
|
||||||
"Sure to delete": "Pasti untuk menghapus",
|
"Sure to delete": "Pasti untuk menghapus",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Sinkronisasi",
|
"Sync": "Sinkronisasi",
|
||||||
"Syncers": "Sinkronisasi",
|
"Syncers": "Sinkronisasi",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "kosong",
|
"empty": "kosong",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "{total} secara keseluruhan"
|
"{total} in total": "{total} secara keseluruhan"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "閉じる",
|
"Close": "閉じる",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "作成された時間",
|
"Created time": "作成された時間",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "デフォルトアプリケーション",
|
"Default application": "デフォルトアプリケーション",
|
||||||
"Default application - Tooltip": "組織ページから直接登録されたユーザーのデフォルトアプリケーション",
|
"Default application - Tooltip": "組織ページから直接登録されたユーザーのデフォルトアプリケーション",
|
||||||
"Default avatar": "デフォルトのアバター",
|
"Default avatar": "デフォルトのアバター",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "削除に失敗しました",
|
"Failed to delete": "削除に失敗しました",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "保存に失敗しました",
|
"Failed to save": "保存に失敗しました",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "ファビコン",
|
"Favicon": "ファビコン",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "サブスクリプション",
|
"Subscriptions": "サブスクリプション",
|
||||||
"Successfully added": "正常に追加されました",
|
"Successfully added": "正常に追加されました",
|
||||||
"Successfully deleted": "正常に削除されました",
|
"Successfully deleted": "正常に削除されました",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "成功的に保存されました",
|
"Successfully saved": "成功的に保存されました",
|
||||||
"Supported country codes": "サポートされている国コード",
|
"Supported country codes": "サポートされている国コード",
|
||||||
"Supported country codes - Tooltip": "組織でサポートされている国コード。これらのコードは、SMS認証コードのプレフィックスとして選択できます",
|
"Supported country codes - Tooltip": "組織でサポートされている国コード。これらのコードは、SMS認証コードのプレフィックスとして選択できます",
|
||||||
"Sure to delete": "削除することが確実です",
|
"Sure to delete": "削除することが確実です",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "同期",
|
"Sync": "同期",
|
||||||
"Syncers": "シンカーズ",
|
"Syncers": "シンカーズ",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "空",
|
"empty": "空",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "総計{total}"
|
"{total} in total": "総計{total}"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "닫다",
|
"Close": "닫다",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "작성한 시간",
|
"Created time": "작성한 시간",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "기본 애플리케이션",
|
"Default application": "기본 애플리케이션",
|
||||||
"Default application - Tooltip": "조직 페이지에서 직접 등록한 사용자의 기본 응용 프로그램",
|
"Default application - Tooltip": "조직 페이지에서 직접 등록한 사용자의 기본 응용 프로그램",
|
||||||
"Default avatar": "기본 아바타",
|
"Default avatar": "기본 아바타",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "삭제에 실패했습니다",
|
"Failed to delete": "삭제에 실패했습니다",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "저장에 실패했습니다",
|
"Failed to save": "저장에 실패했습니다",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "파비콘",
|
"Favicon": "파비콘",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "구독",
|
"Subscriptions": "구독",
|
||||||
"Successfully added": "성공적으로 추가되었습니다",
|
"Successfully added": "성공적으로 추가되었습니다",
|
||||||
"Successfully deleted": "성공적으로 삭제되었습니다",
|
"Successfully deleted": "성공적으로 삭제되었습니다",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "성공적으로 저장되었습니다",
|
"Successfully saved": "성공적으로 저장되었습니다",
|
||||||
"Supported country codes": "지원되는 국가 코드들",
|
"Supported country codes": "지원되는 국가 코드들",
|
||||||
"Supported country codes - Tooltip": "조직에서 지원하는 국가 코드입니다. 이 코드들은 SMS 인증 코드를 보낼 때 접두사로 선택할 수 있습니다",
|
"Supported country codes - Tooltip": "조직에서 지원하는 국가 코드입니다. 이 코드들은 SMS 인증 코드를 보낼 때 접두사로 선택할 수 있습니다",
|
||||||
"Sure to delete": "삭제하시겠습니까?",
|
"Sure to delete": "삭제하시겠습니까?",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "싱크",
|
"Sync": "싱크",
|
||||||
"Syncers": "싱크어스",
|
"Syncers": "싱크어스",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "빈",
|
"empty": "빈",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "총 {total}개"
|
"{total} in total": "총 {total}개"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Fechar",
|
"Close": "Fechar",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Hora de Criação",
|
"Created time": "Hora de Criação",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Aplicação padrão",
|
"Default application": "Aplicação padrão",
|
||||||
"Default application - Tooltip": "Aplicação padrão para usuários registrados diretamente na página da organização",
|
"Default application - Tooltip": "Aplicação padrão para usuários registrados diretamente na página da organização",
|
||||||
"Default avatar": "Avatar padrão",
|
"Default avatar": "Avatar padrão",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Falha ao excluir",
|
"Failed to delete": "Falha ao excluir",
|
||||||
"Failed to enable": "Falha ao habilitar",
|
"Failed to enable": "Falha ao habilitar",
|
||||||
"Failed to get answer": "Falha ao obter resposta",
|
"Failed to get answer": "Falha ao obter resposta",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Falha ao salvar",
|
"Failed to save": "Falha ao salvar",
|
||||||
"Failed to verify": "Falha ao verificar",
|
"Failed to verify": "Falha ao verificar",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Đăng ký",
|
"Subscriptions": "Đăng ký",
|
||||||
"Successfully added": "Adicionado com sucesso",
|
"Successfully added": "Adicionado com sucesso",
|
||||||
"Successfully deleted": "Excluído com sucesso",
|
"Successfully deleted": "Excluído com sucesso",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Salvo com sucesso",
|
"Successfully saved": "Salvo com sucesso",
|
||||||
"Supported country codes": "Códigos de país suportados",
|
"Supported country codes": "Códigos de país suportados",
|
||||||
"Supported country codes - Tooltip": "Códigos de país suportados pela organização. Esses códigos podem ser selecionados como prefixo ao enviar códigos de verificação SMS",
|
"Supported country codes - Tooltip": "Códigos de país suportados pela organização. Esses códigos podem ser selecionados como prefixo ao enviar códigos de verificação SMS",
|
||||||
"Sure to delete": "Tem certeza que deseja excluir",
|
"Sure to delete": "Tem certeza que deseja excluir",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Sincronizar",
|
"Sync": "Sincronizar",
|
||||||
"Syncers": "Sincronizadores",
|
"Syncers": "Sincronizadores",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "vazio",
|
"empty": "vazio",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "{total} no total"
|
"{total} in total": "{total} no total"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Близко",
|
"Close": "Близко",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Созданное время",
|
"Created time": "Созданное время",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Приложение по умолчанию",
|
"Default application": "Приложение по умолчанию",
|
||||||
"Default application - Tooltip": "По умолчанию приложение для пользователей, зарегистрированных непосредственно со страницы организации",
|
"Default application - Tooltip": "По умолчанию приложение для пользователей, зарегистрированных непосредственно со страницы организации",
|
||||||
"Default avatar": "Стандартный аватар",
|
"Default avatar": "Стандартный аватар",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Не удалось удалить",
|
"Failed to delete": "Не удалось удалить",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Не удалось сохранить",
|
"Failed to save": "Не удалось сохранить",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Фавикон",
|
"Favicon": "Фавикон",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Подписки",
|
"Subscriptions": "Подписки",
|
||||||
"Successfully added": "Успешно добавлено",
|
"Successfully added": "Успешно добавлено",
|
||||||
"Successfully deleted": "Успешно удалено",
|
"Successfully deleted": "Успешно удалено",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Успешно сохранено",
|
"Successfully saved": "Успешно сохранено",
|
||||||
"Supported country codes": "Поддерживаемые коды стран",
|
"Supported country codes": "Поддерживаемые коды стран",
|
||||||
"Supported country codes - Tooltip": "Коды стран, поддерживаемые организацией. Эти коды могут быть выбраны в качестве префикса при отправке SMS-кодов подтверждения",
|
"Supported country codes - Tooltip": "Коды стран, поддерживаемые организацией. Эти коды могут быть выбраны в качестве префикса при отправке SMS-кодов подтверждения",
|
||||||
"Sure to delete": "Обязательное удаление",
|
"Sure to delete": "Обязательное удаление",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Синхронизация",
|
"Sync": "Синхронизация",
|
||||||
"Syncers": "Синкеры",
|
"Syncers": "Синкеры",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "пустые",
|
"empty": "пустые",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "{total} в общей сложности"
|
"{total} in total": "{total} в общей сложности"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "Đóng lại",
|
"Close": "Đóng lại",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"Created time": "Thời gian tạo",
|
"Created time": "Thời gian tạo",
|
||||||
|
"Custom": "Custom",
|
||||||
"Default application": "Ứng dụng mặc định",
|
"Default application": "Ứng dụng mặc định",
|
||||||
"Default application - Tooltip": "Ứng dụng mặc định cho người dùng đăng ký trực tiếp từ trang tổ chức",
|
"Default application - Tooltip": "Ứng dụng mặc định cho người dùng đăng ký trực tiếp từ trang tổ chức",
|
||||||
"Default avatar": "Hình đại diện mặc định",
|
"Default avatar": "Hình đại diện mặc định",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "Không thể xoá",
|
"Failed to delete": "Không thể xoá",
|
||||||
"Failed to enable": "Failed to enable",
|
"Failed to enable": "Failed to enable",
|
||||||
"Failed to get answer": "Failed to get answer",
|
"Failed to get answer": "Failed to get answer",
|
||||||
|
"Failed to remove": "Failed to remove",
|
||||||
"Failed to save": "Không thể lưu được",
|
"Failed to save": "Không thể lưu được",
|
||||||
"Failed to verify": "Failed to verify",
|
"Failed to verify": "Failed to verify",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "Đăng ký",
|
"Subscriptions": "Đăng ký",
|
||||||
"Successfully added": "Đã thêm thành công",
|
"Successfully added": "Đã thêm thành công",
|
||||||
"Successfully deleted": "Đã xóa thành công",
|
"Successfully deleted": "Đã xóa thành công",
|
||||||
|
"Successfully removed": "Successfully removed",
|
||||||
"Successfully saved": "Thành công đã được lưu lại",
|
"Successfully saved": "Thành công đã được lưu lại",
|
||||||
"Supported country codes": "Các mã quốc gia được hỗ trợ",
|
"Supported country codes": "Các mã quốc gia được hỗ trợ",
|
||||||
"Supported country codes - Tooltip": "Mã quốc gia được hỗ trợ bởi tổ chức. Những mã này có thể được chọn làm tiền tố khi gửi mã xác nhận SMS",
|
"Supported country codes - Tooltip": "Mã quốc gia được hỗ trợ bởi tổ chức. Những mã này có thể được chọn làm tiền tố khi gửi mã xác nhận SMS",
|
||||||
"Sure to delete": "Chắc chắn muốn xóa",
|
"Sure to delete": "Chắc chắn muốn xóa",
|
||||||
|
"Sure to remove": "Sure to remove",
|
||||||
"Swagger": "Swagger",
|
"Swagger": "Swagger",
|
||||||
"Sync": "Đồng bộ hoá",
|
"Sync": "Đồng bộ hoá",
|
||||||
"Syncers": "Đồng bộ hóa",
|
"Syncers": "Đồng bộ hóa",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "You can only select one physical group",
|
"You can only select one physical group": "You can only select one physical group",
|
||||||
"empty": "trống",
|
"empty": "trống",
|
||||||
|
"remove": "remove",
|
||||||
"{total} in total": "Trong tổng số {total}"
|
"{total} in total": "Trong tổng số {total}"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "Parent group",
|
"Parent group": "Parent group",
|
||||||
"Parent group - Tooltip": "Parent group - Tooltip",
|
"Parent group - Tooltip": "Parent group - Tooltip",
|
||||||
"Physical": "Physical",
|
"Physical": "Physical",
|
||||||
|
"Show all": "Show all",
|
||||||
"Virtual": "Virtual"
|
"Virtual": "Virtual"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -188,6 +188,7 @@
|
|||||||
"Close": "关闭",
|
"Close": "关闭",
|
||||||
"Confirm": "确认",
|
"Confirm": "确认",
|
||||||
"Created time": "创建时间",
|
"Created time": "创建时间",
|
||||||
|
"Custom": "自定义",
|
||||||
"Default application": "默认应用",
|
"Default application": "默认应用",
|
||||||
"Default application - Tooltip": "直接从组织页面注册的用户默认所属的应用",
|
"Default application - Tooltip": "直接从组织页面注册的用户默认所属的应用",
|
||||||
"Default avatar": "默认头像",
|
"Default avatar": "默认头像",
|
||||||
@ -209,6 +210,7 @@
|
|||||||
"Failed to delete": "删除失败",
|
"Failed to delete": "删除失败",
|
||||||
"Failed to enable": "启用失败",
|
"Failed to enable": "启用失败",
|
||||||
"Failed to get answer": "获取回答失败",
|
"Failed to get answer": "获取回答失败",
|
||||||
|
"Failed to remove": "移除失败",
|
||||||
"Failed to save": "保存失败",
|
"Failed to save": "保存失败",
|
||||||
"Failed to verify": "验证失败",
|
"Failed to verify": "验证失败",
|
||||||
"Favicon": "Favicon",
|
"Favicon": "Favicon",
|
||||||
@ -301,10 +303,12 @@
|
|||||||
"Subscriptions": "订阅",
|
"Subscriptions": "订阅",
|
||||||
"Successfully added": "添加成功",
|
"Successfully added": "添加成功",
|
||||||
"Successfully deleted": "删除成功",
|
"Successfully deleted": "删除成功",
|
||||||
|
"Successfully removed": "移除成功",
|
||||||
"Successfully saved": "保存成功",
|
"Successfully saved": "保存成功",
|
||||||
"Supported country codes": "支持的国家代码",
|
"Supported country codes": "支持的国家代码",
|
||||||
"Supported country codes - Tooltip": "该组织所支持的国家代码,发送短信验证码时可以选择这些国家的代码前缀",
|
"Supported country codes - Tooltip": "该组织所支持的国家代码,发送短信验证码时可以选择这些国家的代码前缀",
|
||||||
"Sure to delete": "确定删除",
|
"Sure to delete": "确定删除",
|
||||||
|
"Sure to remove": "确定移除",
|
||||||
"Swagger": "API文档",
|
"Swagger": "API文档",
|
||||||
"Sync": "同步",
|
"Sync": "同步",
|
||||||
"Syncers": "同步器",
|
"Syncers": "同步器",
|
||||||
@ -329,6 +333,7 @@
|
|||||||
"Webhooks": "Webhooks",
|
"Webhooks": "Webhooks",
|
||||||
"You can only select one physical group": "只能选择一个实体组",
|
"You can only select one physical group": "只能选择一个实体组",
|
||||||
"empty": "无",
|
"empty": "无",
|
||||||
|
"remove": "移除",
|
||||||
"{total} in total": "{total} 总计"
|
"{total} in total": "{total} 总计"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
@ -337,6 +342,7 @@
|
|||||||
"Parent group": "上级组",
|
"Parent group": "上级组",
|
||||||
"Parent group - Tooltip": "上级组",
|
"Parent group - Tooltip": "上级组",
|
||||||
"Physical": "物理组",
|
"Physical": "物理组",
|
||||||
|
"Show all": "显示全部",
|
||||||
"Virtual": "虚拟组"
|
"Virtual": "虚拟组"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
|
@ -91,6 +91,7 @@ class AccountTable extends React.Component {
|
|||||||
{name: "Karma", label: i18next.t("user:Karma")},
|
{name: "Karma", label: i18next.t("user:Karma")},
|
||||||
{name: "Ranking", label: i18next.t("user:Ranking")},
|
{name: "Ranking", label: i18next.t("user:Ranking")},
|
||||||
{name: "Signup application", label: i18next.t("general:Signup application")},
|
{name: "Signup application", label: i18next.t("general:Signup application")},
|
||||||
|
{name: "API key", label: i18next.t("general:API key")},
|
||||||
{name: "Roles", label: i18next.t("general:Roles")},
|
{name: "Roles", label: i18next.t("general:Roles")},
|
||||||
{name: "Permissions", label: i18next.t("general:Permissions")},
|
{name: "Permissions", label: i18next.t("general:Permissions")},
|
||||||
{name: "Groups", label: i18next.t("general:Groups")},
|
{name: "Groups", label: i18next.t("general:Groups")},
|
||||||
|
Reference in New Issue
Block a user