diff --git a/controllers/invitation.go b/controllers/invitation.go index 25743ca4..19197eee 100644 --- a/controllers/invitation.go +++ b/controllers/invitation.go @@ -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() +} diff --git a/controllers/service.go b/controllers/service.go index 47d5e8d7..01343a74 100644 --- a/controllers/service.go +++ b/controllers/service.go @@ -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 diff --git a/email/azure_acs.go b/email/azure_acs.go index 3c25b42c..b0d108ac 100644 --- a/email/azure_acs.go +++ b/email/azure_acs.go @@ -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) diff --git a/email/custom_http.go b/email/custom_http.go index d2e855f6..d871da40 100644 --- a/email/custom_http.go +++ b/email/custom_http.go @@ -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() diff --git a/email/provider.go b/email/provider.go index f0d3bddf..91441f64 100644 --- a/email/provider.go +++ b/email/provider.go @@ -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 { diff --git a/email/sendgrid.go b/email/sendgrid.go index a586a16f..010e5fb2 100644 --- a/email/sendgrid.go +++ b/email/sendgrid.go @@ -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 diff --git a/email/smtp.go b/email/smtp.go index 7cc2756c..610762ba 100644 --- a/email/smtp.go +++ b/email/smtp.go @@ -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) diff --git a/i18n/locales/ar/data.json b/i18n/locales/ar/data.json index ad1bc74a..bd054109 100644 --- a/i18n/locales/ar/data.json +++ b/i18n/locales/ar/data.json @@ -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 موجود" }, diff --git a/i18n/locales/az/data.json b/i18n/locales/az/data.json index 802fbce4..122d4adf 100644 --- a/i18n/locales/az/data.json +++ b/i18n/locales/az/data.json @@ -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" } -} \ No newline at end of file +} diff --git a/i18n/locales/cs/data.json b/i18n/locales/cs/data.json index 4fcbe72c..50939176 100644 --- a/i18n/locales/cs/data.json +++ b/i18n/locales/cs/data.json @@ -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" }, diff --git a/i18n/locales/de/data.json b/i18n/locales/de/data.json index 4dd0cac1..2db903b6 100644 --- a/i18n/locales/de/data.json +++ b/i18n/locales/de/data.json @@ -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" }, diff --git a/i18n/locales/en/data.json b/i18n/locales/en/data.json index d3bc8aec..d42d19ee 100644 --- a/i18n/locales/en/data.json +++ b/i18n/locales/en/data.json @@ -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" }, diff --git a/i18n/locales/es/data.json b/i18n/locales/es/data.json index acca485e..80d9757c 100644 --- a/i18n/locales/es/data.json +++ b/i18n/locales/es/data.json @@ -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" }, diff --git a/i18n/locales/fa/data.json b/i18n/locales/fa/data.json index b4490662..8f62ce46 100644 --- a/i18n/locales/fa/data.json +++ b/i18n/locales/fa/data.json @@ -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 وجود دارد" }, diff --git a/i18n/locales/fi/data.json b/i18n/locales/fi/data.json index 08f0540e..c7f10b49 100644 --- a/i18n/locales/fi/data.json +++ b/i18n/locales/fi/data.json @@ -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" }, diff --git a/i18n/locales/fr/data.json b/i18n/locales/fr/data.json index 18f738ca..d3603e50 100644 --- a/i18n/locales/fr/data.json +++ b/i18n/locales/fr/data.json @@ -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" }, diff --git a/i18n/locales/he/data.json b/i18n/locales/he/data.json index 236e7d52..861228f7 100644 --- a/i18n/locales/he/data.json +++ b/i18n/locales/he/data.json @@ -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 קיים" }, diff --git a/i18n/locales/id/data.json b/i18n/locales/id/data.json index 17530ea4..3a9d5c1a 100644 --- a/i18n/locales/id/data.json +++ b/i18n/locales/id/data.json @@ -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" }, diff --git a/i18n/locales/it/data.json b/i18n/locales/it/data.json index 7e0c371b..e1f5634f 100644 --- a/i18n/locales/it/data.json +++ b/i18n/locales/it/data.json @@ -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" }, diff --git a/i18n/locales/ja/data.json b/i18n/locales/ja/data.json index 0b9df102..f4743ef2 100644 --- a/i18n/locales/ja/data.json +++ b/i18n/locales/ja/data.json @@ -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サーバーは存在します" }, diff --git a/i18n/locales/kk/data.json b/i18n/locales/kk/data.json index c7e0d195..3bdeb368 100644 --- a/i18n/locales/kk/data.json +++ b/i18n/locales/kk/data.json @@ -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" }, diff --git a/i18n/locales/ko/data.json b/i18n/locales/ko/data.json index 3e5e8ac2..aa71fbbd 100644 --- a/i18n/locales/ko/data.json +++ b/i18n/locales/ko/data.json @@ -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 서버가 존재합니다" }, diff --git a/i18n/locales/ms/data.json b/i18n/locales/ms/data.json index 69935d4b..53f01900 100644 --- a/i18n/locales/ms/data.json +++ b/i18n/locales/ms/data.json @@ -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" }, diff --git a/i18n/locales/nl/data.json b/i18n/locales/nl/data.json index 2f5acb24..cc46a1a8 100644 --- a/i18n/locales/nl/data.json +++ b/i18n/locales/nl/data.json @@ -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" }, diff --git a/i18n/locales/pl/data.json b/i18n/locales/pl/data.json index f39dd354..1bff4f11 100644 --- a/i18n/locales/pl/data.json +++ b/i18n/locales/pl/data.json @@ -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" }, diff --git a/i18n/locales/pt/data.json b/i18n/locales/pt/data.json index d7ec36d7..55d1f66f 100644 --- a/i18n/locales/pt/data.json +++ b/i18n/locales/pt/data.json @@ -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" }, diff --git a/i18n/locales/ru/data.json b/i18n/locales/ru/data.json index 1be916b6..0a93e638 100644 --- a/i18n/locales/ru/data.json +++ b/i18n/locales/ru/data.json @@ -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-сервер существует" }, diff --git a/i18n/locales/sk/data.json b/i18n/locales/sk/data.json index 32786366..47be7a75 100644 --- a/i18n/locales/sk/data.json +++ b/i18n/locales/sk/data.json @@ -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" }, diff --git a/i18n/locales/sv/data.json b/i18n/locales/sv/data.json index 54c0f00d..6e8f43c6 100644 --- a/i18n/locales/sv/data.json +++ b/i18n/locales/sv/data.json @@ -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" }, diff --git a/i18n/locales/tr/data.json b/i18n/locales/tr/data.json index d0c7f462..25e8c1f2 100644 --- a/i18n/locales/tr/data.json +++ b/i18n/locales/tr/data.json @@ -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" }, diff --git a/i18n/locales/uk/data.json b/i18n/locales/uk/data.json index 894e8251..d6cba019 100644 --- a/i18n/locales/uk/data.json +++ b/i18n/locales/uk/data.json @@ -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 існує" }, diff --git a/i18n/locales/vi/data.json b/i18n/locales/vi/data.json index 670ed284..0dac1fe4 100644 --- a/i18n/locales/vi/data.json +++ b/i18n/locales/vi/data.json @@ -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" }, diff --git a/i18n/locales/zh/data.json b/i18n/locales/zh/data.json index 774f8e7b..6f50bec6 100644 --- a/i18n/locales/zh/data.json +++ b/i18n/locales/zh/data.json @@ -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服务器已存在" }, diff --git a/object/email.go b/object/email.go index b63a2b02..873aa4d4 100644 --- a/object/email.go +++ b/object/email.go @@ -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 diff --git a/object/invitation.go b/object/invitation.go index 57f4b0e8..b90c6d53 100644 --- a/object/invitation.go +++ b/object/invitation.go @@ -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) +} diff --git a/object/verification.go b/object/verification.go index 371f3580..005af238 100644 --- a/object/verification.go +++ b/object/verification.go @@ -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 } diff --git a/routers/authz_filter.go b/routers/authz_filter.go index 4ca45b05..317ab6be 100644 --- a/routers/authz_filter.go +++ b/routers/authz_filter.go @@ -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) diff --git a/routers/router.go b/routers/router.go index 3b3c0d68..2dea9d13 100644 --- a/routers/router.go +++ b/routers/router.go @@ -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") diff --git a/web/src/InvitationEditPage.js b/web/src/InvitationEditPage.js index 049a8ea8..b6bbcc2d 100644 --- a/web/src/InvitationEditPage.js +++ b/web/src/InvitationEditPage.js @@ -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 { + 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")}, + ]} + onCancel={() => {this.setState({showSendModal: false});}}> +
+

