Add invitation pages

This commit is contained in:
Yang Luo 2023-12-31 19:48:58 +08:00
parent 5846e337c7
commit 2fb79e4092
32 changed files with 1311 additions and 2 deletions

164
controllers/invitation.go Normal file
View File

@ -0,0 +1,164 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetInvitations
// @Title GetInvitations
// @Tag Invitation API
// @Description get invitations
// @Param owner query string true "The owner of invitations"
// @Success 200 {array} object.Invitation The Response object
// @router /get-invitations [get]
func (c *ApiController) GetInvitations() {
owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" {
invitations, err := object.GetInvitations(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(invitations)
} else {
limit := util.ParseInt(limit)
count, err := object.GetInvitationCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
invitations, err := object.GetPaginationInvitations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(invitations, paginator.Nums())
}
}
// GetInvitation
// @Title GetInvitation
// @Tag Invitation API
// @Description get invitation
// @Param id query string true "The id ( owner/name ) of the invitation"
// @Success 200 {object} object.Invitation The Response object
// @router /get-invitation [get]
func (c *ApiController) GetInvitation() {
id := c.Input().Get("id")
invitation, err := object.GetInvitation(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(invitation)
}
// UpdateInvitation
// @Title UpdateInvitation
// @Tag Invitation API
// @Description update invitation
// @Param id query string true "The id ( owner/name ) of the invitation"
// @Param body body object.Invitation true "The details of the invitation"
// @Success 200 {object} controllers.Response The Response object
// @router /update-invitation [post]
func (c *ApiController) UpdateInvitation() {
id := c.Input().Get("id")
var invitation object.Invitation
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateInvitation(id, &invitation))
c.ServeJSON()
}
// AddInvitation
// @Title AddInvitation
// @Tag Invitation API
// @Description add invitation
// @Param body body object.Invitation true "The details of the invitation"
// @Success 200 {object} controllers.Response The Response object
// @router /add-invitation [post]
func (c *ApiController) AddInvitation() {
var invitation object.Invitation
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddInvitation(&invitation))
c.ServeJSON()
}
// DeleteInvitation
// @Title DeleteInvitation
// @Tag Invitation API
// @Description delete invitation
// @Param body body object.Invitation true "The details of the invitation"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-invitation [post]
func (c *ApiController) DeleteInvitation() {
var invitation object.Invitation
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteInvitation(&invitation))
c.ServeJSON()
}
// VerifyInvitation
// @Title VerifyInvitation
// @Tag Invitation API
// @Description verify invitation
// @Param id query string true "The id ( owner/name ) of the invitation"
// @Success 200 {object} controllers.Response The Response object
// @router /verify-invitation [get]
func (c *ApiController) VerifyInvitation() {
id := c.Input().Get("id")
payment, attachInfo, err := object.VerifyInvitation(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(payment, attachInfo)
}

134
object/invitation.go Normal file
View File

@ -0,0 +1,134 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"fmt"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
)
type Invitation struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Code string `xorm:"varchar(100)" json:"code"`
Quota int `json:"quota"`
UsedCount int `json:"usedCount"`
Application string `xorm:"varchar(100)" json:"application"`
Username string `xorm:"varchar(100)" json:"username"`
Email string `xorm:"varchar(100)" json:"email"`
Phone string `xorm:"varchar(100)" json:"phone"`
SignupGroup string `xorm:"varchar(100)" json:"signupGroup"`
State string `xorm:"varchar(100)" json:"state"`
}
func GetInvitationCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Invitation{})
}
func GetInvitations(owner string) ([]*Invitation, error) {
invitations := []*Invitation{}
err := ormer.Engine.Desc("created_time").Find(&invitations, &Invitation{Owner: owner})
if err != nil {
return invitations, err
}
return invitations, nil
}
func GetPaginationInvitations(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Invitation, error) {
invitations := []*Invitation{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&invitations)
if err != nil {
return invitations, err
}
return invitations, nil
}
func getInvitation(owner string, name string) (*Invitation, error) {
if owner == "" || name == "" {
return nil, nil
}
invitation := Invitation{Owner: owner, Name: name}
existed, err := ormer.Engine.Get(&invitation)
if err != nil {
return &invitation, nil
}
if existed {
return &invitation, nil
} else {
return nil, nil
}
}
func GetInvitation(id string) (*Invitation, error) {
owner, name := util.GetOwnerAndNameFromId(id)
return getInvitation(owner, name)
}
func UpdateInvitation(id string, invitation *Invitation) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id)
if p, err := getInvitation(owner, name); err != nil {
return false, err
} else if p == nil {
return false, nil
}
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(invitation)
if err != nil {
return false, err
}
return affected != 0, nil
}
func AddInvitation(invitation *Invitation) (bool, error) {
affected, err := ormer.Engine.Insert(invitation)
if err != nil {
return false, err
}
return affected != 0, nil
}
func DeleteInvitation(invitation *Invitation) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{invitation.Owner, invitation.Name}).Delete(&Invitation{})
if err != nil {
return false, err
}
return affected != 0, nil
}
func (invitation *Invitation) GetId() string {
return fmt.Sprintf("%s/%s", invitation.Owner, invitation.Name)
}
func VerifyInvitation(id string) (payment *Payment, attachInfo map[string]interface{}, err error) {
return nil, nil, fmt.Errorf("the invitation: %s does not exist", id)
}

