diff --git a/lang/en.yml b/lang/en.yml
index 6a44c5e96..c4155571b 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -5,7 +5,7 @@ en:
EDITINFO: 'Edit this file'
REMOVE: Remove
SilverStripe\Control\ChangePasswordEmail_ss:
- CHANGEPASSWORDFOREMAIL: 'The password for account with email address {email} has been changed. If you didn''t change your password please change your password using the link below'
+ CHANGEPASSWORDFOREMAIL: 'The password for account with email address {email} has been changed. If you didn\''t change your password please change your password using the link below'
CHANGEPASSWORDTEXT1: 'You changed your password for'
CHANGEPASSWORDTEXT3: 'Change password'
HELLO: Hi
@@ -143,12 +143,12 @@ en:
SilverStripe\Forms\TimeField:
VALIDATEFORMAT: 'Please enter a valid time format ({format})'
SilverStripe\ORM\DataObject:
+ GENERALSEARCH: 'General Search'
PLURALNAME: 'Data Objects'
PLURALS:
one: 'A Data Object'
other: '{count} Data Objects'
SINGULARNAME: 'Data Object'
- GENERALSEARCH: 'General Search'
SilverStripe\ORM\FieldType\DBBoolean:
ANY: Any
NOANSWER: 'No'
diff --git a/lang/eo.yml b/lang/eo.yml
index f68c5ae5f..0776ee3e0 100644
--- a/lang/eo.yml
+++ b/lang/eo.yml
@@ -236,6 +236,7 @@ eo:
Sort: 'Ordiga Ordo'
has_many_Permissions: Permesoj
many_many_Members: Membroj
+ ValidationIdentifierAlreadyExists: 'Jam ekzistas grupo ({group}) kun la sama {identifier}'
SilverStripe\Security\LoginAttempt:
Email: 'Retpoŝta adreso'
EmailHashed: 'Retpoŝta adreso (haketa)'
diff --git a/lang/es.yml b/lang/es.yml
index 4e2e4b510..5adee4a6f 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -78,6 +78,7 @@ es:
PLURALS:
one: 'Un Objeto de Dato'
other: '{count} Objetos de Datos'
+ many: '{count} Objetos de Datos'
SINGULARNAME: 'Objeto de Dato'
SilverStripe\ORM\FieldType\DBBoolean:
ANY: Cualquiera
@@ -87,24 +88,30 @@ es:
DAYS_SHORT_PLURALS:
one: '{count} día'
other: '{count} días'
+ many: '{count} días'
HOURS_SHORT_PLURALS:
one: '{count} hora'
other: '{count} horas'
+ many: '{count} horas'
LessThanMinuteAgo: 'Hace menos de un minuto'
MINUTES_SHORT_PLURALS:
one: '{count} minuto'
other: '{count} minutos'
+ many: '{count} minutos'
MONTHS_SHORT_PLURALS:
one: '{count} mes'
other: '{count} meses'
+ many: '{count} meses'
SECONDS_SHORT_PLURALS:
one: '{count} segundo'
other: '{count} segundos'
+ many: '{count} segundos'
TIMEDIFFAGO: 'hace {difference}'
TIMEDIFFIN: 'en {difference}'
YEARS_SHORT_PLURALS:
one: '{count} año'
other: '{count} años'
+ many: '{count} años'
SilverStripe\ORM\FieldType\DBEnum:
ANY: Cualquiera
SilverStripe\ORM\Hierarchy\Hierarchy:
@@ -135,6 +142,7 @@ es:
PLURALS:
one: 'Un Grupo'
other: '{count} Grupos'
+ many: '{count} Grupos'
Parent: 'Grupo Padre'
RolesAddEditLink: 'Gestionar Roles'
SINGULARNAME: Grupo
@@ -147,6 +155,7 @@ es:
PLURALS:
one: 'Un intento de inicio de sesión'
other: '{count} Intentos de inicios de sesión'
+ many: '{count} Intentos de inicios de sesión'
SINGULARNAME: 'Intento de ingreso'
Status: Estado
SilverStripe\Security\Member:
@@ -176,6 +185,7 @@ es:
PLURALS:
one: 'Un Miembro'
other: '{count} Miembros'
+ many: '{count} Miembros'
SINGULARNAME: Miembro
SUBJECTPASSWORDCHANGED: 'Su contraseña ha sido cambiada'
SUBJECTPASSWORDRESET: 'Enlace para restaurar su contraseña'
@@ -192,6 +202,7 @@ es:
PLURALS:
one: 'Una Contraseña de Miembro'
other: '{count} Contraseñas de Miembros'
+ many: '{count} Contraseñas de Miembros'
SINGULARNAME: 'Contraseña del Miembro'
SilverStripe\Security\PasswordValidator:
LOWCHARSTRENGTH: 'Necesitas aumentar la complejidad de tu contraseña agregando los siguientes caracteres: {chars}'
@@ -206,6 +217,7 @@ es:
PLURALS:
one: 'Un Permiso'
other: '{count} Permisos'
+ many: '{count} Permisos'
SINGULARNAME: Permiso
UserPermissionsIntro: 'Asignar grupos a este usuario ajustará los permisos que tienen. Vea la sección de grupos para obtener información sobre permisos en grupos individuales.'
SilverStripe\Security\PermissionCheckboxSetField:
@@ -219,6 +231,7 @@ es:
PLURALS:
one: 'Un Rol'
other: '{count} Roles'
+ many: '{count} Roles'
SINGULARNAME: Rol
Title: Título
SilverStripe\Security\PermissionRoleCode:
@@ -226,6 +239,7 @@ es:
PLURALS:
one: 'Un Código de Rol de Permiso'
other: '{count} Códigos de Roles de Permiso'
+ many: '{count} Códigos de Roles de Permiso'
PermsError: 'No se puede asignar permisos privilegiados al código "% s" (requiere acceso de administrador)'
SINGULARNAME: 'Código del Rol de Permiso'
SilverStripe\Security\RememberLoginHash:
@@ -233,6 +247,7 @@ es:
PLURALS:
one: 'Un Hash de inicio de sesión'
other: '{count} Hashes de inicio de sesión'
+ many: '{count} Hashes de inicio de sesión'
SINGULARNAME: 'Hash de inicio de sesión'
SilverStripe\Security\Security:
ALREADYLOGGEDIN: 'No tiene acceso a esta página. Si posee otra cuenta con los privilegios para acceder a esta página, puede iniciar sesión a continuación.'
diff --git a/lang/fr.yml b/lang/fr.yml
index 8a87ed38f..d66464adc 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -113,6 +113,7 @@ fr:
PLURALS:
one: 'Un modèle de donnée'
other: '{count} modèles de donnée'
+ many: '{count} modèles de donnée'
SINGULARNAME: 'Modèle de donnée'
SilverStripe\ORM\FieldType\DBBoolean:
ANY: Tout
@@ -122,24 +123,30 @@ fr:
DAYS_SHORT_PLURALS:
one: '{count} jour'
other: '{count} jours'
+ many: '{count} jours'
HOURS_SHORT_PLURALS:
one: '{count} heure'
other: '{count} heures'
+ many: '{count} heures'
LessThanMinuteAgo: 'moins d''une minute'
MINUTES_SHORT_PLURALS:
one: '{count} min.'
other: '{count} min.'
+ many: '{count} min.'
MONTHS_SHORT_PLURALS:
one: '{count} mois'
other: '{count} mois'
+ many: '{count} mois'
SECONDS_SHORT_PLURALS:
one: '{count} sec.'
other: '{count} sec.'
+ many: '{count} sec.'
TIMEDIFFAGO: 'Il y a {difference}'
TIMEDIFFIN: 'Dans {difference}'
YEARS_SHORT_PLURALS:
one: '{count} année'
other: '{count} années'
+ many: '{count} années'
SilverStripe\ORM\FieldType\DBEnum:
ANY: Tout
SilverStripe\ORM\Hierarchy:
@@ -182,6 +189,7 @@ fr:
PLURALS:
one: 'Un groupe'
other: '{count} groupes'
+ many: '{count} groupes'
Parent: 'Groupe parent'
ROLES: Rôles
ROLESDESCRIPTION: 'Les rôles sont des ensembles de permissions prédéfinies, et ils peuvent être attribués à des groupes.
Si nécessaire, ils peuvent hériter de groupes parents.'
@@ -198,6 +206,7 @@ fr:
PLURALS:
one: 'Une tentative de connexion'
other: '{count} tentatives de connexion'
+ many: '{count} tentatives de connexion'
SINGULARNAME: 'Tentative de connexion'
Status: Statut
SilverStripe\Security\Member:
@@ -228,6 +237,7 @@ fr:
PLURALS:
one: 'Un membre'
other: '{count} membres'
+ many: '{count} membres'
SINGULARNAME: Membre
SUBJECTPASSWORDCHANGED: 'Votre mot de passe a été changé'
SUBJECTPASSWORDRESET: 'Lien pour modifier votre mot de passe'
@@ -255,6 +265,7 @@ fr:
PLURALS:
one: 'Un mot de passe utilisateur'
other: '{count} mots de passe utilisateur'
+ many: '{count} mots de passe utilisateur'
SINGULARNAME: 'Mot de passe utilisateur'
SilverStripe\Security\PasswordValidator:
LOWCHARSTRENGTH: 'Veuillez renforcer votre mot de passe en ajoutant certains des caractères suivants : {chars}'
@@ -271,6 +282,7 @@ fr:
PLURALS:
one: 'Une autorisation'
other: '{count} autorisations'
+ many: '{count} autorisations'
SINGULARNAME: Permission
UserPermissionsIntro: "Assigner des groupes à cet utilisateur modifiera les autorisations dont il dispose. Consultez la section «\_Groupes\_» pour plus de détails sur les autorisations associées à chaque groupe."
SilverStripe\Security\PermissionCheckboxSetField:
@@ -284,6 +296,7 @@ fr:
PLURALS:
one: 'Un rôle'
other: '{count} rôles'
+ many: '{count} rôles'
SINGULARNAME: Rôle
Title: Titre
SilverStripe\Security\PermissionRoleCode:
@@ -291,6 +304,7 @@ fr:
PLURALS:
one: 'Un code d''autorisation lié au rôle'
other: '{count} codes d''autorisation liés au rôle'
+ many: '{count} codes d''autorisation liés au rôle'
PermsError: 'Impossible d''attribuer le code "{code}" (requiert un accès en tant qu''administrateur)'
SINGULARNAME: 'Code d''autorisation lié au rôle'
SilverStripe\Security\RememberLoginHash:
@@ -298,6 +312,7 @@ fr:
PLURALS:
one: 'Une signature de mot de passe'
other: '{count} signatures de mot de passe'
+ many: '{count} signatures de mot de passe'
SINGULARNAME: 'Signature de mot de passe'
SilverStripe\Security\Security:
ALREADYLOGGEDIN: 'Vous n''avez pas accès à cette page. Si un autre de vos identifiants vous permet d''accéder à cette page, merci de vous reconnecter ci-dessous en l''utilisant.'
diff --git a/lang/it.yml b/lang/it.yml
index 111d04835..75d5ee68b 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -149,6 +149,7 @@ it:
PLURALS:
one: 'Un Data Object'
other: '{count} Data Object'
+ many: '{count} Data Object'
SINGULARNAME: 'Data Object'
SilverStripe\ORM\FieldType\DBBoolean:
ANY: Qualsiasi
@@ -158,24 +159,30 @@ it:
DAYS_SHORT_PLURALS:
one: '{count} giorno'
other: '{count} giorni'
+ many: '{count} giorni'
HOURS_SHORT_PLURALS:
one: '{count} ora'
other: '{count} ore'
+ many: '{count} ore'
LessThanMinuteAgo: 'meno di un minuto'
MINUTES_SHORT_PLURALS:
one: '{count} minuto'
other: '{count} minuti'
+ many: '{count} minuti'
MONTHS_SHORT_PLURALS:
one: '{count} mese'
other: '{count} mesi'
+ many: '{count} mesi'
SECONDS_SHORT_PLURALS:
one: '{count} secondo'
other: '{count} secondi'
+ many: '{count} secondi'
TIMEDIFFAGO: '{difference} fa'
TIMEDIFFIN: 'in {difference}'
YEARS_SHORT_PLURALS:
one: '{count} anno'
other: '{count} anni'
+ many: '{count} anni'
SilverStripe\ORM\FieldType\DBEnum:
ANY: Qualsiasi
SilverStripe\ORM\FieldType\DBForeignKey:
@@ -228,6 +235,7 @@ it:
PLURALS:
one: 'Un Gruppo'
other: '{count} Gruppi'
+ many: '{count} Gruppi'
Parent: 'Gruppo padre'
ROLES: Ruoli
ROLESDESCRIPTION: 'I ruoli sono insiemi predefiniti di permessi che possono essere assegnati ai gruppi.
Se richiesto possono essere ereditati dai gruppi padre.'
@@ -236,6 +244,7 @@ it:
Sort: 'Tipo ordinamento'
has_many_Permissions: Permessi
many_many_Members: Membri
+ ValidationIdentifierAlreadyExists: 'Esiste già un gruppo ({group}) con lo stesso {identifier}'
SilverStripe\Security\LoginAttempt:
Email: 'Indirizzo e-mail'
EmailHashed: 'Indirizzo email (hash)'
@@ -244,6 +253,7 @@ it:
PLURALS:
one: 'Un tentativo d''accesso'
other: '{count} tentativi d''accesso'
+ many: '{count} tentativi d''accesso'
SINGULARNAME: 'Tentativo d''accesso'
Status: Stato
SilverStripe\Security\Member:
@@ -276,6 +286,7 @@ it:
PLURALS:
one: 'Un Utente'
other: '{count} Utenti'
+ many: '{count} Utenti'
SINGULARNAME: Utente
SUBJECTPASSWORDCHANGED: 'La tua password è stata cambiata'
SUBJECTPASSWORDRESET: 'Link per azzerare la tua password'
@@ -305,6 +316,7 @@ it:
PLURALS:
one: 'Una password utente'
other: '{count} password utente'
+ many: '{count} password utente'
SINGULARNAME: 'Password utente'
SilverStripe\Security\PasswordValidator:
LOWCHARSTRENGTH: 'Perfavore aumenta la sicurezza della password aggiungendo alcuni dei seguenti caratteri: {chars}'
@@ -321,6 +333,7 @@ it:
PLURALS:
one: 'Un Permesso'
other: '{count} Permessi'
+ many: '{count} Permessi'
SINGULARNAME: Permesso
UserPermissionsIntro: 'Assegnando gruppi a questo utente modificherà i suoi permessi. Vedi la sezione gruppi per dettagli sui permessi dei singoli gruppi.'
SilverStripe\Security\PermissionCheckboxSetField:
@@ -334,6 +347,7 @@ it:
PLURALS:
one: 'Un Ruolo'
other: '{count} Ruoli'
+ many: '{count} Ruoli'
SINGULARNAME: Ruolo
Title: Titolo
SilverStripe\Security\PermissionRoleCode:
@@ -341,6 +355,7 @@ it:
PLURALS:
one: 'Un codice di ruolo'
other: '{count} codici di ruolo'
+ many: '{count} codici di ruolo'
PermsError: 'Non posso assegnare permessi privilegiati al codice "{code}" (richiede accesso ADMIN)'
SINGULARNAME: 'Codice di ruolo'
SilverStripe\Security\RememberLoginHash:
@@ -348,6 +363,7 @@ it:
PLURALS:
one: 'Un Hash di Login'
other: '{count} Hash di Login'
+ many: '{count} Hash di Login'
SINGULARNAME: 'Hash di Login'
SilverStripe\Security\Security:
ALREADYLOGGEDIN: 'Non hai accesso a questa pagina. Se hai un altro account che può accederci, puoi autenticarti qui sotto.'
diff --git a/lang/nl.yml b/lang/nl.yml
index 31d6452c1..9d7104cb5 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -7,15 +7,15 @@ nl:
EDITINFO: 'Bewerk dit bestand'
REMOVE: Verwijder
SilverStripe\Control\ChangePasswordEmail_ss:
- CHANGEPASSWORDFOREMAIL: 'Het wachtwoord voor het account met e-mailadres {email} is aangepast. Indien u uw wachtwoord niet heeft aangepast kunt u dat doen met onderstaande link.'
+ CHANGEPASSWORDFOREMAIL: 'Het wachtwoord voor het account met e-mailadres {email} is zojuist aangepast. Heb je geen wachtwoord aangepast, gebruik van onderstaande knop om uit veiligheidsoverwegingen je wachtwoord opnieuw in te stellen.'
CHANGEPASSWORDTEXT1: 'U heeft het wachtwoord veranderd voor'
- CHANGEPASSWORDTEXT3: 'Wachtwoord veranderen'
+ CHANGEPASSWORDTEXT3: 'Wachtwoord opnieuw instellen'
HELLO: Hallo
SilverStripe\Control\Email\ForgotPasswordEmail_ss:
HELLO: Hallo
- TEXT1: 'Hier is uw'
- TEXT2: 'link om uw wachtwoord opnieuw aan te maken'
- TEXT3: voor
+ TEXT1: 'Je hebt aangegeven je wachtwoord te zijn vergeten. Klik op de'
+ TEXT2: 'onderstaande link'
+ TEXT3: 'om je wachtwoord opnieuw in te stellen voor '
SilverStripe\Control\Middleware\ConfirmationMiddleware\GetParameter:
CONFIRMATION_NAME: '"{key}" GET parameter'
SilverStripe\Control\Middleware\ConfirmationMiddleware\Url:
@@ -39,8 +39,8 @@ nl:
ATLEAST: 'Een wachtwoord moet tenminste {min} karakters hebben.'
BETWEEN: 'Een wachtwoord moet tussen de {min} en {max} karakters hebben'
CURRENT_PASSWORD_ERROR: 'Het wachtwoord dat u heeft ingevoerd is niet juist.'
- CURRENT_PASSWORD_MISSING: 'Voer uw huidige wachtwoord in.'
- LOGGED_IN_ERROR: 'U moet ingelogd zijn om uw wachtwoord te kunnen veranderen!'
+ CURRENT_PASSWORD_MISSING: 'Voer je huidige wachtwoord in.'
+ LOGGED_IN_ERROR: 'Je moet ingelogd zijn om je wachtwoord te kunnen veranderen!'
MAXIMUM: 'Een wachtwoord mag maximaal {max} karakters hebben.'
SHOWONCLICKTITLE: 'Verander wachtwoord'
SilverStripe\Forms\DateField:
@@ -63,7 +63,7 @@ nl:
FIELDNOTSET: 'Bestandsinformatie niet gevonden'
SilverStripe\Forms\Form:
BAD_METHOD: 'Dit formulier moet middels {method} verzonden worden'
- CSRF_EXPIRED_MESSAGE: 'Uw sessie is verlopen. Verzend het formulier opnieuw.'
+ CSRF_EXPIRED_MESSAGE: 'De sessie is verlopen. Verzend het formulier opnieuw.'
CSRF_FAILED_MESSAGE: 'Er lijkt een technisch probleem te zijn. Klik op de knop terug, vernieuw uw browser, en probeer het opnieuw.'
FIELDISREQUIRED: '{name} is verplicht'
VALIDATIONPASSWORDSDONTMATCH: 'Wachtwoorden komen niet overeen'
@@ -194,7 +194,7 @@ nl:
ERRORNOTADMIN: 'Die gebruiker is geen beheerder.'
ERRORNOTREC: 'De gebruikersnaam en/of het wachtwoord wordt niet herkend'
SilverStripe\Security\CMSMemberLoginForm:
- PASSWORDEXPIRED: '
Uw wachtwoord is verlopen. Kies een nieuw wachtwoord.
' + PASSWORDEXPIRED: 'Je wachtwoord is verlopen. Kies een nieuw wachtwoord.' SilverStripe\Security\CMSSecurity: INVALIDUSER: 'Ongeldige gebruiker Log hier opnieuw in om verder te gaan.
' LOGIN_MESSAGE: 'De browsersessie is verlopen wegens inactiviteit
' @@ -236,6 +236,7 @@ nl: Sort: Sorteer-richting has_many_Permissions: Rechten many_many_Members: Leden + ValidationIdentifierAlreadyExists: 'Er bestaat al een groep ({group}) met eenzelfde {identifier}' SilverStripe\Security\LoginAttempt: Email: 'E-mailadres ' EmailHashed: 'E-mailadres (versleuteld)' @@ -259,7 +260,7 @@ nl: EDIT_PASSWORD: 'Nieuw wachtwoord' EMAIL: E-mail EMPTYNEWPASSWORD: 'Het nieuwe wachtwoord mag niet leeg zijn, probeer opnieuw' - ENTEREMAIL: 'Typ uw e-mailadres om een link te ontvangen waarmee u uw wachtwoord kunt resetten.' + ENTEREMAIL: 'Typ je e-mailadres om een link te ontvangen, waarmee je je wachtwoord opnieuw kan instellen.' ERRORLOCKEDOUT2: 'Uw account is tijdelijk uitgeschakeld als gevolg van te veel mislukte pogingen om in te loggen. Probeer het over {count} minuten opnieuw.' ERRORNEWPASSWORD: 'De nieuwe wachtwoorden komen niet overeen, probeer het nogmaals' ERRORPASSWORDNOTMATCH: 'Uw huidige wachtwoord is niet correct, probeer het nogmaals' @@ -269,20 +270,20 @@ nl: LOGGEDINAS: 'U bent ingelogd als {name}.' NEWPASSWORD: 'Nieuw wachtwoord' PASSWORD: Wachtwoord - PASSWORDEXPIRED: 'Uw wachtwoord is verlopen. Kies een nieuw wachtwoord.' + PASSWORDEXPIRED: 'Je wachtwoord is verlopen. Kies een nieuw wachtwoord.' PLURALNAME: Leden PLURALS: one: 'Een lid' other: '{count} leden' SINGULARNAME: Lid - SUBJECTPASSWORDCHANGED: 'Uw wachtwoord is veranderd' - SUBJECTPASSWORDRESET: 'Link om uw wachtwoord opnieuw aan te maken' + SUBJECTPASSWORDCHANGED: 'Je wachtwoord is aangepast' + SUBJECTPASSWORDRESET: 'Je wachtwoord opnieuw instellen' SURNAME: Achternaam - VALIDATIONADMINLOSTACCESS: 'Niet mogelijk om alle admin-groepen te verwijderen van uw profiel' - VALIDATIONMEMBEREXISTS: 'Er bestaat al een gebruiker met eenzelfde %s' + VALIDATIONADMINLOSTACCESS: 'Niet mogelijk om alle admin-groepen te verwijderen van je profiel' + VALIDATIONMEMBEREXISTS: 'Er bestaat al een gebruiker met dezelfde {identifier}' ValidationIdentifierFailed: 'Een bestaande gebruiker #{id} kan niet dezelfde unieke velden hebben ({name} = {value}))' WELCOMEBACK: 'Welkom terug, {firstname}' - YOUROLDPASSWORD: 'Uw oude wachtwoord' + YOUROLDPASSWORD: 'Je oude wachtwoord' belongs_many_many_Groups: Groepen db_Locale: 'Interface taal' db_LockedOutUntil: 'Gesloten tot' @@ -307,8 +308,8 @@ nl: other: '{count} Gebruikerswachtwoorden' SINGULARNAME: Gebruikerswachtwoord SilverStripe\Security\PasswordValidator: - LOWCHARSTRENGTH: 'Maak a.u.b. uw wachtwoord sterker door enkele van de volgende karakters te gebruiken: {chars}' - PREVPASSWORD: 'U heeft dit wachtwoord in het verleden al gebruikt, kies a.u.b. een nieuw wachtwoord.' + LOWCHARSTRENGTH: 'Maak a.u.b. het wachtwoord sterker door enkele van de volgende karakters te gebruiken: {chars}' + PREVPASSWORD: 'Dit wachtwoord is in het verleden al gebruikt, kies a.u.b. een nieuw wachtwoord.' TOOSHORT: 'Het wachtwoord is te kort, het moet minimaal {minimum} karakters hebben' SilverStripe\Security\Permission: AdminGroup: Beheerder @@ -352,18 +353,18 @@ nl: SilverStripe\Security\Security: ALREADYLOGGEDIN: 'U hebt geen toegang tot deze pagina. Als u een andere account met de nodige rechten hebt, kan u hieronder opnieuw inloggen.' BUTTONSEND: 'Nieuw wachtwoord aanmaken' - CHANGEPASSWORDBELOW: 'U kunt uw wachtwoord hieronder veranderen.' - CHANGEPASSWORDHEADER: 'Verander uw wachtwoord' + CHANGEPASSWORDBELOW: 'Je kan hieronder het wachtwoord veranderen.' + CHANGEPASSWORDHEADER: 'Wachtwoord veranderen' CONFIRMLOGOUT: 'Klik op onderstaande knop om uit te loggen.' ENTERNEWPASSWORD: 'Voer een nieuw wachtwoord in.' - ERRORPASSWORDPERMISSION: 'U moet ingelogd zijn om uw wachtwoord te kunnen veranderen!' + ERRORPASSWORDPERMISSION: 'Je moet ingelogd zijn om je wachtwoord te kunnen veranderen!' LOGIN: Inloggen LOGOUT: Uitloggen LOSTPASSWORDHEADER: 'Wachtwoord vergeten' NOTEPAGESECURED: 'Deze pagina is beveiligd. Voer uw gegevens in en u wordt automatisch doorgestuurd.' NOTERESETLINKINVALID: 'De reset link is ongeldig of verlopen.
Je kan hier een nieuwe link aanvragen of het wachtwoord veranderen nadat je bent ingelogd.
' - NOTERESETPASSWORD: 'Voer uw e-mailadres in en we sturen een link waarmee u een nieuw wachtwoord kunt instellen.' + NOTERESETPASSWORD: 'Voer je e-mailadres in en we sturen een link waarmee je een nieuw wachtwoord kunt instellen.' PASSWORDRESETSENTHEADER: Verzonden - PASSWORDRESETSENTTEXT: 'Bedankt! Er is een link verstuurd om uw wachtwoord opnieuw in te stellen (mits het e-mailadres reeds bekend is bij ons).' + PASSWORDRESETSENTTEXT: 'Bedankt! Er is een e-mail verstuurd om je wachtwoord opnieuw in te stellen - mits het e-mailadres reeds bekend is bij ons.' SilverStripe\View\Shortcodes\EmbedShortcodeProvider: INVALID_URL: 'Het inladen van de media-bestanden is mislukt.' diff --git a/lang/pl.yml b/lang/pl.yml index 219f2274d..4e0bcec5d 100644 --- a/lang/pl.yml +++ b/lang/pl.yml @@ -62,6 +62,7 @@ pl: VALIDATIONSTRONGPASSWORD: 'Hasła muszą mieć przynajmniej jedną cyfrę oraz jeden znak alfanumeryczny.' VALIDATOR: Walidator VALIDCURRENCY: 'Proszę podaj prawidłową walutę' + FIELDISREQUIRED: 'Pole {name} jest wymagane' SilverStripe\Forms\FormField: EXAMPLE: 'na przykład {format}' NONE: brak @@ -97,10 +98,13 @@ pl: DeletePermissionsFailure: 'Brak uprawnień do usuwania' Deleted: 'Usunięto {type} {name}' Save: Zapisz + Saved: 'Zapisane {name} {link}' SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest: NEW: Dodaj NEXT: 'Przejdź do następnego rekordu' PREVIOUS: 'Przejdź do poprzedniego rekordu' + EditPermissionsFailure: 'Wygląda na to, że nie masz wystarczających uprawnień, aby zmienić {ObjectTitle}' + ViewPermissionsFailure: 'Wygląda na to, że nie masz wystarczających uprawnień, aby zobaczyć {ObjectTitle}' SilverStripe\Forms\GridField\GridFieldEditButton: EDIT: Zmień SilverStripe\Forms\GridField\GridFieldFilterHeader: @@ -232,6 +236,7 @@ pl: Sort: 'Kolejność Sortowania' has_many_Permissions: Zezwolenia many_many_Members: Użytkownicy + ValidationIdentifierAlreadyExists: 'Grupa ({group}) już istnieje z tym samym {identifier}' SilverStripe\Security\LoginAttempt: Email: 'Adres e-mail' EmailHashed: 'Adres e-mail (hashed)' @@ -286,6 +291,10 @@ pl: db_LockedOutUntil: 'Zablokowany do' db_Password: Hasło db_PasswordExpiry: 'Data wygaśnięcia hasła' + KEEP_ME_SIGNED_IN: 'Zapamiętaj mnie / Nie wylogowuj mnie przez {count} dni' + KEEP_ME_SIGNED_IN_TOOLTIP: 'Pozostaniesz uwierzytelniony na tym urządzeniu przez {count} dni. Używaj tej funkcji tylko wtedy, gdy ufasz urządzeniu, z którego korzystasz.' + VALIDATIONMEMBEREXISTS: 'Użytkownik z tym adresem już istnieje {identifier}' + WELCOMEBACK: 'Witaj ponownie, {firstname}' SilverStripe\Security\MemberAuthenticator\CMSMemberLoginForm: AUTHENTICATORNAME: 'Formularz logowania użytkownika CMS' BUTTONFORGOTPASSWORD: 'Zapomniałeś hasła?' @@ -370,3 +379,22 @@ pl: NOTERESETPASSWORD: 'Wpisz adres e-mail, na który mamy wysłać link gdzie możesz zresetować swoje hasło' PASSWORDRESETSENTHEADER: 'Link resetowania hasła wysłany' PASSWORDRESETSENTTEXT: 'Dziękujemy! Link resetujący hasło został wysłany do ''{email}'', o ile konto użytkownika dla takiego e-maila istnieje.' + NOTERESETLINKINVALID: 'Link resetujący hasło wygasł lub jest nieprawidłowy.
Możesz poprosić o nowy tutaj lub zmień swoje hasło po zalogowaniu się.
' + SilverStripe\Control\Middleware\ConfirmationMiddleware\GetParameter: + CONFIRMATION_NAME: 'Parametr GET „{key}”' + SilverStripe\Control\Middleware\ConfirmationMiddleware\Url: + CONFIRMATION_DESCRIPTION: 'Adres URL to: „{url}”' + CONFIRMATION_NAME: 'Adres URL jest chroniony' + SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith: + CONFIRMATION_DESCRIPTION: 'Pełny adres URL to: „{url}”' + CONFIRMATION_NAME: 'URL zaczyna się od „{path}”' + SilverStripe\Forms\HTMLEditor\TinyMCEConfig: + BEST_FIT: 'Dopasuj optymalnie' + ORIGINAL: Oryginał + PIXEL_WIDTH: '{width} pikseli' + SilverStripe\Forms\TextField: + VALIDATEMAXLENGTH: 'Wartość {name} nie może przekraczać {maxLength} znaków długości' + SilverStripe\ORM\FieldType\DBString: + ELLIPSIS: ... + SilverStripe\View\Shortcodes\EmbedShortcodeProvider: + INVALID_URL: 'Podczas ładowania pliku wystąpił problem.' diff --git a/src/Control/HTTPResponse.php b/src/Control/HTTPResponse.php index b4fd1e638..d209257ec 100644 --- a/src/Control/HTTPResponse.php +++ b/src/Control/HTTPResponse.php @@ -267,7 +267,7 @@ class HTTPResponse public function addHeader($header, $value) { $header = strtolower($header ?? ''); - $this->headers[$header] = $value; + $this->headers[$header] = $this->sanitiseHeader($value); return $this; } @@ -310,6 +310,14 @@ class HTTPResponse return $this; } + /** + * Sanitise header values to avoid possible XSS vectors + */ + private function sanitiseHeader(string $value): string + { + return preg_replace('/\v/', '', $value); + } + public function redirect(string $dest, int $code = 302): static { if (!in_array($code, self::$redirect_codes)) { diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 9325d2e89..6fce22087 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -1485,16 +1485,21 @@ class Form extends ViewableData implements HasRequestHandler */ public function saveInto(DataObjectInterface $dataObject, $fieldList = null) { + $form = $this; + $dataObject->invokeWithExtensions('onBeforeFormSaveInto', $form, $fieldList); + $dataFields = $this->fields->saveableFields(); $lastField = null; + if ($dataFields) { foreach ($dataFields as $field) { - // Skip fields that have been excluded + // Skip fields that have been excluded if ($fieldList && is_array($fieldList) && !in_array($field->getName(), $fieldList ?? [])) { continue; } $saveMethod = "save{$field->getName()}"; + if ($field->getName() == "ClassName") { $lastField = $field; } elseif ($dataObject->hasMethod($saveMethod)) { @@ -1504,9 +1509,12 @@ class Form extends ViewableData implements HasRequestHandler } } } + if ($lastField) { $lastField->saveInto($dataObject); } + + $dataObject->invokeWithExtensions('onAfterFormSaveInto', $form, $fieldList); } /** diff --git a/src/Forms/GridField/GridFieldSortableHeader.php b/src/Forms/GridField/GridFieldSortableHeader.php index d12dc0383..1902ee473 100644 --- a/src/Forms/GridField/GridFieldSortableHeader.php +++ b/src/Forms/GridField/GridFieldSortableHeader.php @@ -11,6 +11,7 @@ use SilverStripe\ORM\DataObject; use SilverStripe\View\ArrayData; use SilverStripe\View\SSViewer; use LogicException; +use SilverStripe\Core\Injector\Injector; /** * GridFieldSortableHeader adds column headers to a {@link GridField} that can @@ -271,6 +272,16 @@ class GridFieldSortableHeader extends AbstractGridFieldComponent implements Grid return $dataList; } + // Prevent SQL Injection by validating that SortColumn exists + /** @var GridFieldDataColumns $columns */ + $columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class); + $fields = $columns->getDisplayFields($gridField); + if (!array_key_exists($state->SortColumn, $fields) && + !in_array($state->SortColumn, $this->getFieldSorting()) + ) { + throw new LogicException('Invalid SortColumn: ' . $state->SortColumn); + } + return $dataList->sort($state->SortColumn, $state->SortDirection('asc')); } diff --git a/src/Forms/HTMLEditor/HTMLEditorSanitiser.php b/src/Forms/HTMLEditor/HTMLEditorSanitiser.php index 09ff3e8a4..a075d98fa 100644 --- a/src/Forms/HTMLEditor/HTMLEditorSanitiser.php +++ b/src/Forms/HTMLEditor/HTMLEditorSanitiser.php @@ -347,9 +347,9 @@ class HTMLEditorSanitiser } // Matches "javascript:" with any arbitrary linebreaks inbetween the characters. - $regex = '/^\s*' . implode('\v*', str_split('javascript:')) . '/'; + $regex = '/^\s*' . implode('\s*', str_split('javascript:')) . '/i'; // Strip out javascript execution in href or src attributes. - foreach (['src', 'href'] as $dangerAttribute) { + foreach (['src', 'href', 'data'] as $dangerAttribute) { if ($el->hasAttribute($dangerAttribute)) { if (preg_match($regex, $el->getAttribute($dangerAttribute))) { $el->removeAttribute($dangerAttribute); diff --git a/src/ORM/FieldType/DBDecimal.php b/src/ORM/FieldType/DBDecimal.php index 0482f35f0..a8c9df99b 100644 --- a/src/ORM/FieldType/DBDecimal.php +++ b/src/ORM/FieldType/DBDecimal.php @@ -127,7 +127,7 @@ class DBDecimal extends DBField return 0; } - if (ctype_digit((string) $value)) { + if (abs((float) $value - (int) $value) < PHP_FLOAT_EPSILON) { return (int)$value; } diff --git a/src/View/Shortcodes/EmbedShortcodeProvider.php b/src/View/Shortcodes/EmbedShortcodeProvider.php index 73f074297..e87b31a11 100644 --- a/src/View/Shortcodes/EmbedShortcodeProvider.php +++ b/src/View/Shortcodes/EmbedShortcodeProvider.php @@ -16,6 +16,7 @@ use SilverStripe\View\HTML; use SilverStripe\View\Parsers\ShortcodeHandler; use SilverStripe\View\Parsers\ShortcodeParser; use SilverStripe\Control\Director; +use SilverStripe\Core\Config\Configurable; use SilverStripe\View\Embed\EmbedContainer; /** @@ -25,6 +26,23 @@ use SilverStripe\View\Embed\EmbedContainer; */ class EmbedShortcodeProvider implements ShortcodeHandler { + use Configurable; + + /** + * A whitelist of shortcode attributes which are allowed in the resultant markup. + * Note that the tinymce plugin restricts attributes on the client-side separately. + * + * @config + * @deprecated 4.12.0 Removed without equivalent functionality to replace it + */ + private static array $attribute_whitelist = [ + 'url', + 'thumbnail', + 'class', + 'width', + 'height', + 'caption', + ]; /** * Gets the list of shortcodes provided by this handler @@ -180,9 +198,17 @@ class EmbedShortcodeProvider implements ShortcodeHandler } } + $attributes = static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']); + if (array_key_exists('style', $arguments)) { + $attributes->push(ArrayData::create([ + 'Name' => 'style', + 'Value' => Convert::raw2att($arguments['style']), + ])); + } + $data = [ 'Arguments' => $arguments, - 'Attributes' => static::buildAttributeListFromArguments($arguments, ['width', 'height', 'url', 'caption']), + 'Attributes' => $attributes, 'Content' => DBField::create_field('HTMLFragment', $content) ]; @@ -236,6 +262,12 @@ class EmbedShortcodeProvider implements ShortcodeHandler */ private static function buildAttributeListFromArguments(array $arguments, array $exclude = []): ArrayList { + // Clean out any empty arguments and anything not whitelisted + $whitelist = static::config()->get('attribute_whitelist'); + $arguments = array_filter($arguments, function ($value, $key) use ($whitelist) { + return in_array($key, $whitelist) && strlen(trim($value ?? '')); + }, ARRAY_FILTER_USE_BOTH); + $attributes = ArrayList::create(); foreach ($arguments as $key => $value) { if (in_array($key, $exclude ?? [])) { diff --git a/tests/php/Control/HTTPResponseTest.php b/tests/php/Control/HTTPResponseTest.php index 18469ec62..88c1aaeeb 100644 --- a/tests/php/Control/HTTPResponseTest.php +++ b/tests/php/Control/HTTPResponseTest.php @@ -45,6 +45,26 @@ class HTTPResponseTest extends SapphireTest $this->assertEmpty($response->getHeader('X-Animal')); } + public function providerSanitiseHeaders() + { + return [ + 'plain text is retained' => ['some arbitrary value1', 'some arbitrary value1'], + 'special chars are retained' => ['`~!@#$%^&*()_+-=,./<>?;\':"[]{}\\|', '`~!@#$%^&*()_+-=,./<>?;\':"[]{}\\|'], + 'line breaks are removed' => ['no line breaks', "n\ro line \nbreaks\r\n"], + ]; + } + + /** + * @dataProvider providerSanitiseHeaders + */ + public function testSanitiseHeaders(string $expected, string $value) + { + $response = new HTTPResponse(); + + $response->addHeader('X-Sanitised', $value); + $this->assertSame($expected, $response->getHeader('X-Sanitised')); + } + public function providerTestValidStatusCodes() { return [ diff --git a/tests/php/Forms/GridField/GridFieldSortableHeaderTest.php b/tests/php/Forms/GridField/GridFieldSortableHeaderTest.php index b3c5cb8aa..0f4567cde 100644 --- a/tests/php/Forms/GridField/GridFieldSortableHeaderTest.php +++ b/tests/php/Forms/GridField/GridFieldSortableHeaderTest.php @@ -71,13 +71,14 @@ class GridFieldSortableHeaderTest extends SapphireTest $list = Team::get()->filter([ 'ClassName' => Team::class ]); $config = new GridFieldConfig_RecordEditor(); $gridField = new GridField('testfield', 'testfield', $list, $config); + $component = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class); // Test normal sorting + $component->setFieldSorting(['Name' => 'City']); $state = $gridField->State->GridFieldSortableHeader; $state->SortColumn = 'City'; $state->SortDirection = 'asc'; - $component = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class); $listA = $component->getManipulatedData($gridField, $list); $state->SortDirection = 'desc'; @@ -93,6 +94,7 @@ class GridFieldSortableHeaderTest extends SapphireTest ); // Test one relation 'deep' + $component->setFieldSorting(['Name' => 'Cheerleader.Name']); $state->SortColumn = 'Cheerleader.Name'; $state->SortDirection = 'asc'; $relationListA = $component->getManipulatedData($gridField, $list); @@ -110,6 +112,7 @@ class GridFieldSortableHeaderTest extends SapphireTest ); // Test two relations 'deep' + $component->setFieldSorting(['Name' => 'Cheerleader.Hat.Colour']); $state->SortColumn = 'Cheerleader.Hat.Colour'; $state->SortDirection = 'asc'; $relationListC = $component->getManipulatedData($gridField, $list); @@ -139,6 +142,7 @@ class GridFieldSortableHeaderTest extends SapphireTest $component = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class); // Test that inherited dataobjects will work correctly + $component->setFieldSorting(['Name' => 'Cheerleader.Hat.Colour']); $state->SortColumn = 'Cheerleader.Hat.Colour'; $state->SortDirection = 'asc'; $relationListA = $component->getManipulatedData($gridField, $list); @@ -179,6 +183,7 @@ class GridFieldSortableHeaderTest extends SapphireTest ); // Test subclasses of tables + $component->setFieldSorting(['Name' => 'CheerleadersMom.Hat.Colour']); $state->SortColumn = 'CheerleadersMom.Hat.Colour'; $state->SortDirection = 'asc'; $relationListB = $component->getManipulatedData($gridField, $list); @@ -229,4 +234,21 @@ class GridFieldSortableHeaderTest extends SapphireTest $relationListBdesc->column('City') ); } + + public function testSortColumnValidation() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Invalid SortColumn: INVALID'); + + $list = Team::get()->filter([ 'ClassName' => Team::class ]); + $config = new GridFieldConfig_RecordEditor(); + $gridField = new GridField('testfield', 'testfield', $list, $config); + $component = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class); + + $state = $gridField->State->GridFieldSortableHeader; + $state->SortColumn = 'INVALID'; + $state->SortDirection = 'asc'; + + $component->getManipulatedData($gridField, $list); + } } diff --git a/tests/php/Forms/HTMLEditor/HTMLEditorSanitiserTest.php b/tests/php/Forms/HTMLEditor/HTMLEditorSanitiserTest.php index 4b3695274..3d5c3d5c6 100644 --- a/tests/php/Forms/HTMLEditor/HTMLEditorSanitiserTest.php +++ b/tests/php/Forms/HTMLEditor/HTMLEditorSanitiserTest.php @@ -98,6 +98,36 @@ class HTMLEditorSanitiserTest extends FunctionalTest '', 'Javascript in the src attribute of an iframe is completely removed' ], + [ + 'iframe[src]', + '', + '', + 'Mixed case javascript in the src attribute of an iframe is completely removed' + ], + [ + 'iframe[src]', + "", + '', + 'Javascript with tab elements the src attribute of an iframe is completely removed' + ], + [ + 'object[data]', + '', + '', + 'Object with OK content in the data attribute is retained' + ], + [ + 'object[data]', + '