feat: change email, export my data, delete account (with data anonymization)

This commit is contained in:
Florian Sylvain committed 2026-10-30 20:19:07 +01:00
1 parent bb191c389a
commit 10a66cd979
123 files changed
+10789 -76

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
import { afterEach, describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import uiPlugin from '@nuxt/ui/vue-plugin'
import { createPinia } from 'pinia'
import router from '@/router'
import BlockedDialog from '@/components/BlockedDialog.vue'
import { setLocale } from '@/i18n'
import { messages, type MessageKey } from '@/i18n/messages'
const KEYS: MessageKey[] = [
'Danger zone',
"Delete your account and your personal data. This can't be undone.",
'Delete my account',
'Delete your account?',
"This permanently deletes your account. It can't be undone.",
'Your username, display name and email address',
'Your author profile and API keys',
"Your sessions: you'll be logged out everywhere",
'Your articles and media stay',
'They stay on the site, credited to "Deleted user". Your name is removed from them.',
'Deleted user',
'Export my data first',
'Current password',
'Type DELETE to confirm',
'Keep my account',
'Deleting…',
"That password isn't correct. Try again.",
'Too many requests. Try again in a few minutes.',
"Couldn't delete your account. Nothing was deleted. Try again in a moment.",
"You can't delete your account yet",
'You are the last administrator. RenewCMS needs at least one. Promote another user first, then come back to delete your account.',
'You are the last Owner. RenewCMS needs at least one. Transfer ownership or promote another user first, then come back to delete your account.',
'Back to profile',
'Go to Users',
"This account can't be deleted",
"RenewCMS can't delete this account right now. Ask an administrator to help, then try again.",
'Your account has been deleted',
'Your articles and media were kept without your name.',
'Deleted the account of {0}',
'Failed to delete the account of {0}',
]
function placeholders(text: string) {
return (text.match(/\{\d+\}/g) ?? []).sort()
}
afterEach(() => setLocale('en'))
describe('account deletion catalogue', () => {
it.each(KEYS)('has a French value with the same placeholders for %s', (key) => {
const french = messages[key]
expect(french.trim()).not.toBe('')
expect(placeholders(french)).toEqual(placeholders(key))
})
it('keeps the confirmation word DELETE literal in the French label', () => {
expect(messages['Type DELETE to confirm']).toContain('DELETE')
})
it('renders the blocked dialog in French and back in English', async () => {
const mountBlocked = () =>
mount(BlockedDialog, {
props: { open: true, lastRole: 'Owner' },
attachTo: document.body,
global: { plugins: [router, uiPlugin, createPinia()] },
})
setLocale('fr')
const french = mountBlocked()
await french.vm.$nextTick()
expect(document.body.textContent).toContain(
'Vous ne pouvez pas encore supprimer votre compte',
)
expect(document.body.textContent).toContain('Retour au profil')
expect(document.body.textContent).not.toContain('Back to profile')
french.unmount()
document.body.innerHTML = ''
setLocale('en')
const english = mountBlocked()
await english.vm.$nextTick()
expect(document.body.textContent).toContain("You can't delete your account yet")
english.unmount()
document.body.innerHTML = ''
})
})
+116
View File
@@ -1212,6 +1212,21 @@ export const messages = {
'Registered the account {0}': 'Compte créé : {0}',
'Verified the account {0}': 'Compte vérifié : {0}',
'Updated the profile of {0}': 'Profil mis à jour : {0}',
'Requested an email change for {0}': 'Changement d’e-mail demandé : {0}',
'Failed to request an email change for {0}': 'Échec de la demande de changement d’e-mail : {0}',
'Resent the email change link for {0}': 'Lien de changement d’e-mail renvoyé : {0}',
'Failed to resend the email change link for {0}':
'Échec du renvoi du lien de changement d’e-mail : {0}',
'Cancelled the email change for {0}': 'Changement d’e-mail annulé : {0}',
'Failed to cancel the email change for {0}':
'Échec de l’annulation du changement d’e-mail : {0}',
'Confirmed the email change for {0}': 'Changement d’e-mail confirmé : {0}',
'Failed to confirm the email change for {0}':
'Échec de la confirmation du changement d’e-mail : {0}',
'Exported the personal data of {0}': 'Données personnelles exportées : {0}',
'Failed to export the personal data of {0}': 'Échec de l’export des données personnelles : {0}',
'Deleted the account of {0}': 'Compte supprimé : {0}',
'Failed to delete the account of {0}': 'Échec de la suppression du compte : {0}',
'Invited {0}': 'Invitation envoyée à {0}',
'Cancelled the invite for {0}': 'Invitation annulée : {0}',
'Renewed the invite for {0}': 'Invitation renouvelée : {0}',
@@ -3303,6 +3318,107 @@ export const messages = {
"You haven't created {0} yet": 'Vous n’avez pas encore créé d’entrée dans {0}',
"Your role shows only entries you created, so other people's {0} aren't listed here. Use New entry to add one.":
'Votre rôle n’affiche que les entrées que vous avez créées, donc les entrées des autres personnes dans {0} ne sont pas listées ici. Utilisez Nouvelle entrée pour en ajouter une.',
'Change email': 'Changer l’e-mail',
'New email address': 'Nouvelle adresse e-mail',
'name@example.com': 'nom@exemple.fr',
'Current password': 'Mot de passe actuel',
"We'll send a verification link to the new address. Your email stays the same until you open it.":
'Nous enverrons un lien de vérification à la nouvelle adresse. Votre e-mail reste inchangé tant que vous n’ouvrez pas le lien.',
'Send verification link': 'Envoyer le lien de vérification',
"That's already your email address.": 'C’est déjà votre adresse e-mail.',
'Enter your current password.': 'Saisissez votre mot de passe actuel.',
"That password isn't correct. Try again.": 'Ce mot de passe n’est pas correct. Réessayez.',
'Too many requests. Try again in a few minutes.':
'Trop de demandes. Réessayez dans quelques minutes.',
'Waiting for verification': 'En attente de vérification',
'We sent a link to {0}. Until you open it, you keep logging in with {1}.':
'Nous avons envoyé un lien à {0}. Tant que vous ne l’ouvrez pas, vous continuez à vous connecter avec {1}.',
'Resend link': 'Renvoyer le lien',
'Resend in {0}': 'Renvoyer dans {0}',
'Cancel change': 'Annuler le changement',
'Verification link sent': 'Lien de vérification envoyé',
'Check {0}. The link works once, within 2 hours.':
'Consultez {0}. Le lien ne fonctionne qu’une fois, dans les 2 heures.',
'Link sent again': 'Lien renvoyé',
'The previous link no longer works.': 'Le lien précédent ne fonctionne plus.',
'Email change cancelled': 'Changement d’e-mail annulé',
'You keep logging in with {0}.': 'Vous continuez à vous connecter avec {0}.',
'Confirming your new email…': 'Confirmation de votre nouvelle adresse e-mail…',
'Your email address has changed': 'Votre adresse e-mail a changé',
'Your account now uses {0}. Use it the next time you log in.':
'Votre compte utilise maintenant {0}. Utilisez-la lors de votre prochaine connexion.',
'Go to profile': 'Aller au profil',
'Email address changed': 'Adresse e-mail modifiée',
'Your account now uses {0}.': 'Votre compte utilise maintenant {0}.',
"This link doesn't work": 'Ce lien ne fonctionne pas',
'It may have expired (links last 2 hours), been cancelled or already been used.':
'Il a peut-être expiré (les liens durent 2 heures), été annulé ou déjà été utilisé.',
'Request a new link from your profile.': 'Demandez un nouveau lien depuis votre profil.',
"We couldn't change your email address": 'Nous n’avons pas pu changer votre adresse e-mail',
"This address can't be used for your account. Go back to your profile and try a different one.":
'Cette adresse ne peut pas être utilisée pour votre compte. Retournez à votre profil et essayez-en une autre.',
'Log in to confirm your new email':
'Connectez-vous pour confirmer votre nouvelle adresse e-mail',
"You opened this link somewhere you're not logged in. Log in here and we'll confirm the change right after.":
'Vous avez ouvert ce lien là où vous n’êtes pas connecté. Connectez-vous ici et nous confirmerons le changement juste après.',
'Log in and confirm': 'Se connecter et confirmer',
'The confirmation code is missing. Open the link from your email again, or copy the full address into your browser.':
'Le code de confirmation manque. Rouvrez le lien de votre e-mail ou copiez l’adresse complète dans votre navigateur.',
"Couldn't confirm the change": 'Impossible de confirmer le changement',
'Your data': 'Vos données',
'Download a copy of the personal data RenewCMS holds about you, as JSON files in a ZIP.':
'Téléchargez une copie des données personnelles que RenewCMS conserve à votre sujet, sous forme de fichiers JSON dans un ZIP.',
'Username, display name, email, role and author profile.':
'Nom d’utilisateur, nom d’affichage, e-mail, rôle et profil d’auteur.',
'Authored content': 'Contenus rédigés',
'Your articles and entries.': 'Vos articles et vos entrées.',
'Uploaded media': 'Médias téléversés',
'References to the files you uploaded.': 'Références des fichiers que vous avez téléversés.',
'Account activity': 'Activité du compte',
'Your log-ins and the actions recorded under your name.':
'Vos connexions et les actions enregistrées à votre nom.',
'Download my data': 'Télécharger mes données',
'Ready in a few seconds.': 'Prêt en quelques secondes.',
'{0} is in your downloads.': '{0} se trouve dans vos téléchargements.',
'You can export once every 15 minutes': 'Vous pouvez exporter une fois toutes les 15 minutes',
'Your last export was at {0}. Try again in {1} minutes.':
'Votre dernier export date de {0}. Réessayez dans {1} minutes.',
'Your last export was at {0}. Try again in 1 minute.':
'Votre dernier export date de {0}. Réessayez dans 1 minute.',
'Available in {0}': 'Disponible dans {0}',
"Couldn't prepare your data": 'Impossible de préparer vos données',
'Danger zone': 'Zone de danger',
"Delete your account and your personal data. This can't be undone.":
'Supprimez votre compte et vos données personnelles. Cette action est irréversible.',
'Delete my account': 'Supprimer mon compte',
'Delete your account?': 'Supprimer votre compte ?',
"This permanently deletes your account. It can't be undone.":
'Cette action supprime définitivement votre compte. Elle est irréversible.',
'Your username, display name and email address':
'Votre nom d’utilisateur, votre nom d’affichage et votre adresse e-mail',
'Your author profile and API keys': 'Votre profil d’auteur et vos clés d’API',
"Your sessions: you'll be logged out everywhere":
'Vos sessions : vous serez déconnecté partout',
'Your articles and media stay': 'Vos articles et vos médias restent',
'They stay on the site, credited to "Deleted user". Your name is removed from them.':
'Ils restent sur le site, attribués à « Utilisateur supprimé ». Votre nom en est retiré.',
'Deleted user': 'Utilisateur supprimé',
'Export my data first': 'Exporter mes données d’abord',
'Type DELETE to confirm': 'Saisissez DELETE pour confirmer',
"Couldn't delete your account. Nothing was deleted. Try again in a moment.":
'Impossible de supprimer votre compte. Rien n’a été supprimé. Réessayez dans un instant.',
"You can't delete your account yet": 'Vous ne pouvez pas encore supprimer votre compte',
'You are the last administrator. RenewCMS needs at least one. Promote another user first, then come back to delete your account.':
'Vous êtes le dernier administrateur. RenewCMS en a besoin d’au moins un. Promouvez d’abord un autre utilisateur, puis revenez supprimer votre compte.',
'You are the last Owner. RenewCMS needs at least one. Transfer ownership or promote another user first, then come back to delete your account.':
'Vous êtes le dernier propriétaire. RenewCMS en a besoin d’au moins un. Transférez d’abord la propriété ou promouvez un autre utilisateur, puis revenez supprimer votre compte.',
'Back to profile': 'Retour au profil',
"This account can't be deleted": 'Ce compte ne peut pas être supprimé',
"RenewCMS can't delete this account right now. Ask an administrator to help, then try again.":
'RenewCMS ne peut pas supprimer ce compte pour le moment. Demandez de l’aide à un administrateur, puis réessayez.',
'Your account has been deleted': 'Votre compte a été supprimé',
'Your articles and media were kept without your name.':
'Vos articles et vos médias ont été conservés sans votre nom.',
} as const
export type MessageKey = keyof typeof messages