View File

@ -244,6 +244,11 @@ func (a *Ormer) createTable() {
panic(err)
}
err = a.Engine.Sync2(new(Invitation))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Application))
if err != nil {
panic(err)

View File

@ -91,6 +91,13 @@ func initAPI() {
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
beego.Router("/api/remove-user-from-group", &controllers.ApiController{}, "POST:RemoveUserFromGroup")
beego.Router("/api/get-invitations", &controllers.ApiController{}, "GET:GetInvitations")
beego.Router("/api/get-invitation", &controllers.ApiController{}, "GET:GetInvitation")
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")
beego.Router("/api/verify-invitation", &controllers.ApiController{}, "GET:VerifyInvitation")
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
beego.Router("/api/get-user-application", &controllers.ApiController{}, "GET:GetUserApplication")

View File

@ -31,6 +31,8 @@ import GroupListPage from "./GroupList";
import GroupTreePage from "./GroupTreePage";
import UserListPage from "./UserListPage";
import UserEditPage from "./UserEditPage";
import InvitationListPage from "./InvitationListPage";
import InvitationEditPage from "./InvitationEditPage";
import ApplicationListPage from "./ApplicationListPage";
import ApplicationEditPage from "./ApplicationEditPage";
import ProviderListPage from "./ProviderListPage";
@ -153,7 +155,7 @@ class App extends Component {
});
if (uri === "/" || uri.includes("/shortcuts") || uri.includes("/apps")) {
this.setState({selectedMenuKey: "/home"});
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/users") || uri.includes("/groups")) {
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/groups") || uri.includes("/users") || uri.includes("/invitations")) {
this.setState({selectedMenuKey: "/orgs"});
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
this.setState({selectedMenuKey: "/identity"});
@ -434,6 +436,7 @@ class App extends Component {
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
Setting.getItem(<Link to="/invitations">{i18next.t("general:Invitations")}</Link>, "/invitations"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
@ -514,6 +517,8 @@ class App extends Component {
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
<Route exact path="/invitations" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationListPage account={this.state.account} {...props} />)} />
<Route exact path="/invitations/:organizationName/:invitationName" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />

View File

@ -0,0 +1,282 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import * as InvitationBackend from "./backend/InvitationBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
const {Option} = Select;
class InvitationEditPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
invitationName: props.match.params.invitationName,
invitation: null,
organizations: [],
applications: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
};
}
UNSAFE_componentWillMount() {
this.getInvitation();
this.getOrganizations();
this.getApplicationsByOrganization(this.state.organizationName);
}
getInvitation() {
InvitationBackend.getInvitation(this.state.organizationName, this.state.invitationName)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
this.setState({
invitation: res.data,
});
});
}
getOrganizations() {
OrganizationBackend.getOrganizations("admin")
.then((res) => {
this.setState({
organizations: res.data || [],
});
});
}
getApplicationsByOrganization(organizationName) {
ApplicationBackend.getApplicationsByOrganization("admin", organizationName)
.then((res) => {
this.setState({
applications: res.data || [],
});
});
}
parseInvitationField(key, value) {
if ([""].includes(key)) {
value = Setting.myParseInt(value);
}
return value;
}
updateInvitationField(key, value) {
value = this.parseInvitationField(key, value);
const invitation = this.state.invitation;
invitation[key] = value;
this.setState({
invitation: invitation,
});
}
renderInvitation() {
const isCreatedByPlan = this.state.invitation.tag === "auto_created_invitation_for_plan";
return (
<Card size="small" title={
<div>
{this.state.mode === "add" ? i18next.t("invitation:New Invitation") : i18next.t("invitation:Edit Invitation")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button onClick={() => this.submitInvitationEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitInvitationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteInvitation()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
<Row style={{marginTop: "10px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account) || isCreatedByPlan} value={this.state.invitation.owner} onChange={(value => {this.updateInvitationField("owner", value);})}>
{
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.invitation.name} disabled={isCreatedByPlan} onChange={e => {
this.updateInvitationField("name", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.invitation.displayName} onChange={e => {
this.updateInvitationField("displayName", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("invitation:Code"), i18next.t("invitation:Code - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.invitation.code} onChange={e => {
this.updateInvitationField("code", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("invitation:Quota"), i18next.t("invitation:Quota - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber min={0} value={this.state.invitation.quota} onChange={value => {
this.updateInvitationField("quota", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("invitation:Used count"), i18next.t("invitation:Used count - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber min={0} max={this.state.invitation.quota} value={this.state.invitation.usedCount} onChange={value => {
this.updateInvitationField("usedCount", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.application}
onChange={(value => {this.updateInvitationField("application", value);})}
options={this.state.applications.map((application) => Setting.getOption(application.name, application.name))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.invitation.username} onChange={e => {
this.updateInvitationField("username", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Email"), i18next.t("general:Email - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.invitation.email} onChange={e => {
this.updateInvitationField("email", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Phone"), i18next.t("general:Phone - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.invitation.phone} onChange={e => {
this.updateInvitationField("phone", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.state} onChange={(value => {
this.updateInvitationField("state", value);
})}
options={[
{value: "Active", name: i18next.t("subscription:Active")},
{value: "Suspended", name: i18next.t("subscription:Suspended")},
].map((item) => Setting.getOption(item.name, item.value))}
/>
</Col>
</Row>
</Card>
);
}
submitInvitationEdit(exitAfterSave) {
const invitation = Setting.deepCopy(this.state.invitation);
InvitationBackend.updateInvitation(this.state.organizationName, this.state.invitationName, invitation)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully saved"));
this.setState({
invitationName: this.state.invitation.name,
});
if (exitAfterSave) {
this.props.history.push("/invitations");
} else {
this.props.history.push(`/invitations/${this.state.invitation.owner}/${this.state.invitation.name}`);
}
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
this.updateInvitationField("name", this.state.invitationName);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
deleteInvitation() {
InvitationBackend.deleteInvitation(this.state.invitation)
.then((res) => {
if (res.status === "ok") {
this.props.history.push("/invitations");
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
render() {
return (
<div>
{
this.state.invitation !== null ? this.renderInvitation() : null
}
<div style={{marginTop: "20px", marginLeft: "40px"}}>
<Button size="large" onClick={() => this.submitInvitationEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitInvitationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteInvitation()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
</div>
);
}
}
export default InvitationEditPage;

View File

@ -0,0 +1,310 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import {Link} from "react-router-dom";
import {Button, Table} from "antd";
import {MinusCircleOutlined, SyncOutlined} from "@ant-design/icons";
import moment from "moment";
import * as Setting from "./Setting";
import * as InvitationBackend from "./backend/InvitationBackend";
import i18next from "i18next";
import BaseListPage from "./BaseListPage";
import PopconfirmModal from "./common/modal/PopconfirmModal";
import copy from "copy-to-clipboard";
class InvitationListPage extends BaseListPage {
newInvitation() {
const randomName = Setting.getRandomName();
const owner = Setting.getRequestOrganization(this.props.account);
return {
owner: owner,
name: `invitation_${randomName}`,
createdTime: moment().format(),
updatedTime: moment().format(),
displayName: `New Invitation - ${randomName}`,
code: Math.random().toString(36).slice(-10),
quota: 1,
usedCount: 0,
application: "",
username: "",
email: "",
phone: "",
signupGroup: "",
state: "Active",
};
}
addInvitation() {
const newInvitation = this.newInvitation();
InvitationBackend.addInvitation(newInvitation)
.then((res) => {
if (res.status === "ok") {
this.props.history.push({pathname: `/invitations/${newInvitation.owner}/${newInvitation.name}`, mode: "add"});
Setting.showMessage("success", i18next.t("general:Successfully added"));
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
deleteInvitation(i) {
InvitationBackend.deleteInvitation(this.state.data[i])
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
renderTable(invitations) {
const columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "140px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/invitations/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "150px",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
// {
// title: i18next.t("general:Created time"),
// dataIndex: "createdTime",
// key: "createdTime",
// width: "160px",
// sorter: true,
// render: (text, record, index) => {
// return Setting.getFormattedDate(text);
// },
// },
{
title: i18next.t("general:Updated time"),
dataIndex: "updatedTime",
key: "updatedTime",
width: "160px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("general:Display name"),
dataIndex: "displayName",
key: "displayName",
width: "170px",
sorter: true,
...this.getColumnSearchProps("displayName"),
},
{
title: i18next.t("invitation:Code"),
dataIndex: "code",
key: "code",
width: "160px",
sorter: true,
...this.getColumnSearchProps("code"),
},
{
title: i18next.t("invitation:Quota"),
dataIndex: "quota",
key: "quota",
width: "120px",
sorter: true,
...this.getColumnSearchProps("quota"),
},
{
title: i18next.t("invitation:Used count"),
dataIndex: "usedCount",
key: "usedCount",
width: "130px",
sorter: true,
...this.getColumnSearchProps("usedCount"),
},
{
title: i18next.t("general:Application"),
dataIndex: "application",
key: "application",
width: "170px",
sorter: true,
...this.getColumnSearchProps("application"),
render: (text, record, index) => {
return (
<Link to={`/applications/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Email"),
dataIndex: "email",
key: "email",
width: "160px",
sorter: true,
...this.getColumnSearchProps("email"),
render: (text, record, index) => {
return (
<a href={`mailto:${text}`}>
{text}
</a>
);
},
},
{
title: i18next.t("general:Phone"),
dataIndex: "phone",
key: "phone",
width: "120px",
sorter: true,
...this.getColumnSearchProps("phone"),
},
{
title: i18next.t("general:State"),
dataIndex: "state",
key: "state",
width: "120px",
sorter: true,
...this.getColumnSearchProps("state"),
render: (text, record, index) => {
switch (text) {
case "Active":
return Setting.getTag("success", i18next.t("subscription:Active"), <SyncOutlined spin />);
case "Suspended":
return Setting.getTag("default", i18next.t("subscription:Suspended"), <MinusCircleOutlined />);
default:
return null;
}
},
},
{
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "350px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => {
copy(`${window.location.origin}/login/${record.owner}?invitation_code=${record.code}`);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}}>
{i18next.t("application:Copy signup page URL")}
</Button>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/invitations/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteInvitation(index)}
>
</PopconfirmModal>
</div>
);
},
},
];
const paginationProps = {
total: this.state.pagination.total,
showQuickJumper: true,
showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={invitations} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => (
<div>
{i18next.t("general:Invitations")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" size="small" onClick={this.addInvitation.bind(this)}>{i18next.t("general:Add")}</Button>
</div>
)}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
</div>
);
}
fetch = (params = {}) => {
let field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder;
if (params.type !== undefined && params.type !== null) {
field = "type";
value = params.type;
}
this.setState({loading: true});
InvitationBackend.getInvitations(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => {
this.setState({
loading: false,
});
if (res.status === "ok") {
this.setState({
data: res.data,
pagination: {
...params.pagination,
total: res.data2,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (Setting.isResponseDenied(res)) {
this.setState({
isAuthorized: false,
});
} else {
Setting.showMessage("error", res.msg);
}
}
});
};
}
export default InvitationListPage;

View File

@ -32,7 +32,6 @@ class PricingEditPage extends React.Component {
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
pricingName: props.match.params.pricingName,
organizations: [],
application: null,
applications: [],
pricing: null,
plans: [],

View File

@ -0,0 +1,81 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as Setting from "../Setting";
export function getInvitations(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-invitations?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function getInvitation(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-invitation?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function updateInvitation(owner, name, invitation) {
const newInvitation = Setting.deepCopy(invitation);
return fetch(`${Setting.ServerUrl}/api/update-invitation?id=${owner}/${encodeURIComponent(name)}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newInvitation),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function addInvitation(invitation) {
const newInvitation = Setting.deepCopy(invitation);
return fetch(`${Setting.ServerUrl}/api/add-invitation`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newInvitation),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function deleteInvitation(invitation) {
const newInvitation = Setting.deepCopy(invitation);
return fetch(`${Setting.ServerUrl}/api/delete-invitation`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newInvitation),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function verifyInvitation(owner, name) {
return fetch(`${Setting.ServerUrl}/api/verify-invitation?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Einzigartiger Zufallsstring",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Ist aktiviert",
"Is enabled - Tooltip": "Festlegen, ob es verwendet werden kann",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN oder ID des LDAP-Serveradministrators",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Regionsendpunkt für das Internet",
"Region endpoint for Intranet": "Regionales Endpunkt für Intranet",
"Required": "Benötigt",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpunkt (HTTP)",
"SMS Test": "SMS-Test",
"SMS Test - Tooltip": "Telefonnummer für den Versand von Test-SMS",
"SMS account": "SMS-Konto",
"SMS account - Tooltip": "SMS-Konto",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP-ACS-URL",
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
"SP Entity ID": "SP-Entitäts-ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Can be a single string as an invitation code, or a regular expression. All strings matching the regular expression are valid invitation codes",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "The maximum number of users that can register using this invitation code",
"Used count": "Used count",
"Used count - Tooltip": "The number of times this invitation code has been used"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "identificación",
"ID - Tooltip": "Cadena aleatoria única",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Está habilitado",
"Is enabled - Tooltip": "Establecer si se puede usar",
"LDAPs": "LDAPs (Secure LDAP)",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Administrador",
"Admin - Tooltip": "CN o ID del administrador del servidor LDAP",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Punto final de la región para Internet",
"Region endpoint for Intranet": "Punto final de región para Intranet",
"Required": "Requerido",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "Punto final de SAML 2.0 (HTTP)",
"SMS Test": "Prueba de SMS",
"SMS Test - Tooltip": "Número de teléfono para enviar mensajes de texto de prueba",
"SMS account": "Cuenta de SMS",
"SMS account - Tooltip": "Cuenta de SMS",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "URL del ACS de SP",
"SP Entity ID": "ID de entidad SP",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Chaîne unique aléatoire",
"Identity": "Identité",
"Invitations": "Invitations",
"Is enabled": "Est activé",
"Is enabled - Tooltip": "Définir s'il peut être utilisé",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "30 derniers jours",
"Total users": "Nombre total de comptes"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Compte d'administration",
"Admin - Tooltip": "CN ou ID du compte d'administration du serveur LDAP",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Endpoint de zone géographique pour Internet",
"Region endpoint for Intranet": "Endpoint de zone géographique pour Internet",
"Required": "Requis",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "Endpoint SAML 2.0 (HTTP)",
"SMS Test": "Test SMS",
"SMS Test - Tooltip": "Numéro de téléphone pour l'envoi de SMS de test",
"SMS account": "compte SMS",
"SMS account - Tooltip": "Compte SMS",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "URL du SP ACS",
"SP ACS URL - Tooltip": "URL de l'ACS du fournisseur de service",
"SP Entity ID": "Identifiant d'entité SP",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Karakter acak unik",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Diaktifkan",
"Is enabled - Tooltip": "Atur apakah itu dapat digunakan",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN atau ID dari administrator server LDAP",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Titik akhir wilayah untuk Internet",
"Region endpoint for Intranet": "Titik akhir wilayah untuk Intranet",
"Required": "Dibutuhkan",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "Titik akhir SAML 2.0 (HTTP)",
"SMS Test": "Pengujian SMS",
"SMS Test - Tooltip": "Nomor telepon untuk mengirim SMS uji",
"SMS account": "akun SMS",
"SMS account - Tooltip": "Akun SMS",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "Identitas Entitas SP",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "ユニークなランダム文字列",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "可能になっています",
"Is enabled - Tooltip": "使用可能かどうかを設定してください",
"LDAPs": "LDAP",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "管理者",
"Admin - Tooltip": "LDAPサーバー管理者のCNまたはID",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "インターネットのリージョンエンドポイント",
"Region endpoint for Intranet": "Intranetの地域エンドポイント",
"Required": "必要です",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 エンドポイントHTTP",
"SMS Test": "SMSテスト",
"SMS Test - Tooltip": "テストSMSの送信先電話番号",
"SMS account": "SMSアカウント",
"SMS account - Tooltip": "SMSアカウント",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL - ツールチップ",
"SP Entity ID": "SPエンティティID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "유일한 랜덤 문자열",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "활성화됩니다",
"Is enabled - Tooltip": "사용 가능한 지 여부를 설정하세요",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "LDAP 서버 관리자의 CN 또는 ID",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "인터넷 지역 엔드포인트",
"Region endpoint for Intranet": "인트라넷의 지역 엔드포인트",
"Required": "필요한",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 엔드포인트 (HTTP)",
"SMS Test": "SMS 테스트",
"SMS Test - Tooltip": "테스트 SMS를 보내는 전화번호",
"SMS account": "SMS 계정",
"SMS account - Tooltip": "SMS 계정",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
"SP Entity ID": "SP 개체 ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "String única aleatória",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Está habilitado",
"Is enabled - Tooltip": "Define se está habilitado",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Administrador",
"Admin - Tooltip": "CN ou ID do administrador do servidor LDAP",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Endpoint da região para a Internet",
"Region endpoint for Intranet": "Endpoint da região para Intranet",
"Required": "Obrigatório",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "Ponto de extremidade SAML 2.0 (HTTP)",
"SMS Test": "Teste de SMS",
"SMS Test - Tooltip": "Número de telefone para enviar SMS de teste",
"SMS account": "Conta SMS",
"SMS account - Tooltip": "Conta SMS",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "URL SP ACS",
"SP ACS URL - Tooltip": "URL SP ACS",
"SP Entity ID": "ID da Entidade SP",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Уникальная случайная строка",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Включен",
"Is enabled - Tooltip": "Установить, может ли использоваться",
"LDAPs": "LDAPы",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Админ",
"Admin - Tooltip": "CN или ID администратора сервера LDAP",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Региональный конечная точка для Интернета",
"Region endpoint for Intranet": "Региональный конечный пункт для интранета",
"Required": "Требуется",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "Конечная точка SAML 2.0 (HTTP)",
"SMS Test": "СМС тест",
"SMS Test - Tooltip": "Номер телефона для отправки тестовых SMS сообщений",
"SMS account": "СМС-аккаунт",
"SMS account - Tooltip": "СМС-аккаунт",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL - Подсказка",
"SP Entity ID": "Идентификатор сущности SP",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "Chuỗi ngẫu nhiên độc nhất",
"Identity": "Identity",
"Invitations": "Invitations",
"Is enabled": "Đã được kích hoạt",
"Is enabled - Tooltip": "Đặt liệu nó có thể sử dụng hay không",
"LDAPs": "LDAPs",
@ -383,6 +384,16 @@
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"invitation": {
"Code": "Code",
"Code - Tooltip": "Code - Tooltip",
"Edit Invitation": "Edit Invitation",
"New Invitation": "New Invitation",
"Quota": "Quota",
"Quota - Tooltip": "Quota - Tooltip",
"Used count": "Used count",
"Used count - Tooltip": "Used count - Tooltip"
},
"ldap": {
"Admin": "Quản trị",
"Admin - Tooltip": "CN hoặc ID của quản trị viên máy chủ LDAP",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "Điểm cuối khu vực cho Internet",
"Region endpoint for Intranet": "Điểm cuối khu vực cho mạng nội bộ",
"Required": "Yêu cầu",
"Reset to Default HTML": "Reset to Default HTML",
"Reset to Default Text": "Reset to Default Text",
"SAML 2.0 Endpoint (HTTP)": "Điểm cuối SAML 2.0 (HTTP)",
"SMS Test": "Kiểm tra SMS",
"SMS Test - Tooltip": "Số điện thoại để gửi tin nhắn kiểm tra",
"SMS account": "Tài khoản SMS",
"SMS account - Tooltip": "Tài khoản SMS",
"SMTP connected successfully": "SMTP connected successfully",
"SP ACS URL": "SP ACC URL",
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
"SP Entity ID": "SP Entity ID: Định danh thực thể SP",

View File

@ -242,6 +242,7 @@
"ID": "ID",
"ID - Tooltip": "唯一的随机字符串",
"Identity": "身份认证",
"Invitations": "邀请码",
"Is enabled": "已启用",
"Is enabled - Tooltip": "是否启用",
"LDAPs": "LDAP",
@ -383,6 +384,16 @@
"Past 30 Days": "过去 30 天",
"Total users": "用户总数"
},
"invitation": {
"Code": "邀请码",
"Code - Tooltip": "可以是一个单独的字符串作为邀请码,也可以是正则表达式,所有符合正则表达式的字符串都是合法的邀请码",
"Edit Invitation": "编辑邀请码",
"New Invitation": "新建邀请码",
"Quota": "配额",
"Quota - Tooltip": "该邀请码最多能注册多少个用户",
"Used count": "已使用个数",
"Used count - Tooltip": "该邀请码已使用次数"
},
"ldap": {
"Admin": "管理员",
"Admin - Tooltip": "LDAP服务器管理员的CN或ID",
@ -751,11 +762,14 @@
"Region endpoint for Internet": "地域节点 (外网)",
"Region endpoint for Intranet": "地域节点 (内网)",
"Required": "是否必填项",
"Reset to Default HTML": "重置为默认HTML",
"Reset to Default Text": "重置为默认纯文本",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 端点 (HTTP)",
"SMS Test": "测试短信配置",
"SMS Test - Tooltip": "请输入测试手机号",
"SMS account": "短信账户",
"SMS account - Tooltip": "SMS account - Tooltip",
"SMTP connected successfully": "SMTP连接成功",
"SP ACS URL": "SP ACS 网址",
"SP ACS URL - Tooltip": "SP ACS URL - 工具提示",
"SP Entity ID": "SP 实体 ID",