You will send invitation email to:

+
+
+
; + } + renderInvitation() { const isCreatedByPlan = this.state.invitation.tag === "auto_created_invitation_for_plan"; return ( @@ -199,6 +229,17 @@ class InvitationEditPage extends React.Component { + + + {i18next.t("general:Send")} + + + { + this.setState({emails: value.target.value}); + }}> + + + {Setting.getLabel(i18next.t("invitation:Quota"), i18next.t("invitation:Quota - Tooltip"))} : @@ -338,6 +379,7 @@ class InvitationEditPage extends React.Component { render() { return (
+ {this.state.showSendModal ? this.renderSendEmailModal() : null} { this.state.invitation !== null ? this.renderInvitation() : null } diff --git a/web/src/ProviderEditPage.js b/web/src/ProviderEditPage.js index 0a7e6c51..2df908f8 100644 --- a/web/src/ProviderEditPage.js +++ b/web/src/ProviderEditPage.js @@ -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 { + + + {Setting.getLabel(`${i18next.t("provider:Email content")}-${i18next.t("general:Invitations")}`, i18next.t("provider:Email content - Tooltip"))} : + + + + + + + + +
+ { + this.updateProviderField("metadata", value); + }} + /> +
+ + + +
+
+
+ + + + {Setting.getLabel(i18next.t("provider:Test Email"), i18next.t("provider:Test Email - Tooltip"))} : diff --git a/web/src/Setting.js b/web/src/Setting.js index 76efe9f8..28cc47b1 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -1648,6 +1648,48 @@ export function getDefaultHtmlEmailContent() { `; } +export function getDefaultInvitationHtmlEmailContent() { + return ` + + + + +Invitation Code Email + + + + + +`; +} + export function getCurrencyText(product) { if (product?.currency === "USD") { return i18next.t("currency:USD"); diff --git a/web/src/backend/InvitationBackend.js b/web/src/backend/InvitationBackend.js index 9a8771a3..cf060d6d 100644 --- a/web/src/backend/InvitationBackend.js +++ b/web/src/backend/InvitationBackend.js @@ -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()); +} diff --git a/web/src/locales/ar/data.json b/web/src/locales/ar/data.json index 97716489..70e330c2 100644 --- a/web/src/locales/ar/data.json +++ b/web/src/locales/ar/data.json @@ -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": "الاختصارات", diff --git a/web/src/locales/az/data.json b/web/src/locales/az/data.json index 97791712..b5f4a5ce 100644 --- a/web/src/locales/az/data.json +++ b/web/src/locales/az/data.json @@ -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" } -} \ No newline at end of file +} diff --git a/web/src/locales/cs/data.json b/web/src/locales/cs/data.json index 3fd45a48..cbe63e34 100644 --- a/web/src/locales/cs/data.json +++ b/web/src/locales/cs/data.json @@ -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", diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index b292f855..7239c90f 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -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", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 58541cc6..7044abe3 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -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", diff --git a/web/src/locales/es/data.json b/web/src/locales/es/data.json index f3714048..928b29b4 100644 --- a/web/src/locales/es/data.json +++ b/web/src/locales/es/data.json @@ -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", diff --git a/web/src/locales/fa/data.json b/web/src/locales/fa/data.json index 63c5bbbf..b387838f 100644 --- a/web/src/locales/fa/data.json +++ b/web/src/locales/fa/data.json @@ -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": "میانبرها", diff --git a/web/src/locales/fi/data.json b/web/src/locales/fi/data.json index 96cb46a2..f88ff49d 100644 --- a/web/src/locales/fi/data.json +++ b/web/src/locales/fi/data.json @@ -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", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index eb8d1eaf..9ac8e6f2 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -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", diff --git a/web/src/locales/he/data.json b/web/src/locales/he/data.json index 516c0b12..6970e9db 100644 --- a/web/src/locales/he/data.json +++ b/web/src/locales/he/data.json @@ -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": "קיצורי דרך", diff --git a/web/src/locales/id/data.json b/web/src/locales/id/data.json index b58667db..b03873ec 100644 --- a/web/src/locales/id/data.json +++ b/web/src/locales/id/data.json @@ -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", diff --git a/web/src/locales/it/data.json b/web/src/locales/it/data.json index 29e257fe..8f43e5ba 100644 --- a/web/src/locales/it/data.json +++ b/web/src/locales/it/data.json @@ -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", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index 05cd4ca7..4c4b69cb 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -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": "ショートカット", diff --git a/web/src/locales/kk/data.json b/web/src/locales/kk/data.json index c00998b5..25b2a5bf 100644 --- a/web/src/locales/kk/data.json +++ b/web/src/locales/kk/data.json @@ -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": "Жылдам жолдар", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index c5976e35..e3d77a04 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -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": "단축키", diff --git a/web/src/locales/ms/data.json b/web/src/locales/ms/data.json index 5262e791..12afff94 100644 --- a/web/src/locales/ms/data.json +++ b/web/src/locales/ms/data.json @@ -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", diff --git a/web/src/locales/nl/data.json b/web/src/locales/nl/data.json index e35909c5..fec373ba 100644 --- a/web/src/locales/nl/data.json +++ b/web/src/locales/nl/data.json @@ -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", diff --git a/web/src/locales/pl/data.json b/web/src/locales/pl/data.json index e4e18525..9e898067 100644 --- a/web/src/locales/pl/data.json +++ b/web/src/locales/pl/data.json @@ -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", diff --git a/web/src/locales/pt/data.json b/web/src/locales/pt/data.json index fcc037ee..3b5159a7 100644 --- a/web/src/locales/pt/data.json +++ b/web/src/locales/pt/data.json @@ -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", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index bfb20545..7b8b9d5a 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -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": "Ярлыки", diff --git a/web/src/locales/sk/data.json b/web/src/locales/sk/data.json index 0367896c..336bd8c4 100644 --- a/web/src/locales/sk/data.json +++ b/web/src/locales/sk/data.json @@ -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", diff --git a/web/src/locales/sv/data.json b/web/src/locales/sv/data.json index fe38e9b7..932247e2 100644 --- a/web/src/locales/sv/data.json +++ b/web/src/locales/sv/data.json @@ -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", diff --git a/web/src/locales/tr/data.json b/web/src/locales/tr/data.json index 0f929b94..5daa89de 100644 --- a/web/src/locales/tr/data.json +++ b/web/src/locales/tr/data.json @@ -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", diff --git a/web/src/locales/uk/data.json b/web/src/locales/uk/data.json index 10184209..ff322bdd 100644 --- a/web/src/locales/uk/data.json +++ b/web/src/locales/uk/data.json @@ -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": "Ярлики", diff --git a/web/src/locales/vi/data.json b/web/src/locales/vi/data.json index 0de0d526..4099a509 100644 --- a/web/src/locales/vi/data.json +++ b/web/src/locales/vi/data.json @@ -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", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index 05e1092c..127ff188 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -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": "快捷操作",