Compare commits

..

6 Commits

Author SHA1 Message Date
June
6d4f94986e feat: fix the bug that "app - global" admin is not allowed to update user (#1468) 2023-01-12 12:29:11 +08:00
Yaodong Yu
9ca686b240 feat: disable role and permission update and checks when updating user (#1466) 2023-01-12 11:40:32 +08:00
jakiuncle
c93bc0dda2 fix: add e2e cypress screenshots and videos if failed (#1465)
* feat: location error

* feat: location error

* feat: test error

* feat add e2e

* feat: delete cypress dependency

* feat: Add e2e error feedback
2023-01-11 23:56:09 +08:00
Yaodong Yu
7d25b9cdd8 feat: auto link accounts with the same email (#1464) 2023-01-11 23:19:16 +08:00
Yaodong Yu
ead844131e feat: improve user edit page to fix missing fields and page crash (#1463) 2023-01-11 16:15:06 +08:00
Yaodong Yu
ce2a4bbf6e feat: check uniqueness for email and phone when updating user (#1461)
* fix: check unique field when update user

* Update data.json

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-01-10 22:34:08 +08:00
21 changed files with 200 additions and 88 deletions

View File

@@ -107,12 +107,23 @@ jobs:
run: nohup go run ./main.go &
working-directory: ./
- name: Sleep for starting
run: sleep 60s
run: sleep 90s
shell: bash
- name: e2e
run: npx cypress run --spec "**/e2e/**.cy.js"
working-directory: ./web
- uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-screenshots
path: ./web/cypress/screenshots
- uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos
path: ./web/cypress/videos
release-and-push:
name: Release And Push
runs-on: ubuntu-latest

View File

@@ -405,54 +405,61 @@ func (c *ApiController) Login() {
return
}
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {
uid, err := uuid.NewRandom()
if application.EnableLinkWithEmail {
// find user that has the same email
user = object.GetUserByField(application.Organization, "email", userInfo.Email)
}
if user == nil || user.IsDeleted {
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {
uid, err := uuid.NewRandom()
if err != nil {
c.ResponseError(err.Error())
return
}
uidStr := strings.Split(uid.String(), "-")
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1])
}
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(err.Error())
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
uidStr := strings.Split(uid.String(), "-")
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1])
user = &object.User{
Owner: application.Organization,
Name: userInfo.Username,
CreatedTime: util.GetCurrentTime(),
Id: util.GenerateId(),
Type: "normal-user",
DisplayName: userInfo.DisplayName,
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
}
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
}
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
user = &object.User{
Owner: application.Organization,
Name: userInfo.Username,
CreatedTime: util.GetCurrentTime(),
Id: util.GenerateId(),
Type: "normal-user",
DisplayName: userInfo.DisplayName,
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
}
// sync info from 3rd-party if possible
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
object.LinkUserAccount(user, provider.Type, userInfo.Id)
resp = c.HandleLoggedIn(application, user, &form)

View File

@@ -148,8 +148,8 @@ func (c *ApiController) UpdateUser() {
return
}
if user.DisplayName == "" {
c.ResponseError(c.T("user:Display name cannot be empty"))
if msg := object.CheckUpdateUser(object.GetUser(id), &user, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg)
return
}

View File

@@ -95,20 +95,6 @@ func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiCont
itemsChanged = append(itemsChanged, item)
}
oldUserRolesJson, _ := json.Marshal(oldUser.Roles)
newUserRolesJson, _ := json.Marshal(newUser.Roles)
if string(oldUserRolesJson) != string(newUserRolesJson) {
item := object.GetAccountItemByName("Roles", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPermissionJson, _ := json.Marshal(oldUser.Permissions)
newUserPermissionJson, _ := json.Marshal(newUser.Permissions)
if string(oldUserPermissionJson) != string(newUserPermissionJson) {
item := object.GetAccountItemByName("Permissions", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
@@ -133,8 +119,15 @@ func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiCont
itemsChanged = append(itemsChanged, item)
}
currentUser := c.getCurrentUser()
if currentUser == nil && c.IsGlobalAdmin() {
currentUser = &object.User{
IsGlobalAdmin: true,
}
}
for i := range itemsChanged {
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], c.getCurrentUser(), c.GetAcceptLanguage()); !pass {
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], currentUser, c.GetAcceptLanguage()); !pass {
return pass, err
}
}

View File

@@ -175,9 +175,14 @@ func (c *ApiController) ResetEmailOrPhone() {
}
checkDest := dest
org := object.GetOrganizationByUser(user)
organization := object.GetOrganizationByUser(user)
if destType == "phone" {
phoneItem := object.GetAccountItemByName("Phone", org)
if object.HasUserByField(user.Owner, "phone", user.Phone) {
c.ResponseError(c.T("check:Phone already exists"))
return
}
phoneItem := object.GetAccountItemByName("Phone", organization)
if phoneItem == nil {
c.ResponseError(c.T("verification:Unable to get the phone modify rule."))
return
@@ -189,12 +194,17 @@ func (c *ApiController) ResetEmailOrPhone() {
}
phonePrefix := "86"
if org != nil && org.PhonePrefix != "" {
phonePrefix = org.PhonePrefix
if organization != nil && organization.PhonePrefix != "" {
phonePrefix = organization.PhonePrefix
}
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} else if destType == "email" {
emailItem := object.GetAccountItemByName("Email", org)
if object.HasUserByField(user.Owner, "email", user.Email) {
c.ResponseError(c.T("check:Email already exists"))
return
}
emailItem := object.GetAccountItemByName("Email", organization)
if emailItem == nil {
c.ResponseError(c.T("verification:Unable to get the email modify rule."))
return

View File

@@ -50,6 +50,7 @@ type Application struct {
EnableCodeSignin bool `json:"enableCodeSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`

View File

@@ -60,8 +60,8 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if reWhiteSpace.MatchString(username) {
return i18n.Translate(lang, "check:Username cannot contain white spaces")
}
msg := CheckUsername(username, lang)
if msg != "" {
if msg := CheckUsername(username, lang); msg != "" {
return msg
}
@@ -342,6 +342,34 @@ func CheckUsername(username string, lang string) string {
return ""
}
func CheckUpdateUser(oldUser *User, user *User, lang string) string {
if user.DisplayName == "" {
return i18n.Translate(lang, "user:Display name cannot be empty")
}
if msg := CheckUsername(user.Name, lang); msg != "" {
return msg
}
if oldUser.Name != user.Name {
if HasUserByField(user.Owner, "name", user.Name) {
return i18n.Translate(lang, "check:Username already exists")
}
}
if oldUser.Email != user.Email {
if HasUserByField(user.Name, "email", user.Email) {
return i18n.Translate(lang, "check:Email already exists")
}
}
if oldUser.Phone != user.Phone {
if HasUserByField(user.Owner, "phone", user.Phone) {
return i18n.Translate(lang, "check:Phone already exists")
}
}
return ""
}
func CheckToEnableCaptcha(application *Application) bool {
if len(application.Providers) == 0 {
return false

View File

@@ -110,8 +110,8 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
Roles []*Role `xorm:"-" json:"roles"`
Permissions []*Permission `xorm:"-" json:"permissions"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`

View File

@@ -606,6 +606,16 @@ class ApplicationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col span={(Setting.isMobile()) ? 19 : 6}>
{Setting.getLabel(i18next.t("application:Enable link accounts that with the same email"), i18next.t("application:Enable link accounts that with the same email - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableLinkWithEmail} onChange={checked => {
this.updateApplicationField("enableLinkWithEmail", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :

View File

@@ -37,7 +37,11 @@ export const ResetModal = (props) => {
const handleOk = () => {
if (dest === "") {
Setting.showMessage("error", i18next.t("user:Empty " + destType));
if (destType === "phone") {
Setting.showMessage("error", i18next.t("user:Phone cannot be empty"));
} else {
Setting.showMessage("error", i18next.t("user:Email cannot be empty"));
}
return;
}
if (code === "") {

View File

@@ -284,18 +284,23 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Email"), i18next.t("general:Email - Tooltip"))} :
</Col>
<Col style={{paddingRight: "20px"}} span={11} >
<Input value={this.state.user.email}
disabled={disabled}
onChange={e => {
this.updateUserField("email", e.target.value);
}} />
{Setting.isLocalAdminUser(this.props.account) ?
(<Input value={this.state.user.email}
disabled={disabled}
onChange={e => {
this.updateUserField("email", e.target.value);
}} />) :
(<Select value={this.state.user.email}
options={[Setting.getItem(this.state.user.email, this.state.user.email)]}
disabled={disabled}
onChange={e => {
this.updateUserField("email", e.target.value);
}} />)
}
</Col>
<Col span={11} >
{
!this.isSelf() ? null : (
<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Email...")} destType={"email"} />
)
}
{/* backend auto get the current user, so admin can not edit. Just self can reset*/}
{this.isSelf() ? <ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Email...")} destType={"email"} /> : null}
</Col>
</Row>
);
@@ -306,14 +311,21 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Phone"), i18next.t("general:Phone - Tooltip"))} :
</Col>
<Col style={{paddingRight: "20px"}} span={11} >
<Input value={this.state.user.phone} addonBefore={`+${this.state.application?.organizationObj.phonePrefix}`}
disabled={disabled}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} />
{Setting.isLocalAdminUser(this.props.account) ?
<Input value={this.state.user.phone} addonBefore={`+${this.state.application?.organizationObj.phonePrefix}`}
disabled={disabled}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} /> :
(<Select value={`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`}
options={[Setting.getItem(`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`, this.state.user.phone)]}
disabled={disabled}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} />)}
</Col>
<Col span={11} >
{this.state.user.id === this.props.account?.id ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null}
{this.isSelf() ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null}
</Col>
</Row>
);

View File

@@ -354,6 +354,7 @@ class UserListPage extends BaseListPage {
<Popconfirm
title={`Sure to delete user: ${record.name} ?`}
onConfirm={() => this.deleteUser(index)}
disabled={disabled}
>
<Button disabled={disabled} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
</Popconfirm>

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Code-Anmeldung aktivieren",
"Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip",
"Enable signup": "Anmeldung aktivieren",
@@ -714,6 +716,7 @@
"Country/Region": "Land/Region",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Benutzer bearbeiten",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Homepage",
"Homepage - Tooltip": "Startseite - Tooltip",
@@ -742,6 +745,7 @@
"Old Password": "Altes Passwort",
"Password": "Passwort",
"Password Set": "Passwort setzen",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Eigenschaften",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup",
@@ -714,6 +716,7 @@
"Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region - Tooltip",
"Edit User": "Edit User",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Homepage",
"Homepage - Tooltip": "Homepage - Tooltip",
@@ -742,6 +745,7 @@
"Old Password": "Old Password",
"Password": "Password",
"Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Habilitar inicio de sesión de WebAuthn - Tooltip",
"Enable code signin": "Habilitar inicio de sesión por código",
"Enable code signin - Tooltip": "Habilitar inicio de sesión por código - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Habilitar nuevos registros",
@@ -714,6 +716,7 @@
"Country/Region": "Pais/Región",
"Country/Region - Tooltip": "Pais/Región - Tooltip",
"Edit User": "Editar usuario",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Campo requerido!",
"Homepage": "Página de Inicio",
"Homepage - Tooltip": "Página de Inicio - Tooltip",
@@ -742,6 +745,7 @@
"Old Password": "Contraseña anterior",
"Password": "Contraseña",
"Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Propiedades",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Activer la connexion au code",
"Enable code signin - Tooltip": "Activer la connexion au code - infobulle",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Activer la session de connexion - infobulle",
"Enable signup": "Activer l'inscription",
@@ -714,6 +716,7 @@
"Country/Region": "Pays/Région",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Editer l'utilisateur",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Page d'accueil",
"Homepage - Tooltip": "Page d'accueil - infobulle",
@@ -742,6 +745,7 @@
"Old Password": "Ancien mot de passe",
"Password": "Mot de passe",
"Password Set": "Mot de passe défini",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Propriétés",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "コードサインインを有効にする",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "サインアップを有効にする",
@@ -714,6 +716,7 @@
"Country/Region": "国/地域",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "ユーザーを編集",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "ホームページ",
"Homepage - Tooltip": "ホームページ - ツールチップ",
@@ -742,6 +745,7 @@
"Old Password": "古いパスワード",
"Password": "パスワード",
"Password Set": "パスワード設定",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "プロパティー",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup",
@@ -714,6 +716,7 @@
"Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Edit User",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Homepage",
"Homepage - Tooltip": "Homepage - Tooltip",
@@ -742,6 +745,7 @@
"Old Password": "Old Password",
"Password": "Password",
"Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Включить вход с WebAuthn - Подсказка",
"Enable code signin": "Включить кодовый вход",
"Enable code signin - Tooltip": "Включить вход с кодом - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Включить сеанс входа - Подсказка",
"Enable signup": "Включить регистрацию",
@@ -714,6 +716,7 @@
"Country/Region": "Страна/регион",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Изменить пользователя",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Главная страница",
"Homepage - Tooltip": "Главная страница - Подсказки",
@@ -742,6 +745,7 @@
"Old Password": "Старый пароль",
"Password": "Пароль",
"Password Set": "Пароль установлен",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Свойства",
"Properties - Tooltip": "Properties - Tooltip",

View File

@@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "是否支持用户在登录页面通过WebAuthn方式登录",
"Enable code signin": "启用验证码登录",
"Enable code signin - Tooltip": "是否允许用手机或邮箱验证码登录",
"Enable link accounts that with the same email": "自动关联邮箱相同的账号",
"Enable link accounts that with the same email - Tooltip": "使用第三方授权登录时,如果组织中存在与授权用户邮箱相同的用户,会自动关联该第三方登录方式到该用户",
"Enable side panel": "启用侧面板",
"Enable signin session - Tooltip": "从应用登录Casdoor后Casdoor是否保持会话",
"Enable signup": "启用注册",
@@ -714,6 +716,7 @@
"Country/Region": "国家/地区",
"Country/Region - Tooltip": "国家/地区",
"Edit User": "编辑用户",
"Email cannot be empty": "邮箱不能为空",
"Empty input!": "输入为空!",
"Homepage": "个人主页",
"Homepage - Tooltip": "个人主页链接",
@@ -742,6 +745,7 @@
"Old Password": "旧密码",
"Password": "密码",
"Password Set": "密码已设置",
"Phone cannot be empty": "手机号不能为空",
"Please select avatar from resources": "从资源中选择...",
"Properties": "属性",
"Properties - Tooltip": "属性",

View File

@@ -23,12 +23,15 @@ class PropertyTable extends React.Component {
super(props);
this.state = {
properties: [],
count: Object.entries(this.props.properties).length,
count: this.props.properties !== null ? Object.entries(this.props.properties).length : 0,
};
// transfer the Object to object[]
Object.entries(this.props.properties).map((item, index) => {
this.state.properties.push({key: index, name: item[0], value: item[1]});
});
if (this.props.properties !== null) {
Object.entries(this.props.properties).map((item, index) => {
this.state.properties.push({key: index, name: item[0], value: item[1]});
});
}
}
page = 1;