Compare commits

...

1 Commits

Author SHA1 Message Date
DacongDA
a23033758f feat: Add "Send Invitation Email" action to User Invitation flow (#4113) 2025-08-21 18:53:43 +08:00
68 changed files with 424 additions and 29 deletions

View File

@@ -16,6 +16,8 @@ package controllers
import (
"encoding/json"
"fmt"
"strings"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
@@ -188,3 +190,73 @@ func (c *ApiController) VerifyInvitation() {
c.ResponseOk(payment, attachInfo)
}
// SendInvitation
// @Title VerifyInvitation
// @Tag Invitation API
// @Description verify invitation
// @Param id query string true "The id ( owner/name ) of the invitation"
// @Param body body []string true "The details of the invitation"
// @Success 200 {object} controllers.Response The Response object
// @router /send-invitation [post]
func (c *ApiController) SendInvitation() {
id := c.Input().Get("id")
var destinations []string
err := json.Unmarshal(c.Ctx.Input.RequestBody, &destinations)
if !c.IsAdmin() {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
invitation, err := object.GetInvitation(id)
if err != nil {
c.ResponseError(err.Error())
return
}
if invitation == nil {
c.ResponseError(fmt.Sprintf(c.T("invitation:Invitation %s does not exist"), id))
return
}
organization, err := object.GetOrganization(fmt.Sprintf("admin/%s", invitation.Owner))
if err != nil {
c.ResponseError(err.Error())
return
}
application, err := object.GetApplicationByOrganizationName(invitation.Owner)
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The organization: %s should have one application at least"), invitation.Owner))
return
}
provider, err := application.GetEmailProvider("Invitation")
if err != nil {
c.ResponseError(err.Error())
return
}
if provider == nil {
c.ResponseError(fmt.Sprintf(c.T("verification:please add an Email provider to the \"Providers\" list for the application: %s"), invitation.Owner))
return
}
content := provider.Metadata
content = strings.ReplaceAll(content, "%code", invitation.Code)
content = strings.ReplaceAll(content, "%link", invitation.GetInvitationLink(c.Ctx.Request.Host, application.Name))
err = object.SendEmail(provider, provider.Title, content, destinations, organization.DisplayName)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
}

View File

@@ -144,7 +144,7 @@ func (c *ApiController) SendEmail() {
content = strings.Replace(content, string(matchContent), "", -1)
for _, receiver := range emailForm.Receivers {
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
err = object.SendEmail(provider, emailForm.Title, content, []string{receiver}, emailForm.Sender)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -96,15 +96,17 @@ func NewAzureACSEmailProvider(accessKey string, endpoint string) *AzureACSEmailP
}
}
func newEmail(fromAddress string, toAddress string, subject string, content string) *Email {
func newEmail(fromAddress string, toAddress []string, subject string, content string) *Email {
var to []EmailAddress
for _, addr := range toAddress {
to = append(to, EmailAddress{
DisplayName: addr,
Address: addr,
})
}
return &Email{
Recipients: Recipients{
To: []EmailAddress{
{
DisplayName: toAddress,
Address: toAddress,
},
},
To: to,
},
SenderAddress: fromAddress,
Content: Content{
@@ -116,7 +118,7 @@ func newEmail(fromAddress string, toAddress string, subject string, content stri
}
}
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error {
email := newEmail(fromAddress, toAddress, subject, content)
postBody, err := json.Marshal(email)

View File

@@ -48,21 +48,23 @@ func NewHttpEmailProvider(endpoint string, method string, httpHeaders map[string
return client
}
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error {
var req *http.Request
var err error
fromNameField := "fromName"
toAddressField := "toAddress"
toAddressesField := "toAddresses"
subjectField := "subject"
contentField := "content"
for k, v := range c.bodyMapping {
switch k {
case "fromName":
fromNameField = v
case "toAddress":
toAddressField = v
case "toAddresses":
toAddressesField = v
case "subject":
subjectField = v
case "content":
@@ -73,7 +75,6 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
if c.method == "POST" || c.method == "PUT" || c.method == "DELETE" {
bodyMap := make(map[string]string)
bodyMap[fromNameField] = fromName
bodyMap[toAddressField] = toAddress
bodyMap[subjectField] = subject
bodyMap[contentField] = content
@@ -89,6 +90,13 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
for k, v := range bodyMap {
formValues.Add(k, v)
}
if len(toAddress) == 1 {
formValues.Add(toAddressField, toAddress[0])
} else {
for _, addr := range toAddress {
formValues.Add(toAddressesField, addr)
}
}
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
}
@@ -105,7 +113,13 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
q := req.URL.Query()
q.Add(fromNameField, fromName)
q.Add(toAddressField, toAddress)
if len(toAddress) == 1 {
q.Add(toAddressField, toAddress[0])
} else {
for _, addr := range toAddress {
q.Add(toAddressesField, addr)
}
}
q.Add(subjectField, subject)
q.Add(contentField, content)
req.URL.RawQuery = q.Encode()

View File

@@ -15,7 +15,7 @@
package email
type EmailProvider interface {
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error
}
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string) EmailProvider {

View File

@@ -41,13 +41,22 @@ func NewSendgridEmailProvider(apiKey string, host string, endpoint string) *Send
return &SendgridEmailProvider{ApiKey: apiKey, Host: host, Endpoint: endpoint}
}
func (s *SendgridEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
client := s.initSendgridClient()
func (s *SendgridEmailProvider) Send(fromAddress string, fromName string, toAddresses []string, subject string, content string) error {
from := mail.NewEmail(fromName, fromAddress)
to := mail.NewEmail("", toAddress)
message := mail.NewSingleEmail(from, subject, to, "", content)
message := mail.NewV3Mail()
message.SetFrom(from)
message.AddContent(mail.NewContent("text/html", content))
personalization := mail.NewPersonalization()
for _, toAddress := range toAddresses {
to := mail.NewEmail(toAddress, toAddress)
personalization.AddTos(to)
}
message.AddPersonalizations(personalization)
client := s.initSendgridClient()
resp, err := client.Send(message)
if err != nil {
return err

View File

@@ -44,11 +44,15 @@ func NewSmtpEmailProvider(userName string, password string, host string, port in
return &SmtpEmailProvider{Dialer: dialer}
}
func (s *SmtpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
func (s *SmtpEmailProvider) Send(fromAddress string, fromName string, toAddresses []string, subject string, content string) error {
message := gomail.NewMessage()
message.SetAddressHeader("From", fromAddress, fromName)
message.SetHeader("To", toAddress)
var addresses []string
for _, address := range toAddresses {
addresses = append(addresses, message.FormatAddress(address, ""))
}
message.SetHeader("To", addresses...)
message.SetHeader("Subject", subject)
message.SetBody("text/html", content)

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "هذه العملية غير مسموح بها في وضع العرض التوضيحي",
"this operation requires administrator to perform": "هذه العملية تتطلب مسؤولاً لتنفيذها"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "خادم LDAP موجود"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "bu əməliyyat demo rejimində icazə verilmir",
"this operation requires administrator to perform": "bu əməliyyat administrator tərəfindən yerinə yetirilməsini tələb edir"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP serveri mövcuddur"
},
@@ -190,4 +193,4 @@
"Found no credentials for this user": "Bu istifadəçi üçün heç bir etimadnamə tapılmadı",
"Please call WebAuthnSigninBegin first": "Xahiş edirik əvvəlcə WebAuthnSigninBegin çağırın"
}
}
}

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "tato operace není povolena v demo režimu",
"this operation requires administrator to perform": "tato operace vyžaduje administrátora k provedení"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Ldap server existuje"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "Dieser Vorgang ist im Demo-Modus nicht erlaubt",
"this operation requires administrator to perform": "Dieser Vorgang erfordert einen Administrator zur Ausführung"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Es gibt einen LDAP-Server"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode",
"this operation requires administrator to perform": "this operation requires administrator to perform"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "esta operación no está permitida en modo de demostración",
"this operation requires administrator to perform": "esta operación requiere que el administrador la realice"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "El servidor LDAP existe"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "این عملیات در حالت دمو مجاز نیست",
"this operation requires administrator to perform": "این عملیات نیاز به مدیر برای انجام دارد"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "سرور LDAP وجود دارد"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "tämä toiminto ei ole sallittu demo-tilassa",
"this operation requires administrator to perform": "tämä toiminto vaatii ylläpitäjän suorittamista"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP-palvelin on olemassa"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "cette opération n'est pas autorisée en mode démo",
"this operation requires administrator to perform": "cette opération nécessite un administrateur pour être effectuée"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Le serveur LDAP existe"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "פעולה זו אינה מותרת במצב הדגמה",
"this operation requires administrator to perform": "פעולה זו דורשת מנהל לביצוע"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "שרת LDAP קיים"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "operasi ini tidak diizinkan dalam mode demo",
"this operation requires administrator to perform": "operasi ini memerlukan administrator untuk melakukannya"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Server ldap ada"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "questa operazione non è consentita in modalità demo",
"this operation requires administrator to perform": "questa operazione richiede un amministratore"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Server LDAP esistente"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "この操作はデモモードでは許可されていません",
"this operation requires administrator to perform": "この操作は管理者権限が必要です"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAPサーバーは存在します"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "deze handeling is niet toegestaan in demo-modus",
"this operation requires administrator to perform": "deze handeling vereist beheerder"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP-server bestaat al"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "이 작업은 데모 모드에서 허용되지 않습니다",
"this operation requires administrator to perform": "이 작업은 관리자 권한이 필요합니다"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP 서버가 존재합니다"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "operasi ini tidak dibenarkan dalam mod demo",
"this operation requires administrator to perform": "operasi ini perlukan pentadbir untuk jalankan"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Pelayan LDAP sudah wujud"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "Handeling niet toegestaan in demo-modus",
"this operation requires administrator to perform": "Alleen beheerder kan deze handeling uitvoeren"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP-server bestaat al"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "ta operacja nie jest dozwolona w trybie demo",
"this operation requires administrator to perform": "ta operacja wymaga administratora do wykonania"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Serwer LDAP istnieje"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "esta operação não é permitida no modo de demonstração",
"this operation requires administrator to perform": "esta operação requer um administrador para executar"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Servidor LDAP existe"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "эта операция недоступна в демонстрационном режиме",
"this operation requires administrator to perform": "эта операция требует прав администратора"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP-сервер существует"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "táto operácia nie je povolená v demo režime",
"this operation requires administrator to perform": "táto operácia vyžaduje vykonanie administrátorom"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP server existuje"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "denna åtgärd är inte tillåten i demoläge",
"this operation requires administrator to perform": "denna åtgärd kräver administratör för att genomföras"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP-servern finns redan"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "bu işlem demo modunda izin verilmiyor",
"this operation requires administrator to perform": "bu işlem yönetici tarafından gerçekleştirilmelidir"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "LDAP sunucusu zaten var"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "ця операція недоступна в демо-режимі",
"this operation requires administrator to perform": "ця операція потребує прав адміністратора"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Сервер LDAP існує"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "thao tác này không được phép trong chế độ demo",
"this operation requires administrator to perform": "thao tác này yêu cầu quản trị viên thực hiện"
},
"invitation": {
"Invitation %s does not exist": "Invitation %s does not exist"
},
"ldap": {
"Ldap server exist": "Máy chủ LDAP tồn tại"
},

View File

@@ -106,6 +106,9 @@
"this operation is not allowed in demo mode": "demo模式下不允许该操作",
"this operation requires administrator to perform": "只有管理员才能进行此操作"
},
"invitation": {
"Invitation %s does not exist": "邀请%s不存在"
},
"ldap": {
"Ldap server exist": "LDAP服务器已存在"
},

View File

@@ -30,7 +30,7 @@ func TestSmtpServer(provider *Provider) error {
return nil
}
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
func SendEmail(provider *Provider, title string, content string, dest []string, sender string) error {
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl)
fromAddress := provider.ClientId2

View File

@@ -235,3 +235,8 @@ func (invitation *Invitation) IsInvitationCodeValid(application *Application, in
}
return true, ""
}
func (invitation *Invitation) GetInvitationLink(host string, application string) string {
frontEnd, _ := getOriginFromHost(host)
return fmt.Sprintf("%s/signup/%s?invitationCode=%s", frontEnd, application, invitation.Code)
}

View File

@@ -129,7 +129,7 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
return err
}
err = SendEmail(provider, title, content, dest, sender)
err = SendEmail(provider, title, content, []string{dest}, sender)
if err != nil {
return err
}

View File

@@ -91,7 +91,7 @@ func getObject(ctx *context.Context) (string, string, error) {
return "", "", nil
} else {
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" {
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" || path == "/api/send-invitation" {
id := ctx.Input.Query("id")
if id != "" {
return util.GetOwnerAndNameFromIdWithError(id)

View File

@@ -102,6 +102,7 @@ func initAPI() {
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/send-invitation", &controllers.ApiController{}, "POST:SendInvitation")
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")

View File

@@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import {Button, Card, Col, Input, InputNumber, Modal, Row, Select, Table} from "antd";
import {CopyOutlined} from "@ant-design/icons";
import * as InvitationBackend from "./backend/InvitationBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
@@ -37,6 +37,7 @@ class InvitationEditPage extends React.Component {
applications: [],
groups: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
sendLoading: false,
};
}
@@ -123,6 +124,35 @@ class InvitationEditPage extends React.Component {
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}
renderSendEmailModal() {
const emailColumns = [
{title: "email", dataIndex: "email"},
];
const emails = this.state.emails?.split("\n")?.filter(email => Setting.isValidEmail(email));
const emailData = emails?.map((email) => {return {email: email};});
return <Modal title={i18next.t("general:Send")}
style={{height: "800px"}}
open={this.state.showSendModal}
closable
footer={[
<Button key={1} loading={this.state.sendLoading} type="primary"
onClick={() => {
this.setState({sendLoading: true});
InvitationBackend.sendInvitation(this.state.invitation, emails).then(() => {
this.setState({sendLoading: false});
Setting.showMessage("success", i18next.t("general:Successfully sent"));
}).catch(err => Setting.showMessage("success", err.message));
}}>{i18next.t("general:Send")}</Button>,
]}
onCancel={() => {this.setState({showSendModal: false});}}>
<div >
<p>You will send invitation email to:</p>
<Table showHeader={false} columns={emailColumns} dataSource={emailData} size={"small"}></Table>
</div>
</Modal>;
}
renderInvitation() {
const isCreatedByPlan = this.state.invitation.tag === "auto_created_invitation_for_plan";
return (
@@ -199,6 +229,17 @@ class InvitationEditPage extends React.Component {
</Button>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("general:Send")}
</Col>
<Col span={22} >
<Input.TextArea autoSize={{minRows: 3, maxRows: 10}} value={this.state.emails} onChange={(value) => {
this.setState({emails: value.target.value});
}}></Input.TextArea>
<Button type="primary" style={{marginTop: "20px"}} onClick={() => this.setState({showSendModal: true})}>{i18next.t("general:Send")}</Button>
</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"))} :
@@ -338,6 +379,7 @@ class InvitationEditPage extends React.Component {
render() {
return (
<div>
{this.state.showSendModal ? this.renderSendEmailModal() : null}
{
this.state.invitation !== null ? this.renderInvitation() : null
}

View File

@@ -597,6 +597,7 @@ class ProviderEditPage extends React.Component {
this.updateProviderField("disableSsl", false);
this.updateProviderField("title", "Casdoor Verification Code");
this.updateProviderField("content", Setting.getDefaultHtmlEmailContent());
this.updateProviderField("metadata", Setting.getDefaultInvitationHtmlEmailContent());
this.updateProviderField("receiver", this.props.account.email);
} else if (value === "SMS") {
this.updateProviderField("type", "Twilio SMS");
@@ -1271,6 +1272,42 @@ class ProviderEditPage extends React.Component {
</Row>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(`${i18next.t("provider:Email content")}-${i18next.t("general:Invitations")}`, i18next.t("provider:Email content - Tooltip"))} :
</Col>
<Col span={22} >
<Row style={{marginTop: "20px"}} >
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateProviderField("metadata", "You have invited to join Casdoor. Here is your invitation code: %s, please enter in 5 minutes. Or click %link to signup")} >
{i18next.t("provider:Reset to Default Text")}
</Button>
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => this.updateProviderField("metadata", Setting.getDefaultInvitationHtmlEmailContent())} >
{i18next.t("provider:Reset to Default HTML")}
</Button>
</Row>
<Row>
<Col span={Setting.isMobile() ? 22 : 11}>
<div style={{height: "300px", margin: "10px"}}>
<Editor
value={this.state.provider.metadata}
fillHeight
dark
lang="html"
onChange={value => {
this.updateProviderField("metadata", value);
}}
/>
</div>
</Col>
<Col span={1} />
<Col span={Setting.isMobile() ? 22 : 11}>
<div style={{margin: "10px"}}>
<div dangerouslySetInnerHTML={{__html: this.state.provider.metadata.replace("%code", "123456")}} />
</div>
</Col>
</Row>
</Col>
</Row>
<Row style={{marginTop: "20px"}}>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Test Email"), i18next.t("provider:Test Email - Tooltip"))} :

View File

@@ -1648,6 +1648,48 @@ export function getDefaultHtmlEmailContent() {
</html>`;
}
export function getDefaultInvitationHtmlEmailContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Invitation Code Email</title>
<style>
body { font-family: Arial, sans-serif; }
.email-container { width: 600px; margin: 0 auto; }
.header { text-align: center; }
.code { font-size: 24px; margin: 20px 0; text-align: center; }
.footer { font-size: 12px; text-align: center; margin-top: 50px; }
.footer a { color: #000; text-decoration: none; }
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<h3>Casbin Organization</h3>
<img src="${StaticBaseUrl}/img/casdoor-logo_1185x256.png" alt="Casdoor Logo" width="300">
</div>
<p>You have been invited into Casdoor</p>
<div class="code">
%code
</div>
<reset-link>
<div class="link">
Or click this <a href="%link">link</a> to signup
</div>
</reset-link>
<p>Thanks</p>
<p>Casbin Team</p>
<hr>
<div class="footer">
<p>Casdoor is a brand operated by Casbin organization. For more info please refer to <a href="https://casdoor.org">https://casdoor.org</a></p>
</div>
</div>
</body>
</html>`;
}
export function getCurrencyText(product) {
if (product?.currency === "USD") {
return i18next.t("currency:USD");

View File

@@ -89,3 +89,14 @@ export function verifyInvitation(owner, name) {
},
}).then(res => res.json());
}
export function sendInvitation(invitation, destinations) {
return fetch(`${Setting.ServerUrl}/api/send-invitation?id=${invitation.owner}/${encodeURIComponent(invitation.name)}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(destinations),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@@ -307,6 +307,7 @@
"Favicon": "الرمز المفضل",
"Favicon - Tooltip": "رابط رمز الموقع المستخدم في جميع صفحات Casdoor الخاصة بالمنظمة",
"First name": "الاسم الأول",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "إعادة توجيه الأصل بالقوة - تلميح",
"Forget URL": "رابط نسيان كلمة المرور",
"Forget URL - Tooltip": "رابط مخصص لصفحة \"نسيان كلمة المرور\". إذا لم يتم تعيينه، ستُستخدم صفحة Casdoor الافتراضية. عند التعيين، ستُعيد توجيه روابط \"نسيان كلمة المرور\" إلى هذا الرابط",
@@ -333,6 +334,7 @@
"Languages": "اللغات",
"Languages - Tooltip": "اللغات المتاحة",
"Last name": "الاسم الأخير",
"Last name - Tooltip": "The last name of user",
"Later": "لاحقًا",
"Logging & Auditing": "التسجيل والمراجعة",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "نوع المصادقة لاتصال SSH",
"Save": "حفظ",
"Save & Exit": "حفظ وخروج",
"Send": "Send",
"Session ID": "معرف الجلسة",
"Sessions": "الجلسات",
"Shortcuts": "الاختصارات",

View File

@@ -96,7 +96,7 @@
"Order - Tooltip": "Order - Tooltip",
"Org choice mode": "Təşkilat seçim rejimi",
"Org choice mode - Tooltip": "Təşkilat seçim rejimi - Tooltip",
"Please enable \"Signin session\" first before enabling \"Auto signin\"": "\"Avtomatik giriş\"i aktiv etməzdən əvvəl əvvəlcə \"Giriş sessiyası\"nı aktiv edin",
"Please enable \\\"Signin session\\\" first before enabling \\\"Auto signin\\\"": "Najprv povoľte \\\"Reláciu prihlásenia\\\" pred povolením \\\"Automatického prihlásenia\\\"",
"Please input your application!": "Xahiş edirik tətbiqinizi daxil edin!",
"Please input your organization!": "Xahiş edirik təşkilatınızı daxil edin!",
"Please select a HTML file": "Xahiş edirik HTML faylı seçin",
@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "Təşkilatın bütün Casdoor səhifələrində istifadə olunan favicon ikon URL-i",
"First name": "Ad",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Məcburi yönləndirmə mənşəyi - Tooltip",
"Forget URL": "Unutma URL-i",
"Forget URL - Tooltip": "\"Şifrəni unudun\" səhifəsi üçün xüsusi URL. Təyin edilməsə, defolt Casdoor \"Şifrəni unudun\" səhifəsi istifadə ediləcək. Təyin edildikdə, giriş səhifəsindəki \"Şifrəni unudun\" linki bu URL-ə yönləndirəcək",
@@ -333,6 +334,7 @@
"Languages": "Dillər",
"Languages - Tooltip": "Mövcud dillər",
"Last name": "Soyad",
"Last name - Tooltip": "The last name of user",
"Later": "Sonra",
"Logging & Auditing": "Loglaşdırma və Audit",
"Login page": "Giriş səhifəsi",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH bağlantısının auth növü",
"Save": "Yadda saxla",
"Save & Exit": "Yadda saxla və Çıx",
"Send": "Send",
"Session ID": "Sessiya ID",
"Sessions": "Sessiyalar",
"Shortcuts": "Qısayollar",
@@ -488,7 +491,7 @@
"Show all": "Hamısını göstər",
"Upload (.xlsx)": "Yüklə (.xlsx)",
"Virtual": "Virtual",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Əvvəlcə bütün alt qrupları silməlisiniz. Alt qrupları [Təşkilatlar] -> [Qruplar] səhifəsinin sol qrup ağacında görə bilərsiniz"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Əvvəlcə bütün alt qrupları silməlisiniz. Alt qrupları [Təşkilatlar] -\u003e [Qruplar] səhifəsinin sol qrup ağacında görə bilərsiniz"
},
"home": {
"New users past 30 days": "Son 30 gündə yeni istifadəçilər",
@@ -1328,4 +1331,4 @@
"Single org only - Tooltip": "Yalnız webhook-un aid olduğu təşkilatda tetiklənir",
"Value": "Dəyər"
}
}
}

View File

@@ -307,6 +307,7 @@
"Favicon": "Ikona webu",
"Favicon - Tooltip": "URL ikony favicon použité na všech stránkách Casdoor organizace",
"First name": "Křestní jméno",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Vynucený původ přesměrování - popisek",
"Forget URL": "URL pro zapomenutí",
"Forget URL - Tooltip": "Vlastní URL pro stránku \"Zapomenuté heslo\". Pokud není nastaveno, bude použita výchozí stránka Casdoor \"Zapomenuté heslo\". Když je nastaveno, odkaz \"Zapomenuté heslo\" na přihlašovací stránce přesměruje na tuto URL",
@@ -333,6 +334,7 @@
"Languages": "Jazyky",
"Languages - Tooltip": "Dostupné jazyky",
"Last name": "Příjmení",
"Last name - Tooltip": "The last name of user",
"Later": "Později",
"Logging & Auditing": "Logování a audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Typ ověření SSH připojení",
"Save": "Uložit",
"Save & Exit": "Uložit & Ukončit",
"Send": "Send",
"Session ID": "ID relace",
"Sessions": "Relace",
"Shortcuts": "Zkratky",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "Favicon-URL, die auf allen Casdoor-Seiten der Organisation verwendet wird",
"First name": "Vorname",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Erzwungene Weiterleitung Ursprung Tooltip",
"Forget URL": "Passwort vergessen URL",
"Forget URL - Tooltip": "Benutzerdefinierte URL für die \"Passwort vergessen\" Seite. Wenn nicht festgelegt, wird die standardmäßige Casdoor \"Passwort vergessen\" Seite verwendet. Wenn sie festgelegt ist, wird der \"Passwort vergessen\" Link auf der Login-Seite zu dieser URL umgeleitet",
@@ -333,6 +334,7 @@
"Languages": "Sprachen",
"Languages - Tooltip": "Verfügbare Sprachen",
"Last name": "Nachname",
"Last name - Tooltip": "The last name of user",
"Later": "Später",
"Logging & Auditing": "Protokollierung & Audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Der Authentifizierungstyp für SSH-Verbindungen",
"Save": "Speichern",
"Save & Exit": "Speichern und verlassen",
"Send": "Send",
"Session ID": "Session-ID",
"Sessions": "Sitzungen",
"Shortcuts": "Verknüpfungen",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "Favicon icon URL used in all Casdoor pages of the organization",
"First name": "First name",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Forced redirect origin - Tooltip",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Custom URL for the \"Forget password\" page. If not set, the default Casdoor \"Forget password\" page will be used. When set, the \"Forget password\" link on the login page will redirect to this URL",
@@ -333,6 +334,7 @@
"Languages": "Languages",
"Languages - Tooltip": "Available languages",
"Last name": "Last name",
"Last name - Tooltip": "The last name of user",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "The auth type of SSH connection",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Send": "Send",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Shortcuts": "Shortcuts",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "URL del icono Favicon utilizado en todas las páginas de Casdoor de la organización",
"First name": "Nombre de pila",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Origen de redirección forzada - Información adicional",
"Forget URL": "Olvide la URL",
"Forget URL - Tooltip": "URL personalizada para la página \"Olvidé mi contraseña\". Si no se establece, se utilizará la página \"Olvidé mi contraseña\" predeterminada de Casdoor. Cuando se establezca, el enlace \"Olvidé mi contraseña\" en la página de inicio de sesión redireccionará a esta URL",
@@ -333,6 +334,7 @@
"Languages": "Idiomas",
"Languages - Tooltip": "Idiomas disponibles",
"Last name": "Apellido",
"Last name - Tooltip": "The last name of user",
"Later": "Más tarde",
"Logging & Auditing": "Registro y auditoría",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "El tipo de autenticación de conexión SSH",
"Save": "Guardar",
"Save & Exit": "Guardar y salir",
"Send": "Send",
"Session ID": "ID de sesión",
"Sessions": "Sesiones",
"Shortcuts": "Accesos directos",

View File

@@ -307,6 +307,7 @@
"Favicon": "آیکون وب",
"Favicon - Tooltip": "آدرس آیکون Favicon استفاده شده در تمام صفحات Casdoor سازمان",
"First name": "نام",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "منبع بازگرداندن اجباری - توصیه",
"Forget URL": "آدرس فراموشی",
"Forget URL - Tooltip": "آدرس سفارشی برای صفحه \"فراموشی رمز عبور\". اگر تنظیم نشده باشد، صفحه پیش‌فرض \"فراموشی رمز عبور\" Casdoor استفاده می‌شود. هنگامی که تنظیم شده باشد، لینک \"فراموشی رمز عبور\" در صفحه ورود به این آدرس هدایت می‌شود",
@@ -333,6 +334,7 @@
"Languages": "زبان‌ها",
"Languages - Tooltip": "زبان‌های موجود",
"Last name": "نام خانوادگی",
"Last name - Tooltip": "The last name of user",
"Later": "بعداً",
"Logging & Auditing": "ورود و حسابرسی",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "نوع احراز هویت اتصال SSH",
"Save": "ذخیره",
"Save & Exit": "ذخیره و خروج",
"Send": "Send",
"Session ID": "شناسه جلسه",
"Sessions": "جلسات",
"Shortcuts": "میانبرها",

View File

@@ -307,6 +307,7 @@
"Favicon": "Sivuston ikoni",
"Favicon - Tooltip": "Favicon-kuvakkeen URL, jota käytetään kaikissa Casdoor-sivuissa organisaatiolle",
"First name": "Etunimi",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Pakotettu uudelleenohjaus alkuperä - työkalupala",
"Forget URL": "Unohtamisen URL",
"Forget URL - Tooltip": "Mukautettu URL \"Unohtunut salasana\" -sivulle. Jos ei aseteta, käytetään oletusarvoista Casdoor \"Unohtunut salasana\" -sivua. Kun asetetaan, \"Unohtunut salasana\" -linkki kirjautumissivulla ohjaa tähän URL-osoitteeseen",
@@ -333,6 +334,7 @@
"Languages": "Kielet",
"Languages - Tooltip": "Saatavilla olevat kielet",
"Last name": "Sukunimi",
"Last name - Tooltip": "The last name of user",
"Later": "Myöhemmin",
"Logging & Auditing": "Kirjaaminen ja tarkastus",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH-yhteyden todennustyyppi",
"Save": "Tallenna",
"Save & Exit": "Tallenna ja poistu",
"Send": "Send",
"Session ID": "Istunnon tunniste",
"Sessions": "Istunnot",
"Shortcuts": "Pikakuvakkeet",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "L'URL de l'icône « favicon » utilisée dans toutes les pages Casdoor de l'organisation",
"First name": "Prénom",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Origine de redirection forcée - Infobulle",
"Forget URL": "URL d'oubli",
"Forget URL - Tooltip": "URL personnalisée pour la page \"Mot de passe oublié\". Si elle n'est pas définie, la page par défaut \"Mot de passe oublié\" de Casdoor sera utilisée. Lorsqu'elle est définie, le lien \"Mot de passe oublié\" sur la page de connexion sera redirigé vers cette URL",
@@ -333,6 +334,7 @@
"Languages": "Langues",
"Languages - Tooltip": "Langues disponibles",
"Last name": "Nom de famille",
"Last name - Tooltip": "The last name of user",
"Later": "Plus tard",
"Logging & Auditing": "Journalisation et audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Type d'authentification de connexion SSH",
"Save": "Enregistrer",
"Save & Exit": "Enregistrer et quitter",
"Send": "Send",
"Session ID": "Identifiant de session",
"Sessions": "Sessions",
"Shortcuts": "Raccourcis",

View File

@@ -307,6 +307,7 @@
"Favicon": "סמל אתר",
"Favicon - Tooltip": "כתובת סמל Favicon המשמש בכל דפי Casdoor של הארגון",
"First name": "שם פרטי",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "הפניה כפויה של מקור - תיאור",
"Forget URL": "כתובת שחזור סיסמה",
"Forget URL - Tooltip": "כתובת מותאמת אישית לדף \"שחזור סיסמה\". אם לא מוגדר, ישמש דף ברירת המחדל של Casdoor. כאשר מוגדר, קישורי \"שחזור סיסמה\" בעמוד הכניסה יופנו לכאן",
@@ -333,6 +334,7 @@
"Languages": "שפות",
"Languages - Tooltip": "שפות זמינות",
"Last name": "שם משפחה",
"Last name - Tooltip": "The last name of user",
"Later": "מאוחר יותר",
"Logging & Auditing": "רישום וביקורת",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "סוג האימות של חיבור SSH",
"Save": "שמור",
"Save & Exit": "שמור וצא",
"Send": "Send",
"Session ID": "מזהה סשן",
"Sessions": "סשנים",
"Shortcuts": "קיצורי דרך",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "URL ikon Favicon yang digunakan di semua halaman Casdoor organisasi",
"First name": "Nama depan",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Asal pengalihan paksa - Tooltip",
"Forget URL": "Lupakan URL",
"Forget URL - Tooltip": "URL kustom untuk halaman \"Lupa kata sandi\". Jika tidak diatur, halaman \"Lupa kata sandi\" default Casdoor akan digunakan. Ketika diatur, tautan \"Lupa kata sandi\" pada halaman masuk akan diarahkan ke URL ini",
@@ -333,6 +334,7 @@
"Languages": "Bahasa-bahasa",
"Languages - Tooltip": "Bahasa yang tersedia",
"Last name": "Nama belakang",
"Last name - Tooltip": "The last name of user",
"Later": "Nanti",
"Logging & Auditing": "Pencatatan & Audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Tipe autentikasi koneksi SSH",
"Save": "Menyimpan",
"Save & Exit": "Simpan & Keluar",
"Send": "Send",
"Session ID": "ID sesi",
"Sessions": "Sesi-sesi",
"Shortcuts": "Pintasan",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "Icona favicon utilizzata in tutte le pagine di Casdoor dell'organizzazione",
"First name": "Nome",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Origine reindirizzamento forzato - Tooltip",
"Forget URL": "URL recupero",
"Forget URL - Tooltip": "URL personalizzato per la pagina \"Recupera password\". Se non impostato, verrà utilizzata la pagina predefinita di Casdoor. Quando impostato, il link \"Recupera password\" nella pagina di accesso reindirizzerà a questo URL",
@@ -333,6 +334,7 @@
"Languages": "Lingue",
"Languages - Tooltip": "Lingue disponibili",
"Last name": "Cognome",
"Last name - Tooltip": "The last name of user",
"Later": "Più tardi",
"Logging & Auditing": "Registrazione e audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Tipo di autenticazione della connessione SSH",
"Save": "Salva",
"Save & Exit": "Salva e Esci",
"Send": "Send",
"Session ID": "ID sessione",
"Sessions": "Sessioni",
"Shortcuts": "Scorciatoie",

View File

@@ -307,6 +307,7 @@
"Favicon": "ファビコン",
"Favicon - Tooltip": "組織のすべてのCasdoorページに使用されるFaviconアイコンのURL",
"First name": "名前",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "強制リダイレクトオリジン - ツールチップ",
"Forget URL": "URLを忘れてください",
"Forget URL - Tooltip": "「パスワードをお忘れの場合」ページのカスタムURL。未設定の場合、デフォルトのCasdoor「パスワードをお忘れの場合」ページが使用されます。設定された場合、ログインページの「パスワードをお忘れの場合」リンクはこのURLにリダイレクトされます",
@@ -333,6 +334,7 @@
"Languages": "言語",
"Languages - Tooltip": "利用可能な言語",
"Last name": "苗字",
"Last name - Tooltip": "The last name of user",
"Later": "後で",
"Logging & Auditing": "ログと監査",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH接続の認証タイプ",
"Save": "保存",
"Save & Exit": "保存して終了",
"Send": "Send",
"Session ID": "セッションID",
"Sessions": "セッションズ",
"Shortcuts": "ショートカット",

View File

@@ -307,6 +307,7 @@
"Favicon": "Вэб-сайт иконка",
"Favicon - Tooltip": "Ұйымның барлық Casdoor парақтарында қолданылатын favicon белгі URL",
"First name": "Аты",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Мәжбүрлі қайта бағыттау бастапқысы - Қысқаша түсінік",
"Forget URL": "Парольді ұмыту URL",
"Forget URL - Tooltip": "\"Парольді ұмыту\" парағы үшін теңшеу URL. Орнатылмаған жағдайда әдепті Casdoor \"Парольді ұмыту\" парағы қолданылады. Орнатылған кезде кіру парағындағы \"Парольді ұмыту\" сілтемесі осы URL-ге қайта бағыттайды",
@@ -333,6 +334,7 @@
"Languages": "Тілдер",
"Languages - Tooltip": "Қол жетімді тілдер",
"Last name": "Тегі",
"Last name - Tooltip": "The last name of user",
"Later": "Кейінірек",
"Logging & Auditing": "Журналдау және аудит",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH қосылымының растау түрі",
"Save": "Сақтау",
"Save & Exit": "Сақтау және шығу",
"Send": "Send",
"Session ID": "Сессия ID",
"Sessions": "Сессиялар",
"Shortcuts": "Жылдам жолдар",

View File

@@ -307,6 +307,7 @@
"Favicon": "파비콘",
"Favicon - Tooltip": "조직의 모든 Casdoor 페이지에서 사용되는 Favicon 아이콘 URL",
"First name": "이름",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "강제 리다이렉트 출처 - 툴팁",
"Forget URL": "URL을 잊어버려라",
"Forget URL - Tooltip": "\"비밀번호를 잊어버렸을 경우\" 페이지에 대한 사용자 정의 URL. 설정되지 않은 경우 기본 Casdoor \"비밀번호를 잊어버렸을 경우\" 페이지가 사용됩니다. 설정된 경우 로그인 페이지의 \"비밀번호를 잊으셨나요?\" 링크는 이 URL로 리디렉션됩니다",
@@ -333,6 +334,7 @@
"Languages": "언어",
"Languages - Tooltip": "사용 가능한 언어",
"Last name": "성",
"Last name - Tooltip": "The last name of user",
"Later": "나중에",
"Logging & Auditing": "로깅 및 감사",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH 연결의 인증 유형",
"Save": "저장하다",
"Save & Exit": "저장하고 종료하기",
"Send": "Send",
"Session ID": "세션 ID",
"Sessions": "세션들",
"Shortcuts": "단축키",

View File

@@ -307,6 +307,7 @@
"Favicon": "Ikon Laman",
"Favicon - Tooltip": "URL ikon favicon yang digunakan dalam semua halaman Casdoor organisasi",
"First name": "Nama pertama",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Asal laluan paksa - Tooltip",
"Forget URL": "URL Lupa",
"Forget URL - Tooltip": "URL tersuai untuk halaman \"Lupa kata laluan\". Jika tidak ditetapkan, halaman lalai Casdoor \"Lupa kata laluan\" akan digunakan. Apabila ditetapkan, pautan \"Lupa kata laluan\" pada halaman log masuk akan laluan semula ke URL ini",
@@ -333,6 +334,7 @@
"Languages": "Bahasa",
"Languages - Tooltip": "Bahasa yang tersedia",
"Last name": "Nama terakhir",
"Last name - Tooltip": "The last name of user",
"Later": "Kemudian",
"Logging & Auditing": "Log & Audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Jenis auth sambungan SSH",
"Save": "Simpan",
"Save & Exit": "Simpan & Keluar",
"Send": "Send",
"Session ID": "ID Sesi",
"Sessions": "Sesi",
"Shortcuts": "Pintasan",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "Favicon-pictogram-URL die op alle Casdoor-pagina's van de organisatie wordt gebruikt",
"First name": "Voornaam",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Geforceerde omleidingsbron - Tooltip",
"Forget URL": "Wachtwoordvergeten-URL",
"Forget URL - Tooltip": "Aangepaste URL voor de \"Wachtwoord vergeten\"-pagina. Indien niet ingesteld, wordt de standaard Casdoor \"Wachtwoord vergeten\"-pagina gebruikt. Wanneer ingesteld, verwijst de link op de inlogpagina naar deze URL",
@@ -333,6 +334,7 @@
"Languages": "Talen",
"Languages - Tooltip": "Beschikbare talen",
"Last name": "Achternaam",
"Last name - Tooltip": "The last name of user",
"Later": "Later",
"Logging & Auditing": "Logboekregistratie & Audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Het autorisatietype voor SSH-verbinding",
"Save": "Opslaan",
"Save & Exit": "Opslaan & Afsluiten",
"Send": "Send",
"Session ID": "Sessie-ID",
"Sessions": "Sessies",
"Shortcuts": "Snelkoppelingen",

View File

@@ -307,6 +307,7 @@
"Favicon": "Ikona strony",
"Favicon - Tooltip": "URL ikony favicon używanej na wszystkich stronach Casdoor organizacji",
"First name": "Imię",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Wymuszone przekierowanie źródła - Tooltip",
"Forget URL": "URL odzyskiwania",
"Forget URL - Tooltip": "Niestandardowy URL strony \"Odzyskaj hasło\". Jeśli nie ustawiony, zostanie użyta domyślna strona \"Odzyskaj hasło\" Casdoor. Gdy ustawiony, link \"Odzyskaj hasło\" na stronie logowania przekieruje do tego URL-a",
@@ -333,6 +334,7 @@
"Languages": "Języki",
"Languages - Tooltip": "Dostępne języki",
"Last name": "Nazwisko",
"Last name - Tooltip": "The last name of user",
"Later": "Później",
"Logging & Auditing": "Logowanie i audyt",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Typ uwierzytelniania połączenia SSH",
"Save": "Zapisz",
"Save & Exit": "Zapisz i wyjdź",
"Send": "Send",
"Session ID": "ID sesji",
"Sessions": "Sesje",
"Shortcuts": "Skróty",

View File

@@ -307,6 +307,7 @@
"Favicon": "Ícone do site",
"Favicon - Tooltip": "URL do ícone de favicon usado em todas as páginas do Casdoor da organização",
"First name": "Nome",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Dica: origem de redirecionamento forçado",
"Forget URL": "URL de Esqueci a Senha",
"Forget URL - Tooltip": "URL personalizada para a página de \"Esqueci a senha\". Se não definido, será usada a página padrão de \"Esqueci a senha\" do Casdoor. Quando definido, o link de \"Esqueci a senha\" na página de login será redirecionado para esta URL",
@@ -333,6 +334,7 @@
"Languages": "Idiomas",
"Languages - Tooltip": "Idiomas disponíveis",
"Last name": "Sobrenome",
"Last name - Tooltip": "The last name of user",
"Later": "Depois",
"Logging & Auditing": "Registro e Auditoria",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Tipo de autenticação para conexão SSH",
"Save": "Salvar",
"Save & Exit": "Salvar e Sair",
"Send": "Send",
"Session ID": "ID da sessão",
"Sessions": "Sessões",
"Shortcuts": "Atalhos",

View File

@@ -307,6 +307,7 @@
"Favicon": "Фавикон",
"Favicon - Tooltip": "URL иконки Favicon, используемый на всех страницах организации Casdoor",
"First name": "Имя",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Подсказка: принудительный редирект origin",
"Forget URL": "Забудьте URL",
"Forget URL - Tooltip": "Настроенный URL для страницы \"Забыли пароль\". Если не установлено, будет использоваться стандартная страница \"Забыли пароль\" Casdoor. При установке, ссылка \"Забыли пароль\" на странице входа будет перенаправляться на этот URL",
@@ -333,6 +334,7 @@
"Languages": "Языки",
"Languages - Tooltip": "Доступные языки",
"Last name": "Фамилия",
"Last name - Tooltip": "The last name of user",
"Later": "Позже",
"Logging & Auditing": "Логирование и аудит",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Тип аутентификации SSH-подключения",
"Save": "Сохранить",
"Save & Exit": "Сохранить и выйти",
"Send": "Send",
"Session ID": "Идентификатор сессии",
"Sessions": "Сессии",
"Shortcuts": "Ярлыки",

View File

@@ -307,6 +307,7 @@
"Favicon": "Ikona webu",
"Favicon - Tooltip": "URL ikony favicon používaná na všetkých stránkach Casdoor organizácie",
"First name": "Meno",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Vynútený zdroj presmerovania - Nápoveda",
"Forget URL": "URL zabudnutia",
"Forget URL - Tooltip": "Vlastná URL pre stránku \"Zabudol som heslo\". Ak nie je nastavená, bude použitá predvolená stránka Casdoor \"Zabudol som heslo\". Po nastavení bude odkaz na stránke prihlásenia presmerovaný na túto URL",
@@ -333,6 +334,7 @@
"Languages": "Jazyky",
"Languages - Tooltip": "Dostupné jazyky",
"Last name": "Priezvisko",
"Last name - Tooltip": "The last name of user",
"Later": "Neskôr",
"Logging & Auditing": "Zaznamenávanie a audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Typ autentifikácie SSH pripojenia",
"Save": "Uložiť",
"Save & Exit": "Uložiť a ukončiť",
"Send": "Send",
"Session ID": "ID relácie",
"Sessions": "Relácie",
"Shortcuts": "Skratky",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "URL ikon favicon yang digunakan dalam semua halaman Casdoor organisasi",
"First name": "Nama pertama",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Asal laluan paksa - Tooltip",
"Forget URL": "URL Lupa",
"Forget URL - Tooltip": "URL tersuai untuk halaman \"Lupa kata laluan\". Jika tidak ditetapkan, halaman lalai Casdoor \"Lupa kata laluan\" akan digunakan. Apabila ditetapkan, pautan \"Lupa kata laluan\" pada halaman log masuk akan laluan semula ke URL ini",
@@ -333,6 +334,7 @@
"Languages": "Bahasa",
"Languages - Tooltip": "Bahasa yang tersedia",
"Last name": "Nama terakhir",
"Last name - Tooltip": "The last name of user",
"Later": "Kemudian",
"Logging & Auditing": "Log & Audit",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Jenis auth sambungan SSH",
"Save": "Simpan",
"Save & Exit": "Simpan & Keluar",
"Send": "Send",
"Session ID": "ID Sesi",
"Sessions": "Sesi",
"Shortcuts": "Pintasan",

View File

@@ -307,6 +307,7 @@
"Favicon": "Favicon",
"Favicon - Tooltip": "Organizasyonun tüm Casdoor sayfalarında kullanılan Favicon simgesi URL'si",
"First name": "İsim",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Zorlanan yönlendirme kaynağı - Araç ipucu",
"Forget URL": "Unutma URL'si",
"Forget URL - Tooltip": "\"Parolayı unuttum\" sayfası için özel URL. Ayarlanmamışsa, varsayılan Casdoor \"Parolayı unuttum\" sayfası kullanılacaktır. Ayarlanırsa, giriş sayfasındaki \"Parolayı unuttum\" bağlantısı bu URL'ye yönlendirilecektir",
@@ -333,6 +334,7 @@
"Languages": "Diller",
"Languages - Tooltip": "Kullanılabilir diller",
"Last name": "Soyisim",
"Last name - Tooltip": "The last name of user",
"Later": "Sonra",
"Logging & Auditing": "Günlük ve Denetim",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH bağlantısının kimlik doğrulama türü",
"Save": "Kaydet",
"Save & Exit": "Kaydet ve Çık",
"Send": "Send",
"Session ID": "Oturum ID",
"Sessions": "Oturumlar",
"Shortcuts": "Kısayollar",

View File

@@ -307,6 +307,7 @@
"Favicon": "Фавікон",
"Favicon - Tooltip": "URL-адреса піктограми Favicon, яка використовується на всіх сторінках Casdoor організації",
"First name": "Ім'я",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Примусове джерело перенаправлення - підказка",
"Forget URL": "Забути URL",
"Forget URL - Tooltip": "Користувацька URL-адреса для сторінки \"Забути пароль\". ",
@@ -333,6 +334,7 @@
"Languages": "Мови",
"Languages - Tooltip": "Доступні мови",
"Last name": "Прізвище",
"Last name - Tooltip": "The last name of user",
"Later": "Пізніше",
"Logging & Auditing": "Лісозаготівля",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Тип авторизації підключення SSH",
"Save": "зберегти",
"Save & Exit": "зберегти",
"Send": "Send",
"Session ID": "Ідентифікатор сеансу",
"Sessions": "Сеанси",
"Shortcuts": "Ярлики",

View File

@@ -307,6 +307,7 @@
"Favicon": "Biểu tượng trang",
"Favicon - Tooltip": "URL biểu tượng Favicon được sử dụng trong tất cả các trang của tổ chức Casdoor",
"First name": "Tên",
"First name - Tooltip": "The first name of user",
"Forced redirect origin - Tooltip": "Gợi ý nguồn chuyển hướng bắt buộc",
"Forget URL": "Quên URL",
"Forget URL - Tooltip": "Đường dẫn tùy chỉnh cho trang \"Quên mật khẩu\". Nếu không được thiết lập, trang \"Quên mật khẩu\" mặc định của Casdoor sẽ được sử dụng. Khi cài đặt, liên kết \"Quên mật khẩu\" trên trang đăng nhập sẽ chuyển hướng đến URL này",
@@ -333,6 +334,7 @@
"Languages": "Ngôn ngữ",
"Languages - Tooltip": "Ngôn ngữ hiện có",
"Last name": "Họ",
"Last name - Tooltip": "The last name of user",
"Later": "Để sau",
"Logging & Auditing": "Nhật ký & Kiểm toán",
"Login page": "Login page",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "Loại xác thực kết nối SSH",
"Save": "Lưu",
"Save & Exit": "Lưu và Thoát",
"Send": "Send",
"Session ID": "ID phiên làm việc",
"Sessions": "Phiên",
"Shortcuts": "Lối tắt",

View File

@@ -307,6 +307,7 @@
"Favicon": "组织Favicon",
"Favicon - Tooltip": "该组织所有Casdoor页面中所使用的Favicon图标URL",
"First name": "名字",
"First name - Tooltip": "用户的名字",
"Forced redirect origin - Tooltip": "Forced redirect origin - Tooltip",
"Forget URL": "忘记密码URL",
"Forget URL - Tooltip": "自定义忘记密码页面的URL不设置时采用Casdoor默认的忘记密码页面设置后Casdoor各类页面的忘记密码链接会跳转到该URL",
@@ -333,6 +334,7 @@
"Languages": "语言",
"Languages - Tooltip": "可选语言",
"Last name": "姓氏",
"Last name - Tooltip": "用户的姓氏",
"Later": "稍后",
"Logging & Auditing": "日志 & 审计",
"Login page": "登录页面",
@@ -417,6 +419,7 @@
"SSH type - Tooltip": "SSH连接的认证类型",
"Save": "保存",
"Save & Exit": "保存 & 退出",
"Send": "发送",
"Session ID": "会话ID",
"Sessions": "会话",
"Shortcuts": "快捷操作",