diff --git a/authz/authz.go b/authz/authz.go index e2ae4144..48a92a52 100644 --- a/authz/authz.go +++ b/authz/authz.go @@ -96,6 +96,7 @@ p, *, *, GET, /api/get-organization-names, *, * p, *, *, GET, /api/get-all-objects, *, * p, *, *, GET, /api/get-all-actions, *, * p, *, *, GET, /api/get-all-roles, *, * +p, *, *, GET, /api/get-invitation-info, *, * ` sa := stringadapter.NewAdapter(ruleText) diff --git a/controllers/account.go b/controllers/account.go index 7538c501..7dde0493 100644 --- a/controllers/account.go +++ b/controllers/account.go @@ -227,7 +227,7 @@ func (c *ApiController) Signup() { if invitation != nil { invitation.UsedCount += 1 - _, err := object.UpdateInvitation(invitation.GetId(), invitation) + _, err := object.UpdateInvitation(invitation.GetId(), invitation, c.GetAcceptLanguage()) if err != nil { c.ResponseError(err.Error()) return diff --git a/controllers/invitation.go b/controllers/invitation.go index c4f65f9e..25743ca4 100644 --- a/controllers/invitation.go +++ b/controllers/invitation.go @@ -84,6 +84,32 @@ func (c *ApiController) GetInvitation() { c.ResponseOk(invitation) } +// GetInvitationCodeInfo +// @Title GetInvitationCodeInfo +// @Tag Invitation API +// @Description get invitation code information +// @Param code query string true "Invitation code" +// @Success 200 {object} object.Invitation The Response object +// @router /get-invitation-info [get] +func (c *ApiController) GetInvitationCodeInfo() { + code := c.Input().Get("code") + applicationId := c.Input().Get("applicationId") + + application, err := object.GetApplication(applicationId) + if err != nil { + c.ResponseError(err.Error()) + return + } + + invitation, msg := object.GetInvitationByCode(code, application.Organization, c.GetAcceptLanguage()) + if msg != "" { + c.ResponseError(msg) + return + } + + c.ResponseOk(object.GetMaskedInvitation(invitation)) +} + // UpdateInvitation // @Title UpdateInvitation // @Tag Invitation API @@ -102,7 +128,7 @@ func (c *ApiController) UpdateInvitation() { return } - c.Data["json"] = wrapActionResponse(object.UpdateInvitation(id, &invitation)) + c.Data["json"] = wrapActionResponse(object.UpdateInvitation(id, &invitation, c.GetAcceptLanguage())) c.ServeJSON() } @@ -121,7 +147,7 @@ func (c *ApiController) AddInvitation() { return } - c.Data["json"] = wrapActionResponse(object.AddInvitation(&invitation)) + c.Data["json"] = wrapActionResponse(object.AddInvitation(&invitation, c.GetAcceptLanguage())) c.ServeJSON() } diff --git a/i18n/locales/ar/data.json b/i18n/locales/ar/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/ar/data.json +++ b/i18n/locales/ar/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/de/data.json b/i18n/locales/de/data.json index 3f89c3c5..61346698 100644 --- a/i18n/locales/de/data.json +++ b/i18n/locales/de/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "Anzeigename kann nicht leer sein", "DisplayName is not valid real name": "DisplayName ist kein gültiger Vorname", "Email already exists": "E-Mail existiert bereits", diff --git a/i18n/locales/en/data.json b/i18n/locales/en/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/en/data.json +++ b/i18n/locales/en/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/es/data.json b/i18n/locales/es/data.json index 42c13c9c..fc6096c0 100644 --- a/i18n/locales/es/data.json +++ b/i18n/locales/es/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Afiliación no puede estar en blanco", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco", "DisplayName is not valid real name": "El nombre de pantalla no es un nombre real válido", "Email already exists": "El correo electrónico ya existe", diff --git a/i18n/locales/fa/data.json b/i18n/locales/fa/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/fa/data.json +++ b/i18n/locales/fa/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/fi/data.json b/i18n/locales/fi/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/fi/data.json +++ b/i18n/locales/fi/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/fr/data.json b/i18n/locales/fr/data.json index 40d52fbb..626d4643 100644 --- a/i18n/locales/fr/data.json +++ b/i18n/locales/fr/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation ne peut pas être vide", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide", "DisplayName is not valid real name": "DisplayName n'est pas un nom réel valide", "Email already exists": "E-mail déjà existant", diff --git a/i18n/locales/he/data.json b/i18n/locales/he/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/he/data.json +++ b/i18n/locales/he/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/id/data.json b/i18n/locales/id/data.json index b4d14892..73111cac 100644 --- a/i18n/locales/id/data.json +++ b/i18n/locales/id/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Keterkaitan tidak boleh kosong", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong", "DisplayName is not valid real name": "DisplayName bukanlah nama asli yang valid", "Email already exists": "Email sudah ada", diff --git a/i18n/locales/it/data.json b/i18n/locales/it/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/it/data.json +++ b/i18n/locales/it/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/ja/data.json b/i18n/locales/ja/data.json index fb105452..f4616226 100644 --- a/i18n/locales/ja/data.json +++ b/i18n/locales/ja/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "所属は空白にできません", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "表示名は空白にできません", "DisplayName is not valid real name": "表示名は有効な実名ではありません", "Email already exists": "メールは既に存在します", diff --git a/i18n/locales/kk/data.json b/i18n/locales/kk/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/kk/data.json +++ b/i18n/locales/kk/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/ko/data.json b/i18n/locales/ko/data.json index ed9e7b19..e9e0d6cd 100644 --- a/i18n/locales/ko/data.json +++ b/i18n/locales/ko/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "소속은 비워 둘 수 없습니다", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다", "DisplayName is not valid real name": "DisplayName는 유효한 실제 이름이 아닙니다", "Email already exists": "이메일이 이미 존재합니다", diff --git a/i18n/locales/ms/data.json b/i18n/locales/ms/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/ms/data.json +++ b/i18n/locales/ms/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/nl/data.json b/i18n/locales/nl/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/nl/data.json +++ b/i18n/locales/nl/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/pl/data.json b/i18n/locales/pl/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/pl/data.json +++ b/i18n/locales/pl/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/pt/data.json b/i18n/locales/pt/data.json index 29a6e7a9..c4b00438 100644 --- a/i18n/locales/pt/data.json +++ b/i18n/locales/pt/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/ru/data.json b/i18n/locales/ru/data.json index 058da3a5..ddd24ab3 100644 --- a/i18n/locales/ru/data.json +++ b/i18n/locales/ru/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Принадлежность не может быть пустым значением", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "Имя отображения не может быть пустым", "DisplayName is not valid real name": "DisplayName не является действительным именем", "Email already exists": "Электронная почта уже существует", diff --git a/i18n/locales/sv/data.json b/i18n/locales/sv/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/sv/data.json +++ b/i18n/locales/sv/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/tr/data.json b/i18n/locales/tr/data.json index 7ce577db..05f589a8 100644 --- a/i18n/locales/tr/data.json +++ b/i18n/locales/tr/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/uk/data.json b/i18n/locales/uk/data.json index 3086b85c..8a4a8aab 100644 --- a/i18n/locales/uk/data.json +++ b/i18n/locales/uk/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Affiliation cannot be blank", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName is not valid real name": "DisplayName is not valid real name", "Email already exists": "Email already exists", diff --git a/i18n/locales/vi/data.json b/i18n/locales/vi/data.json index 6f4ac244..3c3c6072 100644 --- a/i18n/locales/vi/data.json +++ b/i18n/locales/vi/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "Tình trạng liên kết không thể để trống", + "Default code does not match the code's matching rules": "Default code does not match the code's matching rules", "DisplayName cannot be blank": "Tên hiển thị không thể để trống", "DisplayName is not valid real name": "DisplayName không phải là tên thật hợp lệ", "Email already exists": "Email đã tồn tại", diff --git a/i18n/locales/zh/data.json b/i18n/locales/zh/data.json index 514512da..21a3408f 100644 --- a/i18n/locales/zh/data.json +++ b/i18n/locales/zh/data.json @@ -30,6 +30,7 @@ }, "check": { "Affiliation cannot be blank": "工作单位不可为空", + "Default code does not match the code's matching rules": "邀请码默认值和邀请码规则不匹配", "DisplayName cannot be blank": "显示名称不可为空", "DisplayName is not valid real name": "显示名称必须是真实姓名", "Email already exists": "该邮箱已存在", diff --git a/object/check.go b/object/check.go index b937c0d0..2fe97a3e 100644 --- a/object/check.go +++ b/object/check.go @@ -184,6 +184,15 @@ func CheckInvitationCode(application *Application, organization *Organization, a } } +func CheckInvitationDefaultCode(code string, defaultCode string, lang string) error { + if matched, err := util.IsInvitationCodeMatch(code, defaultCode); err != nil { + return err + } else if !matched { + return fmt.Errorf(i18n.Translate(lang, "check:Default code does not match the code's matching rules")) + } + return nil +} + func checkSigninErrorTimes(user *User, lang string) error { failedSigninLimit, failedSigninFrozenTime, err := GetFailedSigninConfigByUser(user) if err != nil { diff --git a/object/invitation.go b/object/invitation.go index f90f0466..57f4b0e8 100644 --- a/object/invitation.go +++ b/object/invitation.go @@ -40,6 +40,7 @@ type Invitation struct { Phone string `xorm:"varchar(100)" json:"phone"` SignupGroup string `xorm:"varchar(100)" json:"signupGroup"` + DefaultCode string `xorm:"varchar(100)" json:"defaultCode"` State string `xorm:"varchar(100)" json:"state"` } @@ -93,7 +94,45 @@ func GetInvitation(id string) (*Invitation, error) { return getInvitation(owner, name) } -func UpdateInvitation(id string, invitation *Invitation) (bool, error) { +func GetInvitationByCode(code string, organizationName string, lang string) (*Invitation, string) { + invitations, err := GetInvitations(organizationName) + if err != nil { + return nil, err.Error() + } + errMsg := "" + for _, invitation := range invitations { + if isValid, msg := invitation.SimpleCheckInvitationCode(code, lang); isValid { + return invitation, msg + } else if msg != "" && errMsg == "" { + errMsg = msg + } + } + + if errMsg != "" { + return nil, errMsg + } else { + return nil, i18n.Translate(lang, "check:Invitation code is invalid") + } +} + +func GetMaskedInvitation(invitation *Invitation) *Invitation { + if invitation == nil { + return nil + } + + invitation.CreatedTime = "" + invitation.UpdatedTime = "" + invitation.Code = "***" + invitation.DefaultCode = "***" + invitation.IsRegexp = false + invitation.Quota = -1 + invitation.UsedCount = -1 + invitation.SignupGroup = "" + + return invitation +} + +func UpdateInvitation(id string, invitation *Invitation, lang string) (bool, error) { owner, name := util.GetOwnerAndNameFromId(id) if p, err := getInvitation(owner, name); err != nil { return false, err @@ -107,6 +146,11 @@ func UpdateInvitation(id string, invitation *Invitation) (bool, error) { invitation.IsRegexp = isRegexp } + err := CheckInvitationDefaultCode(invitation.Code, invitation.DefaultCode, lang) + if err != nil { + return false, err + } + affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(invitation) if err != nil { return false, err @@ -115,13 +159,18 @@ func UpdateInvitation(id string, invitation *Invitation) (bool, error) { return affected != 0, nil } -func AddInvitation(invitation *Invitation) (bool, error) { +func AddInvitation(invitation *Invitation, lang string) (bool, error) { if isRegexp, err := util.IsRegexp(invitation.Code); err != nil { return false, err } else { invitation.IsRegexp = isRegexp } + err := CheckInvitationDefaultCode(invitation.Code, invitation.DefaultCode, lang) + if err != nil { + return false, err + } + affected, err := ormer.Engine.Insert(invitation) if err != nil { return false, err @@ -147,7 +196,7 @@ func VerifyInvitation(id string) (payment *Payment, attachInfo map[string]interf return nil, nil, fmt.Errorf("the invitation: %s does not exist", id) } -func (invitation *Invitation) IsInvitationCodeValid(application *Application, invitationCode string, username string, email string, phone string, lang string) (bool, string) { +func (invitation *Invitation) SimpleCheckInvitationCode(invitationCode string, lang string) (bool, string) { if matched, err := util.IsInvitationCodeMatch(invitation.Code, invitationCode); err != nil { return false, err.Error() } else if !matched { @@ -160,15 +209,6 @@ func (invitation *Invitation) IsInvitationCodeValid(application *Application, in if invitation.UsedCount >= invitation.Quota { return false, i18n.Translate(lang, "check:Invitation code exhausted") } - if application.IsSignupItemRequired("Username") && invitation.Username != "" && invitation.Username != username { - return false, i18n.Translate(lang, "check:Please register using the username corresponding to the invitation code") - } - if application.IsSignupItemRequired("Email") && invitation.Email != "" && invitation.Email != email { - return false, i18n.Translate(lang, "check:Please register using the email corresponding to the invitation code") - } - if application.IsSignupItemRequired("Phone") && invitation.Phone != "" && invitation.Phone != phone { - return false, i18n.Translate(lang, "check:Please register using the phone corresponding to the invitation code") - } // Determine whether the invitation code is in the form of a regular expression other than pure numbers and letters if invitation.IsRegexp { @@ -179,3 +219,19 @@ func (invitation *Invitation) IsInvitationCodeValid(application *Application, in } return true, "" } + +func (invitation *Invitation) IsInvitationCodeValid(application *Application, invitationCode string, username string, email string, phone string, lang string) (bool, string) { + if isValid, msg := invitation.SimpleCheckInvitationCode(invitationCode, lang); !isValid { + return false, msg + } + if application.IsSignupItemRequired("Username") && invitation.Username != "" && invitation.Username != username { + return false, i18n.Translate(lang, "check:Please register using the username corresponding to the invitation code") + } + if application.IsSignupItemRequired("Email") && invitation.Email != "" && invitation.Email != email { + return false, i18n.Translate(lang, "check:Please register using the email corresponding to the invitation code") + } + if application.IsSignupItemRequired("Phone") && invitation.Phone != "" && invitation.Phone != phone { + return false, i18n.Translate(lang, "check:Please register using the phone corresponding to the invitation code") + } + return true, "" +} diff --git a/routers/router.go b/routers/router.go index dbf8658c..ba228c68 100644 --- a/routers/router.go +++ b/routers/router.go @@ -94,6 +94,7 @@ func initAPI() { beego.Router("/api/get-invitations", &controllers.ApiController{}, "GET:GetInvitations") beego.Router("/api/get-invitation", &controllers.ApiController{}, "GET:GetInvitation") + beego.Router("/api/get-invitation-info", &controllers.ApiController{}, "GET:GetInvitationCodeInfo") beego.Router("/api/update-invitation", &controllers.ApiController{}, "POST:UpdateInvitation") beego.Router("/api/add-invitation", &controllers.ApiController{}, "POST:AddInvitation") beego.Router("/api/delete-invitation", &controllers.ApiController{}, "POST:DeleteInvitation") diff --git a/web/src/InvitationEditPage.js b/web/src/InvitationEditPage.js index 7aa8510d..7889ff72 100644 --- a/web/src/InvitationEditPage.js +++ b/web/src/InvitationEditPage.js @@ -19,6 +19,7 @@ import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as Setting from "./Setting"; import i18next from "i18next"; +import copy from "copy-to-clipboard"; const {Option} = Select; @@ -99,6 +100,18 @@ class InvitationEditPage extends React.Component { {this.state.mode === "add" ? i18next.t("invitation:New Invitation") : i18next.t("invitation:Edit Invitation")} + {this.state.mode === "add" ? : null} } style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner"> @@ -140,10 +153,24 @@ class InvitationEditPage extends React.Component {