Commit 9b77aa1b authored by James Addison's avatar James Addison
Browse files

Merge branch 'master' into collabora

parents f5e90eee 0c5e05aa
Pipeline #28185 failed with stage
in 10 minutes and 19 seconds
......@@ -111,13 +111,13 @@ PLUGIN_PACKAGES += mattermost-plugin-channel-export-v0.2.2
PLUGIN_PACKAGES += mattermost-plugin-custom-attributes-v1.3.0
PLUGIN_PACKAGES += mattermost-plugin-github-v2.0.1
PLUGIN_PACKAGES += mattermost-plugin-gitlab-v1.3.0
PLUGIN_PACKAGES += mattermost-plugin-incident-collaboration-v1.12.0
PLUGIN_PACKAGES += mattermost-plugin-incident-collaboration-v1.14.2
PLUGIN_PACKAGES += mattermost-plugin-jenkins-v1.1.0
PLUGIN_PACKAGES += mattermost-plugin-jira-v2.4.0
PLUGIN_PACKAGES += mattermost-plugin-nps-v1.1.0
PLUGIN_PACKAGES += mattermost-plugin-welcomebot-v1.2.0
PLUGIN_PACKAGES += mattermost-plugin-zoom-v1.5.0
PLUGIN_PACKAGES += focalboard-v0.7.0
PLUGIN_PACKAGES += focalboard-v0.7.3
# Prepares the enterprise build if exists. The IGNORE stuff is a hack to get the Makefile to execute the commands outside a target
ifeq ($(BUILD_ENTERPRISE_READY),true)
......
......@@ -759,6 +759,35 @@ func (es *EmailService) sendMailWithEmbeddedFiles(to, subject, htmlBody string,
return mail.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, mailConfig, license != nil && *license.Features.Compliance, "")
}
func (es *EmailService) InvalidateVerifyEmailTokensForUser(userID string) *model.AppError {
tokens, err := es.srv.Store.Token().GetAllTokensByType(TokenTypeVerifyEmail)
if err != nil {
return model.NewAppError("InvalidateVerifyEmailTokensForUser", "api.user.invalidate_verify_email_tokens.error", nil, err.Error(), http.StatusInternalServerError)
}
var appErr *model.AppError = nil
for _, token := range tokens {
tokenExtra := struct {
UserId string
Email string
}{}
if err := json.Unmarshal([]byte(token.Extra), &tokenExtra); err != nil {
appErr = model.NewAppError("InvalidateVerifyEmailTokensForUser", "api.user.invalidate_verify_email_tokens_parse.error", nil, err.Error(), http.StatusInternalServerError)
continue
}
if tokenExtra.UserId != userID {
continue
}
if err := es.srv.Store.Token().Delete(token.Token); err != nil {
appErr = model.NewAppError("InvalidateVerifyEmailTokensForUser", "api.user.invalidate_verify_email_tokens_delete.error", nil, err.Error(), http.StatusInternalServerError)
}
}
return appErr
}
func (es *EmailService) CreateVerifyEmailToken(userID string, newEmail string) (*model.Token, *model.AppError) {
tokenExtra := struct {
UserId string
......@@ -775,6 +804,10 @@ func (es *EmailService) CreateVerifyEmailToken(userID string, newEmail string) (
token := model.NewToken(TokenTypeVerifyEmail, string(jsonData))
if err := es.InvalidateVerifyEmailTokensForUser(userID); err != nil {
return nil, err
}
if err = es.srv.Store.Token().Save(token); err != nil {
var appErr *model.AppError
switch {
......@@ -815,6 +848,33 @@ func (es *EmailService) SendAtUserLimitWarningEmail(email string, locale string,
return true, nil
}
func (es *EmailService) SendLicenseUpForRenewalEmail(email, name, locale, siteURL, renewalLink string, daysToExpiration int) (bool, *model.AppError) {
T := i18n.GetUserTranslations(locale)
subject := T("api.templates.license_up_for_renewal_subject")
data := es.newEmailTemplateData(locale)
data.Props["SiteURL"] = siteURL
data.Props["Title"] = T("api.templates.license_up_for_renewal_title")
data.Props["SubTitle"] = T("api.templates.license_up_for_renewal_subtitle", map[string]interface{}{"UserName": name, "Days": daysToExpiration})
data.Props["SubTitleTwo"] = T("api.templates.license_up_for_renewal_subtitle_two")
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
data.Props["Button"] = T("api.templates.license_up_for_renewal_renew_now")
data.Props["ButtonURL"] = renewalLink
data.Props["QuestionTitle"] = T("api.templates.questions_footer.title")
data.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
body, err := es.srv.TemplatesContainer().RenderToString("license_up_for_renewal", data)
if err != nil {
return false, model.NewAppError("SendLicenseUpForRenewalEmail", "api.user.send_license_up_for_renewal_email.error", nil, err.Error(), http.StatusInternalServerError)
}
if err := es.sendMail(email, subject, body); err != nil {
return false, model.NewAppError("SendLicenseUpForRenewalEmail", "api.user.send_license_up_for_renewal_email.error", nil, err.Error(), http.StatusInternalServerError)
}
return true, nil
}
// SendUpgradeEmail formats an email template and sends an email to an admin specified in the email arg
func (es *EmailService) SendUpgradeEmail(user, email, locale, siteURL, action string) (bool, *model.AppError) {
T := i18n.GetUserTranslations(locale)
......
......@@ -1781,6 +1781,52 @@ func (s *Server) startMetricsServer() {
s.Log.Info("Metrics and profiling server is started", mlog.String("address", l.Addr().String()))
}
func (s *Server) sendLicenseUpForRenewalEmail(users map[string]*model.User, license *model.License) *model.AppError {
key := model.LICENSE_UP_FOR_RENEWAL_EMAIL_SENT + license.Id
if _, err := s.Store.System().GetByName(key); err == nil {
// return early because the key already exists and that means we already executed the code below to send email successfully
return nil
}
daysToExpiration := license.DaysToExpiration()
renewalLink, appErr := s.GenerateLicenseRenewalLink()
if appErr != nil {
return model.NewAppError("s.sendLicenseUpForRenewalEmail", "api.server.license_up_for_renewal.error_generating_link", nil, appErr.Error(), http.StatusInternalServerError)
}
// we want to at least have one email sent out to an admin
countNotOks := 0
for _, user := range users {
name := user.FirstName
if name == "" {
name = user.Username
}
ok, err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration)
if !ok || err != nil {
mlog.Error("Error sending license up for renewal email to", mlog.String("user_email", user.Email))
countNotOks++
}
}
// if not even one admin got an email, we consider that this operation errored
if countNotOks == len(users) {
return model.NewAppError("s.sendLicenseUpForRenewalEmail", "api.server.license_up_for_renewal.error_sending_email", nil, "", http.StatusInternalServerError)
}
system := model.System{
Name: key,
Value: "true",
}
if err := s.Store.System().Save(&system); err != nil {
mlog.Debug("Failed to mark license up for renewal email sending as completed.", mlog.Err(err))
}
return nil
}
func (s *Server) doLicenseExpirationCheck() {
s.LoadLicense()
license := s.License()
......@@ -1790,17 +1836,24 @@ func (s *Server) doLicenseExpirationCheck() {
return
}
if !license.IsPastGracePeriod() {
mlog.Debug("License is not past the grace period.")
return
}
users, err := s.Store.User().GetSystemAdminProfiles()
if err != nil {
mlog.Error("Failed to get system admins for license expired message from Mattermost.")
return
}
if license.IsWithinExpirationPeriod() {
appErr := s.sendLicenseUpForRenewalEmail(users, license)
if appErr != nil {
mlog.Debug(appErr.Error())
}
}
if !license.IsPastGracePeriod() {
mlog.Debug("License is not past the grace period.")
return
}
//send email to admin(s)
for _, user := range users {
user := user
......
......@@ -133,6 +133,7 @@ func setup(tb testing.TB) *TestHelper {
if testing.Short() {
tb.SkipNow()
}
tb.Skip("MM-36947")
dbStore := mainHelper.GetStore()
dbStore.DropAllTables()
dbStore.MarkSystemRanUnitTests()
......
......@@ -1301,7 +1301,6 @@ func (a *App) SendPasswordReset(email string, siteURL string) (bool, *model.AppE
}
func (a *App) CreatePasswordRecoveryToken(userID, email string) (*model.Token, *model.AppError) {
tokenExtra := struct {
UserId string
Email string
......
......@@ -488,6 +488,48 @@ func TestUpdateUserEmail(t *testing.T) {
assert.Equal(t, err.Id, "app.user.save.email_exists.app_error")
assert.Nil(t, user3)
})
t.Run("Only the last token works if verification is required", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.RequireEmailVerification = true
})
// we update the email a first time and update. The first
// token is sent with the email
user.Email = th.MakeEmail()
_, appErr := th.App.UpdateUser(user, true)
require.Nil(t, appErr)
tokens := []*model.Token{}
require.Eventually(t, func() bool {
var err error
tokens, err = th.App.Srv().Store.Token().GetAllTokensByType(TokenTypeVerifyEmail)
return err == nil && len(tokens) == 1
}, 100*time.Millisecond, 10*time.Millisecond)
firstToken := tokens[0]
// without using the first token, we update the email a second
// time and another token gets sent. The first one should not
// work anymore and the second should work properly
user.Email = th.MakeEmail()
_, appErr = th.App.UpdateUser(user, true)
require.Nil(t, appErr)
require.Eventually(t, func() bool {
var err error
tokens, err = th.App.Srv().Store.Token().GetAllTokensByType(TokenTypeVerifyEmail)
return err == nil && len(tokens) == 1
}, 100*time.Millisecond, 10*time.Millisecond)
secondToken := tokens[0]
_, err := th.App.Srv().Store.Token().GetByToken(firstToken.Token)
require.Error(t, err)
require.NotNil(t, th.App.VerifyEmailFromToken(firstToken.Token))
require.Nil(t, th.App.VerifyEmailFromToken(secondToken.Token))
require.NotNil(t, th.App.VerifyEmailFromToken(firstToken.Token))
})
}
func getUserFromDB(a *App, id string, t *testing.T) *model.User {
......
......@@ -144,8 +144,6 @@ require (
willnorris.com/go/imageproxy v0.10.0
)
replace github.com/dyatlov/go-opengraph => github.com/agnivade/go-opengraph v0.0.0-20201221052033-34e69ee2a627
// Hack to prevent the willf/bitset module from being upgraded to 1.2.0.
// They changed the module path from github.com/willf/bitset to
// github.com/bits-and-blooms/bitset and a couple of dependent repos are yet
......
......@@ -89,8 +89,6 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
github.com/advancedlogic/GoOse v0.0.0-20191112112754-e742535969c1/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
github.com/advancedlogic/GoOse v0.0.0-20200830213114-1225d531e0ad h1:gyzOmx++wVkSj5kLzYtvNN2ooeJGTFTtV37t5Do4sdM=
github.com/advancedlogic/GoOse v0.0.0-20200830213114-1225d531e0ad/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
github.com/agnivade/go-opengraph v0.0.0-20201221052033-34e69ee2a627 h1:Z5Cd4rshcYMKhKuGaVeSaJ4sAiLHzB5YXF10+TvJDU4=
github.com/agnivade/go-opengraph v0.0.0-20201221052033-34e69ee2a627/go.mod h1:4M/YtupetXsgPzt5YlR87whvFSHtYj1eBFUgcXiwrOY=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
......@@ -251,6 +249,8 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 h1:AQLr//nh20BzN3hIWj2+/Gt3FwSs8Nwo/nz4hMIcLPg=
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09/go.mod h1:nYia/MIs9OyvXXYboPmNOj0gVWo97Wx0sde+ZuKkoM4=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
......
......@@ -429,7 +429,7 @@
},
{
"id": "api.command_channel_header.update_channel.app_error",
"translation": "Fehler beim Aktualisieren des aktuellen Kanals."
"translation": "Fehler beim Aktualisieren der Kanalüberschrift."
},
{
"id": "api.command_channel_purpose.channel.app_error",
......@@ -461,7 +461,7 @@
},
{
"id": "api.command_channel_purpose.update_channel.app_error",
"translation": "Fehler beim Aktualisieren des aktuellen Kanals."
"translation": "Fehler beim Aktualisieren des Kanalzwecks."
},
{
"id": "api.command_channel_remove.channel.app_error",
......@@ -7182,5 +7182,492 @@
{
"id": "error",
"translation": "Fehler"
},
{
"id": "api.command_share.not_shared_channel_unshare",
"translation": "Ein nicht geteilter Kanal kann nicht ungeteilt werden."
},
{
"id": "api.command_share.no_remote_invited",
"translation": "Keine sichere Verbindung wurde in diesen Kanal eingeladen."
},
{
"id": "api.command_share.must_specify_valid_remote",
"translation": "Muss eine gültige sichere Verbindungs-ID für die Einladung verwenden."
},
{
"id": "api.command_share.missing_action",
"translation": "Fehlende Aktion. Verfügbare Aktionen: {{.Actions}}"
},
{
"id": "api.command_share.invite_remote.help",
"translation": "Lädt eine externe Mattermost Instanz zum aktuellen geteilten Kanal ein"
},
{
"id": "api.command_share.invitation_sent",
"translation": "Einladung für geteilten Kanal wurde an `{{.Name}} {{.SiteURL}}` gesendet."
},
{
"id": "api.command_share.invalid_value.error",
"translation": "Ungültiger Wert für '{{.Arg}}': {{.Error}}"
},
{
"id": "api.command_share.fetch_remote_status.error",
"translation": "Konnte den Status für die sicheren Verbindungen nicht abrufen: {{.Error}}."
},
{
"id": "api.command_share.fetch_remote.error",
"translation": "Fehler beim Holen von sicheren Verbindungen: {{.Error}}"
},
{
"id": "api.command_share.desc",
"translation": "Teilt den aktuellen Kanal mit einer externen Mattemost Instanz."
},
{
"id": "api.command_share.could_not_uninvite.error",
"translation": "Konnte `{{.RemoteId}}`nicht ausladen: {{.Error}}"
},
{
"id": "api.command_share.check_channel_exist.error",
"translation": "Fehler während der Prüfung, ob der geteilte Kanal existiert: {{.Error}}"
},
{
"id": "api.command_share.channel_status_id",
"translation": "Status für Kanal ID `{{.ChannelId}}`"
},
{
"id": "api.command_share.channel_status.help",
"translation": "Status für diesen geteilten Kanal anzeigen"
},
{
"id": "api.command_share.channel_shared",
"translation": "Die Kanal ist jetzt geteilt."
},
{
"id": "api.command_share.channel_remote_id_not_exists",
"translation": "Sichere Verbindung für den geteilte Kanal `{{.RemoteId}}` existiert nicht für diesen Kanal."
},
{
"id": "api.command_share.channel_invite_not_home.error",
"translation": "Kann keine sichere Verbindung zu einem geteilten Kanal einladen, der seinen Ursprung woanders hat."
},
{
"id": "api.command_share.channel_invite.error",
"translation": "Fehler beim Einladen von `{{.Name}}`in diesen Kanal: {{.Error}}"
},
{
"id": "api.command_share.available_actions",
"translation": "Verfügbare Aktionen: {{.Actions}}"
},
{
"id": "api.command_remote.unknown_action",
"translation": "Unbekannte Aktion `{{.Action}}`"
},
{
"id": "api.command_remote.status.help",
"translation": "Status für alle sicheren Verbindungen anzeigen"
},
{
"id": "api.command_remote.site_url_not_set",
"translation": "Seiten-URL ist nicht gesetzt. Bitte unter Systemkonsole > Umgebung > Webserver setzen."
},
{
"id": "api.command_remote.service_not_enabled",
"translation": "Dienst für sichere Verbindungen ist nicht aktiviert."
},
{
"id": "api.command_remote.service_disabled",
"translation": "Dienst für sichere Verbindungen ist deaktiviert."
},
{
"id": "api.command_remote.remove_remote_id.help",
"translation": "ID der zu löschenden sicheren Verbindung."
},
{
"id": "api.command_remote.remove_remote.error",
"translation": "Konnte sichere Verbindung nicht löschen: {{.Error}}"
},
{
"id": "api.command_remote.remove.help",
"translation": "Löscht eine sichere Verbindung"
},
{
"id": "api.command_remote.remotes_not_found",
"translation": "Keine sicheren Verbindungen gefunden."
},
{
"id": "api.command_remote.remote_table_header",
"translation": "| Sichere Verbindung | Anzeigename | Verbindungs-ID | Site URL | Einladung angenommen | Online | Letzter Ping |"
},
{
"id": "api.command_remote.remote_add_remove.help",
"translation": "Hinzufügen/Löschen von sicheren Verbindungen. Verfügbare Aktionen: {{.Actions}}"
},
{
"id": "api.command_remote.permission_required",
"translation": "Sie benötigen`{{.Permission}}`Berechtigungen um sichere Verbindungen zu verwalten."
},
{
"id": "api.command_remote.name.hint",
"translation": "Ein eindeutiger Name für die sichere Verbindung"
},
{
"id": "api.command_remote.name.help",
"translation": "Name der sicheren Verbindung"
},
{
"id": "api.command_remote.name",
"translation": "Sichere Verbindung"
},
{
"id": "api.command_remote.missing_empty",
"translation": "Fehlendes order leeres `{{.Arg}}`"
},
{
"id": "api.command_remote.invite_summary",
"translation": "Sende die folgende AES 256-bit verschlüsselte Einladung zu einem externen Mattermost Systemadministrator zusammen mit dem Passwort. Diese werden den `{{.Command}}` Slash-Befehl verwenden um die Einladung zu akzeptieren.\n\n```\n{{.Invitation}}\n```\n\n**Sicherstellen, dass die sichere Verbindung die Mattermost Instanz über {{.SiteURL}} erreicht. **"
},
{
"id": "api.command_remote.invite_password.help",
"translation": "Passwort für Einladung"
},
{
"id": "api.command_remote.invite.help",
"translation": "Sichere Verbindung einladen"
},
{
"id": "api.command_remote.invitation.hint",
"translation": "Die verschlüsselte Einladung von einer sicheren Verbindung"
},
{
"id": "api.command_remote.invitation.help",
"translation": "Einladung von einer sicheren Verbindung"
},
{
"id": "api.command_remote.incorrect_password.error",
"translation": "Konnte Einladung nicht entschlüsseln. Falsches Passwort oder beschädigte Einladung: {{.Error}}"
},
{
"id": "api.command_remote.fetch_status.error",
"translation": "Konnte keine sicheren Verbindungen holen: {{.Error}}"
},
{
"id": "api.command_remote.encrypt_invitation.error",
"translation": "Konnte Einladung nicht verschlüsseln: {{.Error}}"
},
{
"id": "api.command_remote.displayname.hint",
"translation": "Ein Anzeigenname für die sichere Verbindung"
},
{
"id": "api.command_remote.displayname.help",
"translation": "Anzeigename der sicheren Verbindung"
},
{
"id": "api.command_remote.decode_invitation.error",
"translation": "Konnte Einladung nicht dekodieren: {{.Error}}"
},
{
"id": "api.command_custom_status.success",
"translation": "Ihr Status ist auf “{{.EmojiName}} {{.StatusMessage}}” gesetzt. Sie können Ihren Status über das Status-Popover im Kopf der Kanalseitenleiste setzen."
},
{
"id": "api.command_custom_status.hint",
"translation": "[:emoji_name:] [status_message] oder leeren"
},
{
"id": "api.command_custom_status.desc",
"translation": "Status setzen oder löschen"
},
{
"id": "api.command_channel_purpose.update_channel.max_length",
"translation": "Der eingegebene Text ist zu lang. Der Kanalzweck ist auf {{.MaxLength}} Zeichen beschränkt."
},
{
"id": "api.cloud.license_error",
"translation": "Ihre Lizenz unterstützt keine Cloud Anfragen."
},
{
"id": "api.cloud.get_subscription.error",
"translation": "Fehler beim Abruf des Cloud-Abonnements."
},
{
"id": "api.cloud.get_admins_emails.error",
"translation": "Fehler beim Abruf der Email des Systemadmins."
},
{
"id": "api.cloud.cws_webhook_event_missing_error",
"translation": "Ein Webhook-Ereignis wurde nicht bearbeitet. Entweder fehlt es oder ist nicht gültig."
},
{
"id": "api.cloud.app_error",
"translation": "Interner Fehler während einer Cloud API Anfrage."
},
{
"id": "api.post.search_posts.invalid_body.app_error",
"translation": "Der Request Body kann nicht analysiert werden."
},
{
"id": "api.post.search_files.invalid_body.app_error",
"translation": "Der Request Body kann nicht analysiert werden."
},
{
"id": "api.post.error_get_post_id.pending",
"translation": "Die ausstehende Nachricht kann nicht abgerufen werden."
},
{
"id": "api.oauth.auth_complete",
"translation": "Authentifizierung vollständig"
},
{
"id": "api.no_license",
"translation": "E10 oder E20 Lizenz wird benötigt um diesen Endpunkt zu benutzen."
},
{
"id": "api.migrate_to_saml.error",
"translation": "Kann SAML nicht migrieren."
},
{
"id": "api.license.request_trial_license.no-site-url.app_error",
"translation": "Konnte keine Test-Lizenz anfragen. Bitte eine Seiten URL in der Webserver Sektion der Mattermost Systemkonsole konfigurieren."
},
{
"id": "api.license.request_trial_license.fail_get_user_count.app_error",
"translation": "Konnte keine Test-Lizenz holen. Bitte nochmal versuchen oder support@mattermost.com kontaktieren. Konnte die Anzahl der registrierten Benutzer nicht bestimmen."
},
{
"id": "api.license.request_trial_license.app_error",
"translation": "Konnte keine Test-Lizenz holen. Bitte nochmal versuchen oder support@mattermost.com kontaktieren."
},
{
"id": "api.license.request_renewal_link.app_error",
"translation": "Fehler beim Holen des Lizenz-Erneuerungslinks"
},
{
"id": "api.license.request-trial.can-start-trial.not-allowed",
"translation": "Dieser Test-Lizenzschlüssel für Mattermost Enterprise Edition ist abgelaufen und nicht mehr gültig. Wenn Sie Ihre Testphase verlängern wollen, kontaktieren Sie bitte [unser Vertriebsteam](https://mattermost.com/contact-us/)."
},
{
"id": "api.license.request-trial.can-start-trial.error",
"translation": "Konnte nicht prüfen, ob eine Testphase gestartet werden kann"
},
{
"id": "api.license.request-trial.bad-request.terms-not-accepted",
"translation": "Sie müssen das Mattermost Software Evaluation Agreement und die Privacy Policy akzeptieren um eine Lizenz anzufragen."
},
{
"id": "api.job.unable_to_download_job.incorrect_job_type",
"translation": "Der Job-Typ, den Sie versuchen runterzuladen, wird momentan nicht unterstützt"
},
{
"id": "api.job.unable_to_download_job",
"translation": "Kann den Job nicht herunterladen"
},
{
"id": "api.job.unable_to_create_job.incorrect_job_type",
"translation": "Der Job-Typ des Jobs, den Sie versuchen anzulegen ist ungültig"
},
{
"id": "api.job.retrieve.nopermissions",
"translation": "Die Job Typen eines Jobs, den Sie versuchen zu holen, enthalten keine Berechtigungen"
},
{
"id": "api.invalid_custom_url_scheme",
"translation": "Ungültiges benutzerdefiniertes URL Schema wurde zur Verfügung gestellt"
},
{
"id": "api.getThreadsForUser.bad_params",
"translation": "Before und After Parameter für getThreadsForUser schließen sich gegenseitig aus"
},
{
"id": "api.file.test_connection_s3_bucket_does_not_exist.app_error",
"translation": "Stellen Sie sicher, dass Ihr Amazon S3 Bucket verfügbar ist und prüfen Sie Ihre Bucket Berechtigungen."
},
{
"id": "api.file.test_connection_s3_auth.app_error",
"translation": "Kann nicht zu S3 verbinden. Prüfen Sie Ihre Amazon S3 Autorisierungsparameter und Authentifizierungseinstellungen."
},
{
"id": "api.file.file_reader.app_error",
"translation": "Kann keinen Dateileser bekommen."