Compare commits

...

5 Commits

Author SHA1 Message Date
Cornichon420 9ae16163bb Translated using Weblate (French)
Currently translated at 100.0% (109 of 109 strings)

Translation: jfa-go/Setup
Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/setup/fr/
2021-10-07 17:19:30 +02:00
Sundune c5ce66bd4d translation from Weblate (German)
Currently translated at 98.2% (165 of 168 strings)

Translation: jfa-go/Admin Page
Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/admin/de/
2021-10-07 17:19:30 +02:00
Cornichon420 da8dd7def8 translation from Weblate (French)
Currently translated at 100.0% (37 of 37 strings)

Translation: jfa-go/Account Creation Form
Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/form/fr/
2021-10-07 17:19:29 +02:00
Cornichon420 a4b5d6dea8 translation from Weblate (French)
Currently translated at 100.0% (168 of 168 strings)

Translation: jfa-go/Admin Page
Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/admin/fr/
2021-10-07 17:19:29 +02:00
Harvey Tindall 77799b2917
smtp: only construct email once
also switch layout in email file to group each driver and its methods
together.
2021-10-07 16:19:06 +01:00
5 changed files with 101 additions and 105 deletions

161
email.go
View File

@ -13,7 +13,6 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
textTemplate "text/template" textTemplate "text/template"
"time" "time"
@ -31,84 +30,6 @@ type EmailClient interface {
Send(fromName, fromAddr string, message *Message, address ...string) error Send(fromName, fromAddr string, message *Message, address ...string) error
} }
type DummyClient struct{}
func (dc *DummyClient) Send(fromName, fromAddr string, email *Message, address ...string) error {
fmt.Printf("FROM: %s <%s>\nTO: %s\nTEXT: %s\n", fromName, fromAddr, strings.Join(address, ", "), email.Text)
return nil
}
// Mailgun client implements EmailClient.
type Mailgun struct {
client *mailgun.MailgunImpl
}
func (mg *Mailgun) Send(fromName, fromAddr string, email *Message, address ...string) error {
message := mg.client.NewMessage(
fmt.Sprintf("%s <%s>", fromName, fromAddr),
email.Subject,
email.Text,
)
for _, a := range address {
// Adding variable tells mailgun to do a batch send, so users don't see other recipients.
message.AddRecipientAndVariables(a, map[string]interface{}{"unique_id": a})
}
message.SetHtml(email.HTML)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
_, _, err := mg.client.Send(ctx, message)
return err
}
// SMTP supports SSL/TLS and STARTTLS; implements EmailClient.
type SMTP struct {
Client *sMail.SMTPServer
}
func (sm *SMTP) Send(fromName, fromAddr string, email *Message, address ...string) error {
from := fmt.Sprintf("%s <%s>", fromName, fromAddr)
var cli *sMail.SMTPClient
var err error
cli, err = sm.Client.Connect()
if err != nil {
return err
}
defer cli.Close()
var wg sync.WaitGroup
for _, addr := range address {
wg.Add(1)
go func(addr string) {
defer wg.Done()
e := sMail.NewMSG()
e.SetFrom(from)
e.SetSubject(email.Subject)
e.AddTo(addr)
if email.HTML == "" {
e.SetBody(sMail.TextPlain, email.Text)
} else {
e.SetBody(sMail.TextHTML, email.HTML)
e.AddAlternative(sMail.TextPlain, email.Text)
}
err = e.Send(cli)
// e := jEmail.NewEmail()
// e.Subject = email.Subject
// e.From = from
// e.Text = []byte(email.Text)
// e.HTML = []byte(email.HTML)
// e.To = []string{addr}
// err = sm.Pool.Send(e, 15*time.Second)
// if sm.sslTLS {
// err = sm.Pool.S
// err = e.SendWithTLS(server, sm.auth, sm.tlsConfig)
// } else {
// err = e.SendWithStartTLS(server, sm.auth, sm.tlsConfig)
// }
}(addr)
}
wg.Wait()
return err
}
// Emailer contains the email sender, translations, and methods to construct messages. // Emailer contains the email sender, translations, and methods to construct messages.
type Emailer struct { type Emailer struct {
fromAddr, fromName string fromAddr, fromName string
@ -175,18 +96,17 @@ func NewEmailer(app *appContext) *Emailer {
return emailer return emailer
} }
// NewMailgun returns a Mailgun emailClient. // DummyClient just logs the email to the console for debugging purposes. It can be used by settings [email]/method to "dummy".
func (emailer *Emailer) NewMailgun(url, key string) { type DummyClient struct{}
sender := &Mailgun{
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key), func (dc *DummyClient) Send(fromName, fromAddr string, email *Message, address ...string) error {
} fmt.Printf("FROM: %s <%s>\nTO: %s\nTEXT: %s\n", fromName, fromAddr, strings.Join(address, ", "), email.Text)
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages') return nil
if strings.Contains(url, "messages") { }
url = url[0:strings.LastIndex(url, "/")]
url = url[0:strings.LastIndex(url, "/")] // SMTP supports SSL/TLS and STARTTLS; implements EmailClient.
} type SMTP struct {
sender.client.SetAPIBase(url) Client *sMail.SMTPServer
emailer.sender = sender
} }
// NewSMTP returns an SMTP emailClient. // NewSMTP returns an SMTP emailClient.
@ -237,6 +157,65 @@ func (emailer *Emailer) NewSMTP(server string, port int, username, password stri
return return
} }
func (sm *SMTP) Send(fromName, fromAddr string, email *Message, address ...string) error {
from := fmt.Sprintf("%s <%s>", fromName, fromAddr)
var cli *sMail.SMTPClient
var err error
cli, err = sm.Client.Connect()
if err != nil {
return err
}
defer cli.Close()
e := sMail.NewMSG()
e.SetFrom(from)
e.SetSubject(email.Subject)
e.AddTo(address...)
if email.HTML == "" {
e.SetBody(sMail.TextPlain, email.Text)
} else {
e.SetBody(sMail.TextHTML, email.HTML)
e.AddAlternative(sMail.TextPlain, email.Text)
}
err = e.Send(cli)
return err
}
// Mailgun client implements EmailClient.
type Mailgun struct {
client *mailgun.MailgunImpl
}
// NewMailgun returns a Mailgun emailClient.
func (emailer *Emailer) NewMailgun(url, key string) {
sender := &Mailgun{
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
}
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages')
if strings.Contains(url, "messages") {
url = url[0:strings.LastIndex(url, "/")]
url = url[0:strings.LastIndex(url, "/")]
}
sender.client.SetAPIBase(url)
emailer.sender = sender
}
func (mg *Mailgun) Send(fromName, fromAddr string, email *Message, address ...string) error {
message := mg.client.NewMessage(
fmt.Sprintf("%s <%s>", fromName, fromAddr),
email.Subject,
email.Text,
)
for _, a := range address {
// Adding variable tells mailgun to do a batch send, so users don't see other recipients.
message.AddRecipientAndVariables(a, map[string]interface{}{"unique_id": a})
}
message.SetHtml(email.HTML)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
_, _, err := mg.client.Send(ctx, message)
return err
}
type templ interface { type templ interface {
Execute(wr io.Writer, data interface{}) error Execute(wr io.Writer, data interface{}) error
} }

