Commit 0145cd4d authored by Derrick Anderson's avatar Derrick Anderson

Merge branch 'merge-4.7'

parents 30197584 c3929159
......@@ -566,8 +566,6 @@ func (a *App) sendPushNotification(post *model.Post, user *model.User, channel *
channelName = senderName
}
userLocale := utils.GetUserTranslations(user.Locale)
msg := model.PushNotification{}
if badge := <-a.Srv.Store.User().GetUnreadCount(user.Id); badge.Err != nil {
msg.Badge = 1
......@@ -596,44 +594,10 @@ func (a *App) sendPushNotification(post *model.Post, user *model.User, channel *
msg.FromWebhook = fw
}
if *a.Config().EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION {
msg.Category = model.CATEGORY_CAN_REPLY
if channel.Type == model.CHANNEL_DIRECT {
msg.Message = senderName + ": " + model.ClearMentionTags(post.Message)
} else {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_in") + channelName + ": " + model.ClearMentionTags(post.Message)
}
} else if *a.Config().EmailSettings.PushNotificationContents == model.GENERIC_NO_CHANNEL_NOTIFICATION {
if channel.Type == model.CHANNEL_DIRECT {
msg.Category = model.CATEGORY_CAN_REPLY
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
} else if wasMentioned || channel.Type == model.CHANNEL_GROUP {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention_no_channel")
} else {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_non_mention_no_channel")
}
} else {
if channel.Type == model.CHANNEL_DIRECT {
msg.Category = model.CATEGORY_CAN_REPLY
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
} else if wasMentioned || channel.Type == model.CHANNEL_GROUP {
msg.Category = model.CATEGORY_CAN_REPLY
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
} else {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_non_mention") + channelName
}
}
// If the post only has images then push an appropriate message
if len(post.Message) == 0 && post.FileIds != nil && len(post.FileIds) > 0 {
if channel.Type == model.CHANNEL_DIRECT {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_image_only_dm")
} else {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_image_only") + channelName
}
}
userLocale := utils.GetUserTranslations(user.Locale)
hasFiles := post.FileIds != nil && len(post.FileIds) > 0
//l4g.Debug("Sending push notification for user %v with msg of '%v'", user.Id, msg.Message)
msg.Message, msg.Category = a.getPushNotificationMessage(post.Message, wasMentioned, hasFiles, senderName, channelName, channel.Type, userLocale)
for _, session := range sessions {
tmpMessage := *model.PushNotificationFromJson(strings.NewReader(msg.ToJson()))
......@@ -655,6 +619,54 @@ func (a *App) sendPushNotification(post *model.Post, user *model.User, channel *
return nil
}
func (a *App) getPushNotificationMessage(postMessage string, wasMentioned bool, hasFiles bool, senderName string, channelName string, channelType string, userLocale i18n.TranslateFunc) (string, string) {
message := ""
category := ""
if *a.Config().EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION {
category = model.CATEGORY_CAN_REPLY
if channelType == model.CHANNEL_DIRECT {
message = senderName + ": " + model.ClearMentionTags(postMessage)
} else {
message = senderName + userLocale("api.post.send_notifications_and_forget.push_in") + channelName + ": " + model.ClearMentionTags(postMessage)
}
} else if *a.Config().EmailSettings.PushNotificationContents == model.GENERIC_NO_CHANNEL_NOTIFICATION {
if channelType == model.CHANNEL_DIRECT {
category = model.CATEGORY_CAN_REPLY
message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
} else if wasMentioned {
message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention_no_channel")
} else {
message = senderName + userLocale("api.post.send_notifications_and_forget.push_non_mention_no_channel")
}
} else {
if channelType == model.CHANNEL_DIRECT {
category = model.CATEGORY_CAN_REPLY
message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
} else if wasMentioned {
category = model.CATEGORY_CAN_REPLY
message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
} else {
message = senderName + userLocale("api.post.send_notifications_and_forget.push_non_mention") + channelName
}
}
// If the post only has images then push an appropriate message
if len(postMessage) == 0 && hasFiles {
if channelType == model.CHANNEL_DIRECT {
message = senderName + userLocale("api.post.send_notifications_and_forget.push_image_only_dm")
} else {
message = senderName + userLocale("api.post.send_notifications_and_forget.push_image_only") + channelName
}
}
return message, category
}
func (a *App) ClearPushNotification(userId string, channelId string) {
a.Go(func() {
// Sleep is to allow the read replicas a chance to fully sync
......@@ -819,44 +831,52 @@ func GetExplicitMentions(message string, keywords map[string][]string) *Explicit
ret.MentionedUserIds[id] = true
}
}
checkForMention := func(word string) bool {
isMention := false
if word == "@here" {
ret.HereMentioned = true
}
if word == "@channel" {
ret.ChannelMentioned = true
}
if word == "@all" {
ret.AllMentioned = true
}
// Non-case-sensitive check for regular keys
if ids, match := keywords[strings.ToLower(word)]; match {
addMentionedUsers(ids)
isMention = true
}
// Case-sensitive check for first name
if ids, match := keywords[word]; match {
addMentionedUsers(ids)
isMention = true
}
return isMention
}
processText := func(text string) {
for _, word := range strings.FieldsFunc(text, func(c rune) bool {
// Split on any whitespace or punctuation that can't be part of an at mention or emoji pattern
return !(c == ':' || c == '.' || c == '-' || c == '_' || c == '@' || unicode.IsLetter(c) || unicode.IsNumber(c))
}) {
isMention := false
// skip word with format ':word:' with an assumption that it is an emoji format only
if word[0] == ':' && word[len(word)-1] == ':' {
continue
}
if word == "@here" {
ret.HereMentioned = true
}
if word == "@channel" {
ret.ChannelMentioned = true
}
if word == "@all" {
ret.AllMentioned = true
}
// Non-case-sensitive check for regular keys
if ids, match := keywords[strings.ToLower(word)]; match {
addMentionedUsers(ids)
isMention = true
}
// Case-sensitive check for first name
if ids, match := keywords[word]; match {
addMentionedUsers(ids)
isMention = true
if checkForMention(word) {
continue
}
if isMention {
// remove trailing '.', as that is the end of a sentence
word = strings.TrimSuffix(word, ".")
if checkForMention(word) {
continue
}
......@@ -867,27 +887,10 @@ func GetExplicitMentions(message string, keywords map[string][]string) *Explicit
})
for _, splitWord := range splitWords {
if splitWord == "@here" {
ret.HereMentioned = true
}
if splitWord == "@all" {
ret.AllMentioned = true
}
if splitWord == "@channel" {
ret.ChannelMentioned = true
}
// Non-case-sensitive check for regular keys
if ids, match := keywords[strings.ToLower(splitWord)]; match {
addMentionedUsers(ids)
if checkForMention(splitWord) {
continue
}
// Case-sensitive check for first name
if ids, match := keywords[splitWord]; match {
addMentionedUsers(ids)
} else if _, ok := systemMentions[splitWord]; !ok && strings.HasPrefix(splitWord, "@") {
if _, ok := systemMentions[splitWord]; !ok && strings.HasPrefix(splitWord, "@") {
username := splitWord[1:]
ret.OtherPotentialMentions = append(ret.OtherPotentialMentions, username)
}
......
This diff is collapsed.
......@@ -302,10 +302,16 @@ func (a *App) joinUserToTeam(team *model.Team, user *model.User) (*model.TeamMem
return rtm, true, nil
}
if tmr := <-a.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
return nil, false, tmr.Err
if membersCount := <-a.Srv.Store.Team().GetActiveMemberCount(tm.TeamId); membersCount.Err != nil {
return nil, false, membersCount.Err
} else if membersCount.Data.(int64) >= int64(*a.Config().TeamSettings.MaxUsersPerTeam) {
return nil, false, model.NewAppError("joinUserToTeam", "app.team.join_user_to_team.max_accounts.app_error", nil, "teamId="+tm.TeamId, http.StatusBadRequest)
} else {
return tmr.Data.(*model.TeamMember), false, nil
if tmr := <-a.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
return nil, false, tmr.Err
} else {
return tmr.Data.(*model.TeamMember), false, nil
}
}
} else {
// Membership appears to be missing. Lets try to add.
......
......@@ -460,3 +460,91 @@ func TestAddUserToTeamByHashMismatchedInviteId(t *testing.T) {
assert.Nil(t, team)
assert.Equal(t, "api.user.create_user.signup_link_mismatched_invite_id.app_error", err.Id)
}
func TestJoinUserToTeam(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
id := model.NewId()
team := &model.Team{
DisplayName: "dn_" + id,
Name: "name" + id,
Email: "success+" + id + "@simulator.amazonses.com",
Type: model.TEAM_OPEN,
}
if _, err := th.App.CreateTeam(team); err != nil {
t.Log(err)
t.Fatal("Should create a new team")
}
maxUsersPerTeam := th.App.Config().TeamSettings.MaxUsersPerTeam
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.MaxUsersPerTeam = maxUsersPerTeam })
th.App.PermanentDeleteTeam(team)
}()
one := 1
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.MaxUsersPerTeam = &one })
t.Run("new join", func(t *testing.T) {
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser, _ := th.App.CreateUser(&user)
defer th.App.PermanentDeleteUser(&user)
if _, alreadyAdded, err := th.App.joinUserToTeam(team, ruser); alreadyAdded || err != nil {
t.Fatal("Should return already added equal to false and no error")
}
})
t.Run("join when you are a member", func(t *testing.T) {
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser, _ := th.App.CreateUser(&user)
defer th.App.PermanentDeleteUser(&user)
th.App.joinUserToTeam(team, ruser)
if _, alreadyAdded, err := th.App.joinUserToTeam(team, ruser); !alreadyAdded || err != nil {
t.Fatal("Should return already added and no error")
}
})
t.Run("re-join after leaving", func(t *testing.T) {
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser, _ := th.App.CreateUser(&user)
defer th.App.PermanentDeleteUser(&user)
th.App.joinUserToTeam(team, ruser)
th.App.LeaveTeam(team, ruser, ruser.Id)
if _, alreadyAdded, err := th.App.joinUserToTeam(team, ruser); alreadyAdded || err != nil {
t.Fatal("Should return already added equal to false and no error")
}
})
t.Run("new join with limit problem", func(t *testing.T) {
user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser1, _ := th.App.CreateUser(&user1)
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser2, _ := th.App.CreateUser(&user2)
defer th.App.PermanentDeleteUser(&user1)
defer th.App.PermanentDeleteUser(&user2)
th.App.joinUserToTeam(team, ruser1)
if _, _, err := th.App.joinUserToTeam(team, ruser2); err == nil {
t.Fatal("Should fail")
}
})
t.Run("re-join alfter leaving with limit problem", func(t *testing.T) {
user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser1, _ := th.App.CreateUser(&user1)
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
ruser2, _ := th.App.CreateUser(&user2)
defer th.App.PermanentDeleteUser(&user1)
defer th.App.PermanentDeleteUser(&user2)
th.App.joinUserToTeam(team, ruser1)
th.App.LeaveTeam(team, ruser1, ruser1.Id)
th.App.joinUserToTeam(team, ruser2)
if _, _, err := th.App.joinUserToTeam(team, ruser1); err == nil {
t.Fatal("Should fail")
}
})
}
......@@ -2184,7 +2184,7 @@
},
{
"id": "api.team.add_user_to_team.added",
"translation": "%v wurde von %v zum Team hinzugefügt"
"translation": "%v wurde von %v zum Team hinzugefügt."
},
{
"id": "api.team.add_user_to_team.missing_parameter.app_error",
......@@ -2202,10 +2202,6 @@
"id": "api.team.create_team_from_signup.expired_link.app_error",
"translation": "Der Link ist abgelaufen"
},
{
"id": "api.team.create_team_from_signup.invalid_link.app_error",
"translation": "Der Einladungslink scheint nicht gültig zu sein"
},
{
"id": "api.team.create_team_from_signup.unavailable.app_error",
"translation": "Diese URL ist nicht verfügbar. Bitte wähle eine andere."
......@@ -2710,6 +2706,10 @@
"id": "api.user.create_user.signup_link_invalid.app_error",
"translation": "Der Registrierungslink scheint nicht gültig zu sein"
},
{
"id": "api.user.create_user.signup_link_mismatched_invite_id.app_error",
"translation": "Der Einladungslink scheint nicht gültig zu sein"
},
{
"id": "api.user.create_user.team_name.app_error",
"translation": "Ungültiger Teamname"
......@@ -3690,6 +3690,10 @@
"id": "app.plugin.upload_disabled.app_error",
"translation": "Plugins und/oder Plugin-Uploads wurden deaktiviert."
},
{
"id": "app.team.join_user_to_team.max_accounts.app_error",
"translation": "Dieses Team hat die maximale Anzahl erlaubter Konten erreicht. Kontaktieren Sie Ihren Systemadministrator, um eine höhere Begrenzung setzen zu lassen."
},
{
"id": "app.user_access_token.disabled",
"translation": "Persönliche Zugriffs-Token sind auf diesem Server deaktiviert. Bitte kontaktieren Sie ihren Systemadministrator für Details."
......@@ -4758,6 +4762,10 @@
"id": "model.config.is_valid.file_thumb_width.app_error",
"translation": "Ungültige Thumbnauilbreite in Dateieinstellungen. Muss eine positive Zahl sein."
},
{
"id": "model.config.is_valid.group_unread_channels.app_error",
"translation": "Ungültige Diensteinstellungen für Gruppierung von ungelesenen Kanälen. Muss 'disabled', 'default_on' oder default_off' sein."
},
{
"id": "model.config.is_valid.image_proxy_type.app_error",
"translation": "Ungültiger Bild-Proxy-Typ für Diensteinstellungen."
......@@ -7290,10 +7298,6 @@
"id": "web.root.singup_title",
"translation": "Registrieren"
},
{
"id": "web.signup_team_complete.invalid_link.app_error",
"translation": "Der Registrierungslink scheint nicht gültig zu sein"
},
{
"id": "web.signup_team_complete.link_expired.app_error",
"translation": "Der Registrierungslink ist abgelaufen"
......@@ -7310,10 +7314,6 @@
"id": "web.signup_user_complete.link_expired.app_error",
"translation": "Der Registrierungslink ist abgelaufen"
},
{
"id": "web.signup_user_complete.link_invalid.app_error",
"translation": "Der Registrierungslink scheint nicht gültig zu sein"
},
{
"id": "web.signup_user_complete.no_invites.app_error",
"translation": "Dieser Teamtyp erlaubt keine offnen Einladungen"
......
......@@ -1808,11 +1808,11 @@
},
{
"id": "api.post.send_notifications_and_forget.push_image_only",
"translation": " Uploaded one or more files in "
"translation": " uploaded one or more files in "
},
{
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
"translation": " Uploaded one or more files in a direct message"
"translation": " uploaded one or more files in a direct message"
},
{
"id": "api.post.send_notifications_and_forget.push_in",
......@@ -2699,11 +2699,11 @@
"translation": "The signup link has expired"
},
{
"id": "api.user.create_user.signup_link_mismatched_invite_id.app_error",
"id": "api.user.create_user.signup_link_invalid.app_error",
"translation": "The signup link does not appear to be valid"
},
{
"id": "api.user.create_user.signup_link_invalid.app_error",
"id": "api.user.create_user.signup_link_mismatched_invite_id.app_error",
"translation": "The signup link does not appear to be valid"
},
{
......@@ -3698,6 +3698,10 @@
"id": "app.plugin.upload_disabled.app_error",
"translation": "Plugins and/or plugin uploads have been disabled."
},
{
"id": "app.team.join_user_to_team.max_accounts.app_error",
"translation": "This team has reached the maximum number of allowed accounts. Contact your systems administrator to set a higher limit."
},
{
"id": "app.user_access_token.disabled",
"translation": "Personal access tokens are disabled on this server. Please contact your system administrator for details."
......
......@@ -2202,10 +2202,6 @@
"id": "api.team.create_team_from_signup.expired_link.app_error",
"translation": "El enlace de registro ha expirado"
},
{
"id": "api.team.create_team_from_signup.invalid_link.app_error",
"translation": "El enlace de registro parece ser inválido"
},
{
"id": "api.team.create_team_from_signup.unavailable.app_error",
"translation": "Este URL no está disponible. Por favor, prueba con otra."
......@@ -2710,6 +2706,10 @@
"id": "api.user.create_user.signup_link_invalid.app_error",
"translation": "El enlace de registro parece ser inválido"
},
{
"id": "api.user.create_user.signup_link_mismatched_invite_id.app_error",
"translation": "El enlace de registro parece ser inválido"
},
{
"id": "api.user.create_user.team_name.app_error",
"translation": "Nombre del equipo inválido"
......@@ -3690,6 +3690,10 @@
"id": "app.plugin.upload_disabled.app_error",
"translation": "Los Complementos y/o la carga de complementos han sido deshabilitados."
},
{
"id": "app.team.join_user_to_team.max_accounts.app_error",
"translation": "Este equipo ha alcanzado el número máximo de cuentas permitidas. Contacta a un administrador de sistema para que asigne un límite mayor."
},
{
"id": "app.user_access_token.disabled",
"translation": "Los tokens de acceso personal están inhabilitados en este servidor. Por favor, póngase en contacto con su administrador del sistema para obtener más detalles."
......@@ -4758,6 +4762,10 @@
"id": "model.config.is_valid.file_thumb_width.app_error",
"translation": "El ancho para la imagen de miniatura es inválido en la configuración de archivos. Debe ser un número positivo."
},
{
"id": "model.config.is_valid.group_unread_channels.app_error",
"translation": "Ajuste invalido para el agrupamiento de canales no leídos en la configuración de servicio. Debe ser 'disabled', 'default_on' o 'default_off'."
},
{
"id": "model.config.is_valid.image_proxy_type.app_error",
"translation": "Tipo de proxy para las imágenes no válido en la configuración del servicio."
......@@ -7290,10 +7298,6 @@
"id": "web.root.singup_title",
"translation": "Registrar"
},
{
"id": "web.signup_team_complete.invalid_link.app_error",
"translation": "El enlace de registro parece ser inválido"
},
{
"id": "web.signup_team_complete.link_expired.app_error",
"translation": "El enlace de registro ha expirado"
......@@ -7310,10 +7314,6 @@
"id": "web.signup_user_complete.link_expired.app_error",
"translation": "El enlace de registro ha expirado"
},
{
"id": "web.signup_user_complete.link_invalid.app_error",
"translation": "El enlace de registro parece ser inválido"
},
{
"id": "web.signup_user_complete.no_invites.app_error",
"translation": "El tipo de equipo no permite realizar invitaciones"
......
......@@ -153,7 +153,7 @@
},
{
"id": "api.channel.add_member.added",
"translation": "%v a été ajouté au canal par %v"
"translation": "%v a été ajouté au canal par %v."
},
{
"id": "api.channel.add_member.find_channel.app_error",
......@@ -201,11 +201,11 @@
},
{
"id": "api.channel.change_channel_privacy.private_to_public",
"translation": "This channel has been converted to a Public Channel and can be joined by any team member."
"translation": "Ce canal a été converti en canal public et peut être rejoint par tout membre de l'équipe."
},
{
"id": "api.channel.change_channel_privacy.public_to_private",
"translation": "This channel has been converted to a Private Channel."
"translation": "Ce canal a été converti en canal privé."
},
{
"id": "api.channel.create_channel.direct_channel.app_error",
......@@ -2184,7 +2184,7 @@
},
{
"id": "api.team.add_user_to_team.added",
"translation": "%v a été ajouté à l'équipe par %v"
"translation": "%v a été ajouté à l'équipe par %v."
},
{
"id": "api.team.add_user_to_team.missing_parameter.app_error",
......@@ -2202,10 +2202,6 @@
"id": "api.team.create_team_from_signup.expired_link.app_error",
"translation": "Le lien d'inscription a expiré."
},
{
"id": "api.team.create_team_from_signup.invalid_link.app_error",
"translation": "Le lien d'inscription semble ne pas être valide."
},
{
"id": "api.team.create_team_from_signup.unavailable.app_error",
"translation": "Cette URL n'est pas disponible. Veuillez en essayer une autre."
......@@ -2710,6 +2706,10 @@
"id": "api.user.create_user.signup_link_invalid.app_error",
"translation": "Le lien d'enregistrement n'est pas valide."
},
{
"id": "api.user.create_user.signup_link_mismatched_invite_id.app_error",
"translation": "Le lien d'inscription semble ne pas être valide."
},
{
"id": "api.user.create_user.team_name.app_error",
"translation": "Nom d'équipe incorrect"
......@@ -2956,7 +2956,7 @@
},
{
"id": "api.user.upload_profile_user.decode_config.app_error",
"translation": "Impossible de sauvegarder l'image de profile. Le fichier ne semble pas être un fichier d'image valide."
"translation": "Impossible de sauvegarder l'image de profil. Le fichier ne semble pas être un fichier d'image valide."
},
{
"id": "api.user.upload_profile_user.encode.app_error",
......@@ -3360,7 +3360,7 @@
},
{
"id": "app.import.validate_post_import_data.create_at_zero.error",
"translation": "La propriété de message CreateAt ne doit pas être 0 si ce champ est défini."
"translation": "La propriété de message CreateAt ne doit pas être 0."
},
{
"id": "app.import.validate_post_import_data.message_length.error",
......@@ -3380,51 +3380,51 @@
},
{
"id": "app.import.validate_reaction_import_data.create_at_before_parent.error",
"translation": "Reaction CreateAt property must be greater than the parent post CreateAt."
"translation": "La propriété de réponse CreateAt doit être plus grande que la valeur de la propriété CreateAt parente."
},
{
"id": "app.import.validate_reaction_import_data.create_at_missing.error",
"translation": "La propriété requise pour un message est manquante : create_at."
"translation": "La propriété requise de réaction est manquante : create_at."
},
{
"id": "app.import.validate_reaction_import_data.create_at_zero.error",
"translation": "La propriété de message CreateAt ne doit pas être 0 si ce champ est défini."
"translation": "La propriété de réaction CreateAt ne doit pas être 0."
},
{
"id": "app.import.validate_reaction_import_data.emoji_name_length.error",
"translation": "La propriété Message du message est plus longue que la longueur maximale autorisée."
"translation": "La propriété de réaction EmojiName est plus longue que la longueur maximale autorisée."
},
{
"id": "app.import.validate_reaction_import_data.emoji_name_missing.error",
"translation": "La propriété requise du message est manquante : User."
"translation": "La propriété requise de réaction est manquante : EmojiName."
},
{
"id": "app.import.validate_reaction_import_data.user_missing.error",
"translation": "La propriété requise du message est manquante : User."
"translation": "La propriété requise de réaction est manquante : User."
},
{
"id": "app.import.validate_reply_import_data.create_at_before_parent.error",
"translation": "Reply CreateAt property must be greater than the parent post CreateAt."
"translation": "La propriété de réponse CreateAt doit être plus grande que la valeur de la propriété CreateAt parente."
},
{
"id": "app.import.validate_reply_import_data.create_at_missing.error",
"translation": "La propriété requise pour un message est manquante : create_at."
"translation": "La propriété requise de réponse est manquante : create_at."
},
{
"id": "app.import.validate_reply_import_data.create_at_zero.error",
"translation": "La propriété de message CreateAt ne doit pas être 0 si ce champ est défini."
"translation": "La propriété de message CreateAt ne doit pas être 0."
},
{
"id": "app.import.validate_reply_import_data.message_length.error",
"translation": "La propriété Message du message est plus longue que la longueur maximale autorisée."
"translation": "La propriété de réponse Message est plus longue que la longueur maximale autorisée."
},
{
"id": "app.import.validate_reply_import_data.message_missing.error",
"translation": "La propriété requise du message est manquante : Message."
"translation": "La propriété requise de réponse est manquante : Message."
},
{
"id": "app.import.validate_reply_import_data.user_missing.error",
"translation": "La propriété requise du message est manquante : User."
"translation": "La propriété requise de réponse est manquante : User."
},
{
"id": "app.import.validate_team_import_data.allowed_domains_length.error",
......@@ -3564,7 +3564,7 @@
},
{
"id": "app.import.validate_user_import_data.profile_image.error",
"translation": "Invalid profile image."
"translation": "Image de profil invalide."
},
{
"id": "app.import.validate_user_import_data.roles_invalid.error",
......@@ -3690,6 +3690,10 @@
"id": "app.plugin.upload_disabled.app_error",
"translation": "Les plugins et/ou l'envoi de plugins ont été désactivés."
},
{
"id": "app.team.join_user_to_team.max_accounts.app_error",
"translation": "Cette équipe a atteint la limite du nombre maximum de comptes autorisés. Contactez votre administrateur système pour augmenter cette limite."
},
{
"id": "app.user_access_token.disabled",
"translation": "Les jetons d'accès personnel sont désactivés sur ce serveur. Veuillez contacter votre administrateur système pour plus d'informations."
......@@ -4644,7 +4648,7 @@
},
{
"id": "model.config.is_valid.atmos_camo_image_proxy_options.app_error",
"translation": "Invalid atmos/camo image proxy options for service settings. Must be set to your shared key."
"translation": "Les paramètres d'options du proxy d'image atmos/camo sont invalides. Votre clé partagée doit être définie comme paramètre."
},
{
"id": "model.config.is_valid.cluster_email_batching.app_error",
......@@ -4758,9 +4762,13 @@
"id": "model.config.is_valid.file_thumb_width.app_error",
"translation": "Largeur des miniatures invalide dans les paramètres de fichiers. Doit être un entier positif."
},
{
"id": "model.config.is_valid.group_unread_channels.app_error",
"translation": "Le paramètre de groupement de canaux non lus est invalide. Doit être défini sur « disabled », « default_on » ou « default_off »."
},
{
"id": "model.config.is_valid.image_proxy_type.app_error",
"translation": "Invalid image proxy type for service settings."
"translation": "Paramètre de type de proxy d'image invalide."
},
{
"id": "model.config.is_valid.ldap_basedn",
......@@ -5404,7 +5412,7 @@
},
{
"id": "model.user.is_valid.position.app_error",
"translation": "Rôle invalide : ne doit pas faire plus de 35 caractères."
"translation": "Rôle invalide : ne doit pas faire plus de 128 caractères."
},
{
"id": "model.user.is_valid.pwd.app_error",
......@@ -6848,7 +6856,7 @@
},
{
"id": "store.sql_user_access_token.get_all.app_error",
"translation": "Impossible de récupérer le jeton d'accès personnel"
"translation": "Impossible de récupérer tous les jetons d'accès personnel"
},
{
"id": "store.sql_user_access_token.get_by_token.app_error",
......@@ -6864,7 +6872,7 @@
},
{
"id": "store.sql_user_access_token.search.app_error",
"translation": "Nous avons rencontré une erreur lors de la recherche du jeton d'accès"
"translation": "Nous avons rencontré une erreur lors de la recherche des jetons d'accès personnel"
},
{
"id": "store.sql_webhooks.analytics_incoming_count.app_error",
......@@ -7290,10 +7298,6 @@
"id": "web.root.singup_title",
"translation": "Inscription"
},
{
"id": "web.signup_team_complete.invalid_link.app_error",
"translation": "Le lien d'inscription ne semble pas être valide"
},
{
"id": "web.signup_team_complete.link_expired.app_error",
"translation": "Le lien d'inscription a expiré"
......@@ -7310,10 +7314,6 @@
"id": "web.signup_user_complete.link_expired.app_error",
"translation": "Le lien d'inscription a expiré"
},
{