View File

@ -100,7 +100,8 @@
"searchDiscordUser": "Gib den Discord-Benutzername ein, um den Benutzer zu finden.", "searchDiscordUser": "Gib den Discord-Benutzername ein, um den Benutzer zu finden.",
"findDiscordUser": "Suche Discord-Benutzer", "findDiscordUser": "Suche Discord-Benutzer",
"linkMatrixDescription": "Gib den Benutzernamen und das Passwort des Benutzers ein, der als Bot verwendet werden soll. Nach dem Absenden wird die App neu gestartet.", "linkMatrixDescription": "Gib den Benutzernamen und das Passwort des Benutzers ein, der als Bot verwendet werden soll. Nach dem Absenden wird die App neu gestartet.",
"matrixHomeServer": "Adresse des Homeservers" "matrixHomeServer": "Adresse des Homeservers",
"templates": "Vorlagen"
}, },
"notifications": { "notifications": {
"changedEmailAddress": "E-Mail-Adresse von {n} geändert.", "changedEmailAddress": "E-Mail-Adresse von {n} geändert.",
@ -140,7 +141,8 @@
"noUpdatesAvailable": "Keinen neuen Aktualisierungen verfügbar.", "noUpdatesAvailable": "Keinen neuen Aktualisierungen verfügbar.",
"updateAppliedRefresh": "Update angewendet, bitte aktualisieren.", "updateAppliedRefresh": "Update angewendet, bitte aktualisieren.",
"telegramVerified": "Telegram-Konto verifiziert.", "telegramVerified": "Telegram-Konto verifiziert.",
"accountConnected": "Konto verbunden." "accountConnected": "Konto verbunden.",
"savedAnnouncement": "Ankündigung gespeichert."
}, },
"quantityStrings": { "quantityStrings": {
"modifySettingsFor": { "modifySettingsFor": {

View File

@ -101,7 +101,11 @@
"findDiscordUser": "Trouver l'utilisateur Discord", "findDiscordUser": "Trouver l'utilisateur Discord",
"linkMatrixDescription": "Entrez le nom d'utilisateur et le mot de passe de l'utilisateur pour lutilisateur comme bot. Une fois soumis, l'application va redémarrer.", "linkMatrixDescription": "Entrez le nom d'utilisateur et le mot de passe de l'utilisateur pour lutilisateur comme bot. Une fois soumis, l'application va redémarrer.",
"searchDiscordUser": "Commencez à taper le nom d'utilisateur Discord pour trouver l'utilisateur.", "searchDiscordUser": "Commencez à taper le nom d'utilisateur Discord pour trouver l'utilisateur.",
"matrixHomeServer": "Adresse du serveur" "matrixHomeServer": "Adresse du serveur domestique",
"saveAsTemplate": "Sauvegarder comme modèle",
"templateEnterName": "Entrez un nom pour sauvegarder ce modèle.",
"deleteTemplate": "Supprimer le modèle",
"templates": "Modèles"
}, },
"notifications": { "notifications": {
"changedEmailAddress": "Adresse e-mail modifiée de {n}.", "changedEmailAddress": "Adresse e-mail modifiée de {n}.",
@ -141,7 +145,8 @@
"noUpdatesAvailable": "Aucune nouvelle mise à jour disponible.", "noUpdatesAvailable": "Aucune nouvelle mise à jour disponible.",
"telegramVerified": "Compte Telegram vérifié.", "telegramVerified": "Compte Telegram vérifié.",
"updateAppliedRefresh": "Mise à jour appliquée, veuillez actualiser.", "updateAppliedRefresh": "Mise à jour appliquée, veuillez actualiser.",
"accountConnected": "Compte connecté." "accountConnected": "Compte connecté.",
"savedAnnouncement": "Annonce enregistrée."
}, },
"quantityStrings": { "quantityStrings": {
"modifySettingsFor": { "modifySettingsFor": {

View File

@ -7,7 +7,7 @@
"pageTitle": "Créer un compte Jellyfin", "pageTitle": "Créer un compte Jellyfin",
"createAccountHeader": "Création du compte", "createAccountHeader": "Création du compte",
"accountDetails": "Détails", "accountDetails": "Détails",
"emailAddress": "Email", "emailAddress": "E-mail",
"username": "Nom d'utilisateur", "username": "Nom d'utilisateur",
"password": "Mot de passe", "password": "Mot de passe",
"reEnterPassword": "Confirmez mot de passe", "reEnterPassword": "Confirmez mot de passe",

View File

@ -15,7 +15,11 @@
"serverAddress": "Adresse du serveur", "serverAddress": "Adresse du serveur",
"emailSubject": "Objet de l'e-mail", "emailSubject": "Objet de l'e-mail",
"URL": "URL", "URL": "URL",
"apiKey": "Clé API" "apiKey": "Clé API",
"errorUserDisabled": "L'utilisateur est peut-être désactivé.",
"error404": "404, vérifiez l'URL interne.",
"errorInvalidUserPass": "Nom d'utilisateur/mot de passe invalide.",
"errorNotAdmin": "L'utilisateur n'est pas autorisé à gérer le serveur."
}, },
"startPage": { "startPage": {
"welcome": "Bienvenue !", "welcome": "Bienvenue !",
@ -25,7 +29,7 @@
}, },
"endPage": { "endPage": {
"finished": "Terminé !", "finished": "Terminé !",
"restartMessage": "Il y a d'autres paramètres que vous pouvez configurer sur la page administrateur. Cliquez en dessous pour redémarrer, rafraichissez ensuite la page.", "restartMessage": "Vous pouvez configurer les bots Discord/Telegram/Matrix, personnaliser vos messages et plus encore dans Paramètres. Cliquez ci-dessous pour redémarrer, puis actualisez la page.",
"refreshPage": "Rafraichir" "refreshPage": "Rafraichir"
}, },
"language": { "language": {
@ -86,16 +90,16 @@
"mailgunApiURL": "URL de l'API" "mailgunApiURL": "URL de l'API"
}, },
"notifications": { "notifications": {
"title": "Notifications", "title": "Notifications d'administrateur",
"description": "Si activé, vous pouvez choisir (par invitation) de recevoir un e-mail lorsque l'invitation expire, ou lorsque l'utilisateur est créé. Si vous ne choisissez pas la méthode de connexion par Jellyfin, assurez-vous d'avoir fourni votre adresse e-mail." "description": "Si activé, vous pouvez choisir (par invitation) de recevoir un message lorsque l'invitation expire, ou lorsque l'utilisateur est créé. Si vous ne choisissez pas la méthode de connexion par Jellyfin, assurez-vous d'avoir fourni votre adresse e-mail ou un autre moyen de contact."
}, },
"welcomeEmails": { "welcomeEmails": {
"title": "E-mails de bienvenue", "title": "Messages de bienvenue",
"description": "Si activé, un e-mail sera envoyé aux nouveaux utilisateurs avec l'URL de Jellyfin/Emby et leur nom d'utilisateur." "description": "Si activé, un message sera envoyé aux nouveaux utilisateurs avec l'URL de Jellyfin/Emby et leur nom d'utilisateur."
}, },
"inviteEmails": { "inviteEmails": {
"title": "E-mails d'invitation", "title": "Messages d'invitation",
"description": "Si activé, vous pouvez envoyer une invitation directement à l'adresse e-mail de l'utilisateur. Parce que vous pourriez utiliser un reverse proxy, vous devez renseigner l'URL d'accès aux invitations. Renseignez votre Base URL et ajoutez '/invite'." "description": "Si activé, vous pouvez envoyer une invitation directement à l'adresse e-mail, le Discord ou Matrix de l'utilisateur. Parce que vous pourriez utiliser un reverse proxy, vous devez renseigner l'URL d'accès aux invitations. Renseignez votre Base URL et ajoutez '/invite'."
}, },
"passwordResets": { "passwordResets": {
"title": "Réinitialisation de mot de passe", "title": "Réinitialisation de mot de passe",
@ -104,7 +108,9 @@
"pathToJellyfinNotice": "Si vous ne savez pas où c'est, essayez de réinitialiser votre mot de passe dans Jellyfin. Une popup avec '<path to jellyfin>/passwordreset-*.json' apparaitra.", "pathToJellyfinNotice": "Si vous ne savez pas où c'est, essayez de réinitialiser votre mot de passe dans Jellyfin. Une popup avec '<path to jellyfin>/passwordreset-*.json' apparaitra.",
"resetLinks": "Envoyer un lien plutôt qu'un PIN", "resetLinks": "Envoyer un lien plutôt qu'un PIN",
"resetLinksNotice": "Si l'intégration est activée, utilisez ceci pour synchroniser les réinitialisations de mots de passe Jellyfin avec Ombi.", "resetLinksNotice": "Si l'intégration est activée, utilisez ceci pour synchroniser les réinitialisations de mots de passe Jellyfin avec Ombi.",
"resetLinksLanguage": "Langue du lien de réinitialisation par défaut" "resetLinksLanguage": "Langue du lien de réinitialisation par défaut",
"setPassword": "Définir le mot de passe via le lien",
"setPasswordNotice": "L'activation de cette option signifie que l'utilisateur n'a pas à modifier son mot de passe à partir du code PIN après la réinitialisation. La validation du mot de passe sera également appliquée."
}, },
"passwordValidation": { "passwordValidation": {
"title": "Validation du mot de passe", "title": "Validation du mot de passe",
@ -133,5 +139,9 @@
"updateChannel": "Mettre à jour la chaîne", "updateChannel": "Mettre à jour la chaîne",
"stable": "Stable", "stable": "Stable",
"unstable": "Instable" "unstable": "Instable"
},
"messages": {
"title": "Messages",
"description": "jfa-go peut envoyer des réinitialisations de mot de passe et divers messages par e-mail, Discord, Telegram et/ou Matrix. Vous pouvez configurer l'e-mail ci-dessous, et les autres peuvent être configurés dans les paramètres plus tard. Les instructions se trouvent sur le {n}. Si vous n'en avez pas besoin, vous pouvez désactiver ces fonctionnalités ici."
} }
} }