Merge branch '4.0' into 4

This commit is contained in:
Damian Mooyman 2017-12-07 13:52:00 +13:00
commit 6b384f4b35
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
27 changed files with 672 additions and 279 deletions

View File

@ -0,0 +1,88 @@
# 4.0.1-rc1
<!--- Changes below this line will be automatically regenerated -->
## Change Log
### Security
* 2017-11-30 [f1dd3d6f0]() Prevent disclosure of sensitive information via LoginAttempt (Damian Mooyman) - See [ss-2017-009](http://www.silverstripe.org/download/security-releases/ss-2017-009)
* 2017-11-29 [cfe1d4f48]() Ensure xls formulae are safely sanitised on output (Damian Mooyman) - See [ss-2017-007](http://www.silverstripe.org/download/security-releases/ss-2017-007)
* 2017-11-27 [7a79cd039]() Prevent install.php from disclosing system passwords (Damian Mooyman) - See [ss-2017-010](http://www.silverstripe.org/download/security-releases/ss-2017-010)
* 2017-11-20 [099a5a3c2]() SQL injection in full text search (Damian Mooyman) - See [ss-2017-008](http://www.silverstripe.org/download/security-releases/ss-2017-008)
### API Changes
* 2017-11-07 [96dfc95](https://github.com/silverstripe/silverstripe-versioned/commit/96dfc951c7bb76a112cb49af7f0aebf75bd29af4) Remove MemberExtension, functionality is replaced by framework update (Robbie Averill)
### Features and Enhancements
* 2017-12-04 [8ee35f4](https://github.com/silverstripe/silverstripe-asset-admin/commit/8ee35f46cbc9ae96e7927af46ea7ca8f067e55e1) added loadComponent fix for asset-admin entwine components (Christopher Joe)
* 2017-12-01 [259ae3f78]() Add ViewableData::getViewerTemplates() (Loz Calver)
* 2017-11-27 [b418616](https://github.com/silverstripe/silverstripe-admin/commit/b4186162769cee0c4fe7ab2df50781241b4e17ad) Use recipes for test configuration (Damian Mooyman)
* 2017-11-27 [33a39666b]() Promote portugese (portugal) as primary locale (Damian Mooyman)
* 2017-11-20 [3d5f4b3](https://github.com/silverstripe/silverstripe-assets/commit/3d5f4b38952d3b4d9fbf3a53767a942065490a0d) Hide Image_Backend construction behind image manipulations to improve performance (Damian Mooyman)
* 2017-11-20 [dea77c7](https://github.com/silverstripe/silverstripe-assets/commit/dea77c7792f9ee92f6c2ac600978c16cf7bf222c) Disable force_resample by default (Damian Mooyman)
* 2017-11-20 [0a37ff3](https://github.com/silverstripe/silverstripe-asset-admin/commit/0a37ff30c0d747e89dbcbd1a2a42411392aeb118) Don't request unused width / height from graphql (Damian Mooyman)
* 2017-11-15 [6a6cf2f9e]() Raise warning if DBField::create_field() would behave unpredictably and improve PHPDoc (Damian Mooyman)
* 2017-11-15 [8b063026f]() Ensure that non-writable assets files are notified during install (Damian Mooyman)
### Bugfixes
* 2017-12-05 [7efe667a](https://github.com/silverstripe/silverstripe-cms/commit/7efe667a487b4551dc21c0435b83cdced6290fc6) VirtualPage not using target page's template (fixes #2039) (#2041) (Loz Calver)
* 2017-12-03 [9fcebe7](https://github.com/silverstripe/silverstripe-asset-admin/commit/9fcebe7f317d2bdc7c1371ec4b4f6f20f6687e02) Fix unit tests (Christopher Joe)
* 2017-12-02 [3a4c6705c]() db autodiscover comment on loading behavior. (Russell Maclean)
* 2017-12-01 [91bd92df3]() Remove some unnecessary ClassInfo calls in DataObjectSchema (Loz Calver)
* 2017-12-01 [69295a6e2]() Ensure that all tinymce_lang mappings are valid (Damian Mooyman)
* 2017-11-28 [b3a51aa8d]() Fix broken scrutinizer (Damian Mooyman)
* 2017-11-28 [0de9b07](https://github.com/silverstripe/silverstripe-admin/commit/0de9b074fe6ac1e2d44c67b16f76fe3e01224131) Fix typo in Menu.scss (Damian Mooyman)
* 2017-11-27 [c4b366828]() Restore BackURL preservation on log out (closes #7636) (Loz Calver)
* 2017-11-27 [83cf147](https://github.com/silverstripe/silverstripe-admin/commit/83cf147649095f35ea6421a583153587c4afd8d9) Issue where logging out from the CMS presents you with a login form with no BackURL (Daniel Hensby)
* 2017-11-27 [b7ea05900]() Support self::class text collection (Robbie Averill)
* 2017-11-27 [cc72b5c85]() Added warning for auto-generated table_name for non-test classes (Christopher Joe)
* 2017-11-24 [09a003bc1]() deprecated usage of getMock in unit tests (Daniel Hensby)
* 2017-11-22 [ef6d86f2c]() Allow lowercase and uppercase delcaration of legacy Int class (Daniel Hensby)
* 2017-11-22 [d9e7a92b3]() Fix regressions in asset resize behaviour change (Damian Mooyman)
* 2017-11-21 [966622263]() Fix _configure_database.php being ignored (Damian Mooyman)
* 2017-11-21 [14b2641](https://github.com/silverstripe/silverstripe-config/commit/14b2641d0e10641817c15f1cda183b046fb86232) Fix added module fluid-prefix so module config will not require the full path to match (Christopher Joe)
* 2017-11-21 [27c079c](https://github.com/silverstripe/silverstripe-asset-admin/commit/27c079c307cb4d7ea4f0c176ad5105f37ecf3c27) Fix change in resampled config setting (Christopher Joe)
* 2017-11-21 [b1dc681](https://github.com/silverstripe/silverstripe-admin/commit/b1dc681d369899b2205340d1522441d6ccce5d24) Ensure changetracker safely defers to other init scripts (Damian Mooyman)
* 2017-11-20 [1e96989](https://github.com/silverstripe/silverstripe-config/commit/1e96989b99b108356ef349ce3a40f8681b71e925) Fix parameter order (Christopher Joe)
* 2017-11-20 [4fb0911](https://github.com/silverstripe/silverstripe-admin/commit/4fb0911290506d63d61c5a285f6ad6102cd3384f) Fix for buttons in change tracking and gridfield reloading (Christopher Joe)
* 2017-11-19 [694d2589](https://github.com/silverstripe/silverstripe-cms/commit/694d2589579f944ec714dac6aff7b888e5c42a96) Fix allowed children types now load properly (Christopher Joe)
* 2017-11-17 [509a590](https://github.com/silverstripe/silverstripe-admin/commit/509a590362d815f7d680c14a747e09674b3651b4) fix ignore `no-change-track` marked fields in changetracker (Christopher Joe)
* 2017-11-16 [cbf9e4011]() Fix postgres / PDO support (Damian Mooyman)
* 2017-11-16 [dda14e895]() HTTP::get_mime_type with uppercase filenames. (Roman Schmid)
* 2017-11-16 [52f0eadd3]() for #7606: Ensure the object we're handling is actually an Image instance before calling methods specific to that class (e.g. in case of using SVG's in &lt;img&gt; tag which may be File instances). (Patrick Nelson)
* 2017-11-16 [8de154d9](https://github.com/silverstripe/silverstripe-cms/commit/8de154d92cc9b044824205216683c0a187d17e5b) Restore missing '(Choose Page)' text in link insert modal (Damian Mooyman)
* 2017-11-15 [ef5879910]() Fix DBEnum ignoring empty defaults (Damian Mooyman)
* 2017-11-15 [ce3fd370f]() ManyMany link table joined with LEFT JOIN (Daniel Hensby)
* 2017-11-15 [3ad6a93](https://github.com/silverstripe/silverstripe-admin/commit/3ad6a937b8ae499f2f401c4cc1dd1193442f971e) page header center aligns when site tree is closed (Saophalkun Ponlu)
* 2017-11-15 [1c81430](https://github.com/silverstripe/silverstripe-admin/commit/1c8143078048bc0bdd516aeefcb171ec527359f2) fix show empty string title when relevant - rather than null when no options (Christopher Joe)
* 2017-11-14 [2bc7edbf4]() Fix don't treat zero-date as invalid (Christopher Joe)
* 2017-11-14 [b7e96fc](https://github.com/silverstripe/silverstripe-assets/commit/b7e96fcf3e39777d75740468ee39c224318ec68d) Prevent .htaccess operations from users in the same group failing (Damian Mooyman)
* 2017-11-14 [2e3bbe4](https://github.com/silverstripe/silverstripe-asset-admin/commit/2e3bbe437c5c7234a590c514c56b2c5674eaa051) Fix shortcodes not being parsed (Damian Mooyman)
* 2017-11-13 [3b5cee8](https://github.com/silverstripe/silverstripe-admin/commit/3b5cee8135ed444dba05bf66676ecf90c4b220e3) unsaved change dialog display just after creating a record (Saophalkun Ponlu)
* 2017-11-13 [d60f4a3](https://github.com/silverstripe/silverstripe-admin/commit/d60f4a30795a7d40d0b766bf62ed55c8291efe69) fix missing chosen sprites added to dist folder (Christopher Joe)
* 2017-11-09 [bf20d59cb]() Fixes SapphireTest masking userland coding errors. (Russell Michell)
* 2017-11-09 [1053de7ec]() Don't redirect in force_redirect() in CLI (Damian Mooyman)
* 2017-11-08 [0a87ad2](https://github.com/silverstripe/silverstripe-assets/commit/0a87ad270214563e7a90338b0a090da28dce6e1a) Remove whitespace around download link title (Robbie Averill)
* 2017-11-08 [6139de868]() Make sure plain parts are rendered when re-rendering emails (Daniel Hensby)
* 2017-11-06 [d3ff0d7](https://github.com/silverstripe/silverstripe-asset-admin/commit/d3ff0d74d2d49a2294b40f532174351bdef58101) Fix buttons in upload field to be proper button types (Christopher Joe)
* 2017-11-05 [d8b4ca91d]() Fix ContextSummary behaviour with UTF8 chars (Sam Minnee)
* 2017-11-03 [1d5cd20](https://github.com/silverstripe/silverstripe-admin/commit/1d5cd20ac553278396600e848f5482633c96a52c) Fix react-select does not return the true value when the option is missing (Christopher Joe)
* 2017-11-02 [457e653](https://github.com/silverstripe/silverstripe-asset-admin/commit/457e653867631d1fcec5676beb48ed01e05caaee) Fix native upload dialog appearing in entwine sections and added a canUpload condition for UploadField (Christopher Joe)
* 2017-11-01 [1bdfd056](https://github.com/silverstripe/silverstripe-cms/commit/1bdfd056482f1ba2c682e683a3582c682ec3dbbe) Remove usage of deprecated each() (Damian Mooyman)
* 2017-11-01 [5bc4f3d1f]() Remove usage of deprecated each() and use a helper method instead (Damian Mooyman)
* 2017-10-31 [f952ef747]() ed array/object mismatch bug in PaginatedList (Colin Tucker)
* 2017-10-25 [dd54e04](https://github.com/silverstripe/silverstripe-asset-admin/commit/dd54e04a4998ffb1968ab1e38423752f8e44b426) Fix usability issue, can tab to the upload field item even when it doesn't do anything by default (Christopher Joe)
* 2017-10-25 [cbac37559]() Helpful warning when phpunit bootstrap appears misconfigured (Daniel Hensby)
* 2017-10-25 [32cef975e]() Use self::inst() for Injector/Config nest methods (Daniel Hensby)
* 2017-10-25 [23316fc](https://github.com/silverstripe/silverstripe-asset-admin/commit/23316fccf2e1da907251614f6aaf40f58a6427ac) Fix wrong mouse cursor for description text in upload field area (Christopher Joe)
* 2017-10-19 [4965dc4](https://github.com/silverstripe/silverstripe-admin/commit/4965dc4fcb1cf650b1b4840f13e25aca39fd286f) stop bothering people with pop-ups (Christopher Joe)
* 2017-10-19 [a73d5b41](https://github.com/silverstripe/silverstripe-cms/commit/a73d5b4177be445128a6fa42e20dd8df13eaf554) revert to this button after archiving (Christopher Joe)
* 2017-10-12 [fd39faee](https://github.com/silverstripe/silverstripe-cms/commit/fd39faeefd5241cf96313e968142183de767c51b) UploadField overwriteWarning isn't working in AssetAdmin (Jason)
* 2017-10-09 [264cec123]() Dont use var_export for cache key generation as it fails on circular references (Daniel Hensby)
* 2017-10-04 [24e190ea](https://github.com/silverstripe/silverstripe-cms/commit/24e190ea8265d16445a3210f7b06de191e474004) TreeDropdownField showing broken page icons (fixes silverstripe/silverstripe-framework#7420) (Loz Calver)
* 2017-08-13 [2f579b64c]() Files without extensions (folders) do not have a trailing period added (Robbie Averill)
* 2017-07-04 [00f1ad5d6]() Fixes #7116 Improves server requirements docs viz: OpCaches. (Russell Michell)

View File

@ -211,6 +211,7 @@ en:
many_many_Members: Members many_many_Members: Members
SilverStripe\Security\LoginAttempt: SilverStripe\Security\LoginAttempt:
Email: 'Email Address' Email: 'Email Address'
EmailHashed: 'Email Address (hashed)'
IP: 'IP Address' IP: 'IP Address'
PLURALNAME: 'Login Attempts' PLURALNAME: 'Login Attempts'
PLURALS: PLURALS:
@ -269,6 +270,8 @@ en:
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator: SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
ERRORWRONGCRED: 'The provided details don''t seem to be correct. Please try again.' ERRORWRONGCRED: 'The provided details don''t seem to be correct. Please try again.'
NoPassword: 'There is no password on this member.' NoPassword: 'There is no password on this member.'
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
AUTHENTICATORNAME: 'E-mail &amp; Password'
SilverStripe\Security\MemberPassword: SilverStripe\Security\MemberPassword:
PLURALNAME: 'Member Passwords' PLURALNAME: 'Member Passwords'
PLURALS: PLURALS:

View File

@ -100,6 +100,8 @@ fi:
Save: Tallenna Save: Tallenna
SilverStripe\Forms\GridField\GridFieldEditButton_ss: SilverStripe\Forms\GridField\GridFieldEditButton_ss:
EDIT: Muokkaa EDIT: Muokkaa
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
UnlinkSelfFailure: 'Et voi siirtää itseäsi pois tästä ryhmästä: menettäisit pääkäyttäjän oikeudet'
SilverStripe\Forms\GridField\GridFieldPaginator: SilverStripe\Forms\GridField\GridFieldPaginator:
OF: / OF: /
Page: Sivu Page: Sivu
@ -250,6 +252,7 @@ fi:
SUBJECTPASSWORDCHANGED: 'Salasanasi on vaihdettu' SUBJECTPASSWORDCHANGED: 'Salasanasi on vaihdettu'
SUBJECTPASSWORDRESET: 'Salasanasi palautuslinkki' SUBJECTPASSWORDRESET: 'Salasanasi palautuslinkki'
SURNAME: Sukunimi SURNAME: Sukunimi
VALIDATIONADMINLOSTACCESS: 'Ylläpitäjäryhmää ei voi poistaa profiilistasi'
ValidationIdentifierFailed: 'Olemassa olevan käyttäjän id:n #{id} päälle ei voida kirjoittaa samalla tunnisteella ({name} = {value}))' ValidationIdentifierFailed: 'Olemassa olevan käyttäjän id:n #{id} päälle ei voida kirjoittaa samalla tunnisteella ({name} = {value}))'
WELCOMEBACK: 'Tervetuloa takaisin, {firstname}' WELCOMEBACK: 'Tervetuloa takaisin, {firstname}'
YOUROLDPASSWORD: 'Vanha salasanasi' YOUROLDPASSWORD: 'Vanha salasanasi'

View File

@ -1,63 +1,84 @@
fr: fr:
SilverStripe\Admin\LeftAndMain:
VersionUnknown: Inconnu
SilverStripe\AssetAdmin\Forms\UploadField: SilverStripe\AssetAdmin\Forms\UploadField:
Dimensions: Dimensions Dimensions: Dimensions
EDIT: Éditer EDIT: Éditer
EDITINFO: 'Éditer ce fichier' EDITINFO: 'Éditer ce fichier'
REMOVE: Retirer
SilverStripe\Control\ChangePasswordEmail_ss: SilverStripe\Control\ChangePasswordEmail_ss:
CHANGEPASSWORDFOREMAIL: 'Le mot de passe du compte correspondant à l''adresse {email} a été modifié. Si vous n''avez pas modifié votre mot de passe, merci de le changer à l''aide du lien suivant'
CHANGEPASSWORDTEXT1: 'Votre mot de passe est maintenant' CHANGEPASSWORDTEXT1: 'Votre mot de passe est maintenant'
CHANGEPASSWORDTEXT3: 'Changer le mot de passe' CHANGEPASSWORDTEXT3: 'Changer le mot de passe'
HELLO: Bonjour HELLO: Bonjour
SilverStripe\Control\Email\ForgotPasswordEmail_ss: SilverStripe\Control\Email\ForgotPasswordEmail_ss:
HELLO: Bonjour HELLO: Bonjour
TEXT1: 'Voici votre'
TEXT2: 'Lien de réinitialisation de mot de passe'
TEXT3: pour
SilverStripe\Control\RequestProcessor:
INVALID_REQUEST: 'Requête invalide'
REQUEST_ABORTED: 'Requête non aboutie'
SilverStripe\Core\Manifest\VersionProvider:
VERSIONUNKNOWN: Inconnu
SilverStripe\Forms\CheckboxField: SilverStripe\Forms\CheckboxField:
NOANSWER: Non NOANSWER: Non
YESANSWER: Oui YESANSWER: Oui
SilverStripe\Forms\CheckboxSetField_ss:
NOOPTIONSAVAILABLE: 'Aucune option disponible'
SilverStripe\Forms\ConfirmedPasswordField: SilverStripe\Forms\ConfirmedPasswordField:
ATLEAST: 'Le mot de passe doit comporter au moins {min} caractères.' ATLEAST: 'Le mot de passe doit comporter au moins {min} caractères.'
BETWEEN: 'Le mot de passe doit comporter entre {min} et {max} caractères.' BETWEEN: 'Le mot de passe doit comporter entre {min} et {max} caractères.'
CURRENT_PASSWORD_ERROR: 'Le mot de passe que vous avez saisi n''est pas correct' CURRENT_PASSWORD_ERROR: 'Le mot de passe que vous avez saisi n''est pas correct'
CURRENT_PASSWORD_MISSING: 'Vous devez saisir votre mot de passe actuel.' CURRENT_PASSWORD_MISSING: 'Vous devez saisir votre mot de passe actuel.'
LOGGED_IN_ERROR: 'Vous devez être connecté pour pouvoir changer votre mot de passe' LOGGED_IN_ERROR: 'Vous devez être connecté pour pouvoir changer votre mot de passe'
MAXIMUM: 'Le mot de passe ne doit comporter plus de {max} caractères.' MAXIMUM: 'Le mot de passe ne doit pas comporter plus de {max} caractères.'
SHOWONCLICKTITLE: 'Changer le mot de passe' SHOWONCLICKTITLE: 'Changer le mot de passe'
SilverStripe\Forms\CurrencyField: SilverStripe\Forms\CurrencyField:
CURRENCYSYMBOL: $ CURRENCYSYMBOL: $
SilverStripe\Forms\DateField: SilverStripe\Forms\DateField:
NOTSET: 'non renseigné' NOTSET: 'non renseigné'
TODAY: 'aujourd''hui' TODAY: 'aujourd''hui'
VALIDDATEFORMAT2: 'Saisissez la date au format valide ({format})' VALIDDATEFORMAT2: 'Saisissez une date au format valide ({format})'
VALIDDATEMAXDATE: 'La date doit être antérieure ou égale à celle qui a été autorisée ({date})' VALIDDATEMAXDATE: 'La date doit être antérieure ou égale à celle autorisée ({date})'
VALIDDATEMINDATE: 'La date doit être plus récente ou égale à celle qui a été autorisée ({date})' VALIDDATEMINDATE: 'La date doit être postérieure ou égale à celle autorisée ({date})'
SilverStripe\Forms\DatetimeField: SilverStripe\Forms\DatetimeField:
VALIDDATEMAXDATETIME: 'La date doit être antérieure ou égale à celle autorisée ({datetime})'
VALIDDATETIMEFORMAT: 'Saisissez un format de date et d''heure valide ({format})' VALIDDATETIMEFORMAT: 'Saisissez un format de date et d''heure valide ({format})'
VALIDDATETIMEMINDATE: 'La date doit être postérieure ou égale à celle autorisée ({datetime})'
SilverStripe\Forms\DropdownField: SilverStripe\Forms\DropdownField:
CHOOSE: (Choisir) CHOOSE: (Choisir)
CHOOSE_MODEL: '(Choisir {name})' CHOOSE_MODEL: '(Choisir {name})'
SOURCE_VALIDATION: 'Merci de choisir une valeur parmi celles proposées dans la liste. {value} n''est pas une option valide'
SilverStripe\Forms\EmailField: SilverStripe\Forms\EmailField:
VALIDATION: 'Saisissez une adresse de courrier électronique' VALIDATION: 'Merci de saisir une adresse email'
SilverStripe\Forms\FileUploadReceiver: SilverStripe\Forms\FileUploadReceiver:
FIELDNOTSET: 'Informations concernant le fichiers non-trouvées' FIELDNOTSET: 'Information sur le fichier introuvable'
SilverStripe\Forms\Form: SilverStripe\Forms\Form:
CSRF_EXPIRED_MESSAGE: 'Votre session a expiré. Renvoyez le formulaire.' BAD_METHOD: 'Ce formulaire requiert une action {method}'
CSRF_EXPIRED_MESSAGE: 'Votre session a expiré. Merci de renvoyer le formulaire.'
CSRF_FAILED_MESSAGE: 'Un problème technique est probablement survenu. Merci de cliquer sur le bouton "retour", de rafraîchir la page de votre navigateur, et de réessayer.'
VALIDATIONPASSWORDSDONTMATCH: 'Les mots de passe ne correspondent pas' VALIDATIONPASSWORDSDONTMATCH: 'Les mots de passe ne correspondent pas'
VALIDATIONPASSWORDSNOTEMPTY: 'Les mots de passe peuvent être vides' VALIDATIONPASSWORDSNOTEMPTY: 'Les mots de passe ne peuvent pas être vides'
VALIDATIONSTRONGPASSWORD: 'Le mot de passe doit comporter au moins un chiffre et un caractère alphanumérique' VALIDATIONSTRONGPASSWORD: 'Le mot de passe doit comporter au moins un chiffre et un caractère alphanumérique'
VALIDATOR: Validateur VALIDATOR: Validateur
VALIDCURRENCY: 'Saisissez une monnaie valide' VALIDCURRENCY: 'Saisissez une monnaie valide'
SilverStripe\Forms\FormField: SilverStripe\Forms\FormField:
EXAMPLE: 'Par ex. {format}'
NONE: aucun NONE: aucun
SilverStripe\Forms\FormScaffolder: SilverStripe\Forms\FormScaffolder:
TABMAIN: Principal TABMAIN: Principal
SilverStripe\Forms\GridField\GridField: SilverStripe\Forms\GridField\GridField:
Add: 'Ajouter {name}' Add: 'Ajouter {name}'
CSVEXPORT: 'Exporter vers un fichier CSV' CSVEXPORT: 'Exporter vers un fichier CSV'
CSVIMPORT: 'Importer un fichier CSV'
Filter: Filtrer Filter: Filtrer
FilterBy: 'Filtrer par' FilterBy: 'Filtrer par'
Find: Trouver Find: Trouver
LinkExisting: 'Lien existant' LinkExisting: 'Lien existant'
NewRecord: 'Nouveau {type}' NewRecord: 'Nouveau {type}'
NoItemsFound: 'Aucun élément na été trouvé.' NoItemsFound: 'Aucun élément na été trouvé.'
PRINTEDAT: 'Imprimé le' PRINTEDAT: 'Imprimé à'
PRINTEDBY: 'Imprimé par' PRINTEDBY: 'Imprimé par'
PlaceHolder: 'Rechercher {type}' PlaceHolder: 'Rechercher {type}'
PlaceHolderWithLabels: 'Rechercher {type} par {name}' PlaceHolderWithLabels: 'Rechercher {type} par {name}'
@ -65,7 +86,7 @@ fr:
RelationSearch: 'Rechercher relations' RelationSearch: 'Rechercher relations'
ResetFilter: Réinitialiser ResetFilter: Réinitialiser
SilverStripe\Forms\GridField\GridFieldDeleteAction: SilverStripe\Forms\GridField\GridFieldDeleteAction:
DELETE_DESCRIPTION: Supprime DELETE_DESCRIPTION: Supprimer
Delete: Supprimer Delete: Supprimer
DeletePermissionsFailure: 'Vous navez pas les autorisations pour supprimer' DeletePermissionsFailure: 'Vous navez pas les autorisations pour supprimer'
EditPermissionsFailure: 'Pas de permissions pour délier l''enregistrement' EditPermissionsFailure: 'Pas de permissions pour délier l''enregistrement'
@ -79,52 +100,110 @@ fr:
Save: Enregistrer Save: Enregistrer
SilverStripe\Forms\GridField\GridFieldEditButton_ss: SilverStripe\Forms\GridField\GridFieldEditButton_ss:
EDIT: Editer EDIT: Editer
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
UnlinkSelfFailure: 'Impossible de retirer votre propre profil de ce groupe, vous perdriez vos droits d''administration'
SilverStripe\Forms\GridField\GridFieldPaginator: SilverStripe\Forms\GridField\GridFieldPaginator:
OF: de OF: de
Page: Page Page: Page
View: Vue View: Vue
SilverStripe\Forms\GridField\GridFieldVersionedState:
ADDEDTODRAFTHELP: 'L''élément n''a pas encore été publié'
ADDEDTODRAFTSHORT: Brouillon
ARCHIVEDPAGEHELP: 'Elément retiré du brouillon et du site public'
ARCHIVEDPAGESHORT: Archivé
MODIFIEDONDRAFTHELP: 'L''élément comporte des modifications non sauvegardées'
MODIFIEDONDRAFTSHORT: Modifié
ONLIVEONLYSHORT: 'Site public uniquement'
ONLIVEONLYSHORTHELP: 'L''élément a été publié, mais sa version brouillon a été supprimée'
SilverStripe\Forms\MoneyField: SilverStripe\Forms\MoneyField:
FIELDLABELAMOUNT: Quantité FIELDLABELAMOUNT: Quantité
FIELDLABELCURRENCY: Devise FIELDLABELCURRENCY: Devise
INVALID_CURRENCY: '{currency} ne figure pas dans la liste des devises autorisées'
SilverStripe\Forms\MultiSelectField:
SOURCE_VALIDATION: 'Merci de choisir des valeurs parmi celles proposées dans la liste. Option(s) {value} non valide(s)'
SilverStripe\Forms\NullableField: SilverStripe\Forms\NullableField:
IsNullLabel: 'Est Null' IsNullLabel: 'Est Null'
SilverStripe\Forms\NumericField: SilverStripe\Forms\NumericField:
VALIDATION: "«\_{value}\_» nest pas un chiffre, seule donnée acceptée dans ce champ " VALIDATION: "«\_{value}\_» nest pas un nombre, seul type de donnée acceptée dans ce champ "
SilverStripe\Forms\TimeField: SilverStripe\Forms\TimeField:
VALIDATEFORMAT: 'Saisissez lheure au format valide ({format})' VALIDATEFORMAT: 'Merci de saisir un format de date valide ({format})'
SilverStripe\ORM\DataObject:
PLURALNAME: 'Modèles de donnée'
PLURALS:
one: 'Un modèle de donnée'
other: '{count} modèles de donnée'
SINGULARNAME: 'Modèle de donnée'
SilverStripe\ORM\FieldType\DBBoolean: SilverStripe\ORM\FieldType\DBBoolean:
ANY: Tout ANY: Tout
NOANSWER: Non NOANSWER: Non
YESANSWER: Oui YESANSWER: Oui
SilverStripe\ORM\FieldType\DBDate: SilverStripe\ORM\FieldType\DBDate:
DAYS_SHORT_PLURALS:
one: '{count} jour'
other: '{count} jours'
HOURS_SHORT_PLURALS:
one: '{count} heure'
other: '{count} heures'
LessThanMinuteAgo: 'moins d''une minute' LessThanMinuteAgo: 'moins d''une minute'
MINUTES_SHORT_PLURALS:
one: '{count} min.'
other: '{count} min.'
MONTHS_SHORT_PLURALS:
one: '{count} mois'
other: '{count} mois'
SECONDS_SHORT_PLURALS:
one: '{count} sec.'
other: '{count} sec.'
TIMEDIFFAGO: 'Il y a {difference}' TIMEDIFFAGO: 'Il y a {difference}'
TIMEDIFFIN: 'Dans {difference}' TIMEDIFFIN: 'Dans {difference}'
YEARS_SHORT_PLURALS:
one: '{count} année'
other: '{count} années'
SilverStripe\ORM\FieldType\DBEnum: SilverStripe\ORM\FieldType\DBEnum:
ANY: Tous ANY: Tout
SilverStripe\ORM\Hierarchy:
LIMITED_TITLE: 'Enfants trop nombreux ({count})'
SilverStripe\ORM\Hierarchy\Hierarchy: SilverStripe\ORM\Hierarchy\Hierarchy:
InfiniteLoopNotAllowed: "Une boucle infinie sest produite dans la hiérarchie «\_{type}\_». Modifiez le parent pour le résoudre." InfiniteLoopNotAllowed: "Une boucle sans fin sest produite dans la hiérarchie «\_{type}\_». Merci de modifier le parent pour résoudre le problème."
LIMITED_TITLE: 'Enfants trop nombreux ({count})'
SilverStripe\ORM\ValidationException: SilverStripe\ORM\ValidationException:
DEFAULT_ERROR: 'Erreur de validation' DEFAULT_ERROR: 'Erreur de validation'
SilverStripe\Security\BasicAuth: SilverStripe\Security\BasicAuth:
ENTERINFO: 'Entrer un identifiant et un mot de passe s''il vous plaît.' ENTERINFO: 'Merci d''entrer un identifiant et un mot de passe.'
ERRORNOTADMIN: 'Cet utilisateur n''est pas un administrateur.' ERRORNOTADMIN: 'Cet utilisateur n''est pas un administrateur.'
ERRORNOTREC: 'Cet identifiant / mot de passe n''est pas reconnu' ERRORNOTREC: 'Identifiant et/ou mot de passe non reconnus'
SilverStripe\Security\CMSMemberLoginForm:
PASSWORDEXPIRED: '<p>Votre mot de passe a expiré. <a target="_top" href="{link}">Merci d''en choisir un nouveau.</a></p>'
SilverStripe\Security\CMSSecurity:
INVALIDUSER: '<p>Utilisateur non valide. <a target="_top" href="{link}">Merci de vous authentifier de nouveau ici</a> pour poursuivre.</p>'
LOGIN_MESSAGE: '<p>Votre session a expiré pour cause d''inactivité</p>'
LOGIN_TITLE: 'Retournez là où vous en étiez en vos connectant de nouveau'
SUCCESS: Succès
SUCCESSCONTENT: '<p>Connexion réussie. Si vous n''êtes pas automatiquement redirigé <a target="_top" href="{link}">cliquez ici</a></p>'
SUCCESS_TITLE: 'Connexion réussie'
SilverStripe\Security\DefaultAdminService:
DefaultAdminFirstname: 'Administrateur par défaut'
SilverStripe\Security\Group: SilverStripe\Security\Group:
AddRole: 'Ajouter un rôle pour ce groupe' AddRole: 'Ajouter un rôle pour ce groupe'
Code: 'Code de groupe' Code: 'Code du groupe'
DefaultGroupTitleAdministrators: Administrateur DefaultGroupTitleAdministrators: Administrateur
DefaultGroupTitleContentAuthors: 'Auteurs du contenu' DefaultGroupTitleContentAuthors: 'Auteurs du contenu'
Description: Description Description: Description
GROUPNAME: 'Nom du groupe'
GroupReminder: 'Si vous choisissez un groupe parent, ce groupe prendra tous ses rôles' GroupReminder: 'Si vous choisissez un groupe parent, ce groupe prendra tous ses rôles'
HierarchyPermsError: 'Impossible d''attribuer des autorisations au groupe parent "{group}" (requiert un accès en tant qu''administrateur)'
Locked: 'Verrouillé?' Locked: 'Verrouillé?'
MEMBERS: Membres MEMBERS: Membres
NEWGROUP: 'Nouveau groupe' NEWGROUP: 'Nouveau groupe'
NoRoles: 'Vous navez pas la permission pour faire ça' NoRoles: 'Aucun rôle trouvé'
PERMISSIONS: Permissions PERMISSIONS: Permissions
PLURALNAME: Groupes PLURALNAME: Groupes
PLURALS:
one: 'Un groupe'
other: '{count} groupes'
Parent: 'Groupe parent' Parent: 'Groupe parent'
ROLES: Rôles ROLES: Rôles
ROLESDESCRIPTION: 'Les rôles sont des ensembles de permissions prédéfinies, et ils peuvent être attribués à des groupes. <br />Si nécessaire, ils peuvent hériter de groupes parents.'
RolesAddEditLink: 'Ajouter/éditer les rôles' RolesAddEditLink: 'Ajouter/éditer les rôles'
SINGULARNAME: Groupe SINGULARNAME: Groupe
Sort: 'Ordre de tri' Sort: 'Ordre de tri'
@ -133,80 +212,125 @@ fr:
SilverStripe\Security\LoginAttempt: SilverStripe\Security\LoginAttempt:
Email: 'Adresse email' Email: 'Adresse email'
IP: 'Adresse IP' IP: 'Adresse IP'
PLURALNAME: 'Tentatives de connexion'
PLURALS:
one: 'Une tentative de connexion'
other: '{count} tentatives de connexion'
SINGULARNAME: 'Tentative de connexion'
Status: Statut Status: Statut
SilverStripe\Security\Member: SilverStripe\Security\Member:
ADDGROUP: 'Ajouter un groupe' ADDGROUP: 'Ajouter un groupe'
BUTTONCHANGEPASSWORD: 'Changer mot de passe' BUTTONCHANGEPASSWORD: 'Changer mot de passe'
BUTTONLOGIN: 'Se connecter' BUTTONLOGIN: 'Se connecter'
BUTTONLOGINOTHER: 'Connectez vous avec un différent identifiant' BUTTONLOGINOTHER: 'Se connecter avec un identifiant différent'
BUTTONLOGOUT: Déconnexion
BUTTONLOSTPASSWORD: 'J''ai perdu mon mot de passe' BUTTONLOSTPASSWORD: 'J''ai perdu mon mot de passe'
CONFIRMNEWPASSWORD: 'Confirmer nouveau mot de passe' CONFIRMNEWPASSWORD: 'Confirmer nouveau mot de passe'
CONFIRMPASSWORD: 'Confirmer Mot De Passe' CONFIRMPASSWORD: 'Confirmer Mot De Passe'
CURRENT_PASSWORD: 'Mot de passe actuel' CURRENT_PASSWORD: 'Mot de passe actuel'
EDIT_PASSWORD: 'Nouveau mot de passe' EDIT_PASSWORD: 'Nouveau mot de passe'
EMAIL: Email EMAIL: Email
EMPTYNEWPASSWORD: 'Le champs nouveau mot de passe ne peut être vide, essayez de nouveau' EMPTYNEWPASSWORD: 'Le champs nouveau mot de passe ne peut être vide, merci de réessayer'
ENTEREMAIL: 'Veuillez écrire une adresse email pour obtenir le lien de réinitialisation du mot de passe.' ENTEREMAIL: 'Veuillez entrer une adresse email pour obtenir un lien de réinitialisation du mot de passe.'
ERRORLOCKEDOUT2: 'Votre compte a été temporairement désactivé à cause de trop nombreux échecs d''identification. Veuillez réessayer dans {count} minutes.' ERRORLOCKEDOUT2: 'Votre compte a été temporairement désactivé en raison d''un nombre trop élevé d''échecs d''identification. Veuillez réessayer dans {count} minutes.'
ERRORNEWPASSWORD: 'Vous avez entré votre nouveau mot de passe différemment, réessayez' ERRORNEWPASSWORD: 'Vous avez entré votre nouveau mot de passe différemment, réessayez'
ERRORPASSWORDNOTMATCH: 'Votre actuel mot de passe ne correspond pas, essayez encore s''il vous plaît' ERRORPASSWORDNOTMATCH: 'Votre actuel mot de passe ne correspond pas, merci de réessayer'
ERRORWRONGCRED: 'Il semble que ce ne soit pas le bon email ou mot de passe. Essayez encore s''il vous plaît.' ERRORWRONGCRED: 'Il semble que ce ne soit pas le bon email ou mot de passe. Merci de réessayer.'
FIRSTNAME: Prénom FIRSTNAME: Prénom
INTERFACELANG: 'Langue de l''interface' INTERFACELANG: 'Langue de l''interface'
KEEPMESIGNEDIN: 'Se souvenir de moi' KEEPMESIGNEDIN: 'Se souvenir de moi'
LOGGEDINAS: 'Vous êtes connecté comme {name}.' LOGGEDINAS: 'Vous êtes connecté en tant que {name}.'
NEWPASSWORD: 'Nouveau mot de passe' NEWPASSWORD: 'Nouveau mot de passe'
PASSWORD: 'Mot de passe' PASSWORD: 'Mot de passe'
PASSWORDEXPIRED: 'Votre mot de passe a expiré. Choisissez-en un nouveau.' PASSWORDEXPIRED: 'Votre mot de passe a expiré. Merci d''en choisir un nouveau.'
PLURALNAME: Membres PLURALNAME: Membres
PLURALS:
one: 'Un membre'
other: '{count} membres'
REMEMBERME: 'Se souvenir de moi la prochaine fois ? (pour une durée de {count} jours sur cet appareil)'
SINGULARNAME: Membre SINGULARNAME: Membre
SUBJECTPASSWORDCHANGED: 'Votre mot de passe a été changé' SUBJECTPASSWORDCHANGED: 'Votre mot de passe a été changé'
SUBJECTPASSWORDRESET: 'Lien pour modifier votre mot de passe' SUBJECTPASSWORDRESET: 'Lien pour modifier votre mot de passe'
SURNAME: 'Nom de famille' SURNAME: 'Nom de famille'
ValidationIdentifierFailed: 'On ne peut pas réenregistrer le membre nº {id} avec un identifiant identique ({name} = {value}))' VALIDATIONADMINLOSTACCESS: 'Impossible de retirer tous les groupes d''administrateur à partir de votre profil'
WELCOMEBACK: 'Bienvenue {firstname}' ValidationIdentifierFailed: 'Impossible de réenregistrer le membre nº {id} avec un identifiant identique ({name} = {value}))'
WELCOMEBACK: 'Bienvenue de nouveau, {firstname}'
YOUROLDPASSWORD: 'Votre ancien mot de passe' YOUROLDPASSWORD: 'Votre ancien mot de passe'
belongs_many_many_Groups: Groupes belongs_many_many_Groups: Groupes
db_Locale: 'Langue de l''Interface' db_Locale: 'Langue de l''Interface'
db_LockedOutUntil: 'Verrouillé jusqu''à' db_LockedOutUntil: 'Verrouillé jusqu''à'
db_Password: 'Mot de passe' db_Password: 'Mot de passe'
db_PasswordExpiry: 'Date d''expiration du mot de passe' db_PasswordExpiry: 'Date d''expiration du mot de passe'
SilverStripe\Security\MemberAuthenticator\CMSMemberLoginForm:
AUTHENTICATORNAME: 'Formulaire de connexion pour un utilisateur du CMS'
BUTTONFORGOTPASSWORD: 'Mot de passe oublié'
BUTTONLOGIN: 'De retour dans'
BUTTONLOGOUT: 'Se déconnecter'
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
ERRORWRONGCRED: 'Les renseignements fournis semblent incorrects. Merci de réessayer.'
NoPassword: 'Ce membre n''a pas de mot de passe'
SilverStripe\Security\MemberPassword: SilverStripe\Security\MemberPassword:
PLURALNAME: 'Mots de passe du membre' PLURALNAME: 'Mots de passe utilisateur'
SINGULARNAME: 'Mot de passe du membre' PLURALS:
one: 'Un mot de passe utilisateur'
other: '{count} mots de passe utilisateur'
SINGULARNAME: 'Mot de passe utilisateur'
SilverStripe\Security\PasswordValidator: SilverStripe\Security\PasswordValidator:
LOWCHARSTRENGTH: 'Veuillez augmenter la force de votre mot de passe en ajoutant certains caractères suivants : {chars}' LOWCHARSTRENGTH: 'Veuillez renforcer votre mot de passe en ajoutant certains des caractères suivants : {chars}'
PREVPASSWORD: 'Vous avez déjà utilisé ce mot de passe par le passé, veuillez en choisir un autre' PREVPASSWORD: 'Vous avez déjà utilisé ce mot de passe par le passé, veuillez en choisir un autre'
TOOSHORT: 'Le mot de passe est trop court, il doit contenir au moins {minimum} caractères' TOOSHORT: 'Le mot de passe est trop court, il doit contenir au moins {minimum} caractères'
SilverStripe\Security\Permission: SilverStripe\Security\Permission:
AdminGroup: Administrateur AdminGroup: Administrateur
CONTENT_CATEGORY: 'Permissions du contenu' CMS_ACCESS_CATEGORY: 'Accès au CMS'
CONTENT_CATEGORY: 'Permissions de contenu'
FULLADMINRIGHTS: 'Droits d''administration complets' FULLADMINRIGHTS: 'Droits d''administration complets'
FULLADMINRIGHTS_HELP: 'Implique et prévaut sur toutes les autres autorisations assignées.' FULLADMINRIGHTS_HELP: 'Prévaut sur toutes les autres autorisations assignées.'
PERMISSIONS_CATEGORY: 'Rôles et autorisations daccès'
PLURALNAME: Permissions PLURALNAME: Permissions
PLURALS:
one: 'Une autorisation'
other: '{count} autorisations'
SINGULARNAME: Permission 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." 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: SilverStripe\Security\PermissionCheckboxSetField:
AssignedTo: 'assignée au groupe « {title} »' AssignedTo: 'assigné au groupe « {title} »'
FromGroup: "hérité du groupe «\_{title}\_»" FromGroup: "hérite du groupe «\_{title}\_»"
FromRole: "héritée du rôle «\_{title}\_»" FromRole: "hérite du rôle «\_{title}\_»"
FromRoleOnGroup: "hérité du rôle «\_{roletitle}\_» du groupe «\_{grouptitle}\_»" FromRoleOnGroup: "hérite du rôle «\_{roletitle}\_» du groupe «\_{grouptitle}\_»"
SilverStripe\Security\PermissionRole: SilverStripe\Security\PermissionRole:
OnlyAdminCanApply: 'Limité aux administrateurs' OnlyAdminCanApply: 'Limité aux administrateurs'
PLURALNAME: Rôles PLURALNAME: Rôles
PLURALS:
one: 'Un rôle'
other: '{count} rôles'
SINGULARNAME: Rôle SINGULARNAME: Rôle
Title: Titre Title: Titre
SilverStripe\Security\PermissionRoleCode:
PLURALNAME: 'Codes d''autorisations liés au rôle'
PLURALS:
one: 'Un code d''autorisation lié au rôle'
other: '{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:
PLURALNAME: 'Signatures de mot de passe'
PLURALS:
one: 'Une signature de mot de passe'
other: '{count} signatures de mot de passe'
SINGULARNAME: 'Signature de mot de passe'
SilverStripe\Security\Security: SilverStripe\Security\Security:
ALREADYLOGGEDIN: 'Vous n''avez pas accès à cette page. Si vous avez un autre identifiant pouvant accéder à cette page, vous pouvez l''utiliser ci-dessous.' 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.'
BUTTONSEND: 'Envoyer moi le lien pour modifier le mot de passe' BUTTONSEND: 'Envoyer moi le lien pour modifier le mot de passe'
CHANGEPASSWORDBELOW: 'Vous pouvez modifier votre mot de passe ci-dessous.' CHANGEPASSWORDBELOW: 'Vous pouvez modifier votre mot de passe ci-dessous.'
CHANGEPASSWORDHEADER: 'Modifier votre mot de passe' CHANGEPASSWORDHEADER: 'Modifier votre mot de passe'
CONFIRMLOGOUT: 'Merci de cliquer le bouton ci-dessous pour confirmer que vous souhaitez vous déconnecter.'
ENTERNEWPASSWORD: 'Entrer un nouveau mot de passe s''il vous plaît.' ENTERNEWPASSWORD: 'Entrer un nouveau mot de passe s''il vous plaît.'
ERRORPASSWORDPERMISSION: 'Vous devez être connecté pour modifier votre mot de passe !' ERRORPASSWORDPERMISSION: 'Vous devez être connecté pour modifier votre mot de passe !'
LOGIN: Connectez-vous LOGIN: 'Se connecter'
LOGOUT: 'Se déconnecter'
LOSTPASSWORDHEADER: 'Mot de passe oublié' LOSTPASSWORDHEADER: 'Mot de passe oublié'
NOTEPAGESECURED: 'Cette page est sécurisée. Entrer vos détails ci-dessous et nous vous enverrons directement.' NOTEPAGESECURED: 'Cette page est sécurisée. Entrez vos identifiants ci-dessous et vous pourrez y avoir accès.'
NOTERESETLINKINVALID: '<p>Le lien de réinitialisation du mot de passe nest pas valide ou a expiré.</p><p>Vous pouvez en demander un nouveau <a href="{link1}">en suivant ce lien</a> ou changer de mot de passe après <a href="{link2}">connexion</a>.</p>' NOTERESETLINKINVALID: '<p>Le lien de réinitialisation du mot de passe nest pas valide ou a expiré.</p><p>Vous pouvez en demander un nouveau <a href="{link1}">en suivant ce lien</a> ou changer de mot de passe après <a href="{link2}">connexion</a>.</p>'
NOTERESETPASSWORD: 'Entrer votre adresse email et nous vous enverrons un lien pour modifier votre mot de passe' NOTERESETPASSWORD: 'Entrez votre adresse email et nous vous enverrons un lien pour modifier votre mot de passe'
PASSWORDSENTHEADER: "Lien de réinitialisation de mot de passe envoyé à «\_{email}\_»" PASSWORDSENTHEADER: "Lien de réinitialisation de mot de passe envoyé à «\_{email}\_»"
PASSWORDSENTTEXT: "Merci\_! Un lien de réinitialisation vient dêtre envoyé à «\_{email}\_», à condition que cette adresse existe." PASSWORDSENTTEXT: "Merci\_! Un lien de réinitialisation vient dêtre envoyé à «\_{email}\_», à condition que cette adresse existe."

View File

@ -1,7 +1,31 @@
sk: sk:
SilverStripe\Admin\LeftAndMain:
VersionUnknown: neznáma
SilverStripe\AssetAdmin\Forms\UploadField:
Dimensions: Rozmery
EDIT: Editovať
EDITINFO: 'Editovať súbor'
REMOVE: Odstrániť
SilverStripe\Control\ChangePasswordEmail_ss:
CHANGEPASSWORDFOREMAIL: 'Heslo pre účet s e-mailovou adresou {email} bolo zmenené. Ak ste nemenili vaše heslo, tak ho zmeňte pomocou odkazu uvedeného nižšie, prosím.'
CHANGEPASSWORDTEXT1: 'Vaše heslo bolo zmenené pre'
CHANGEPASSWORDTEXT3: 'Zmeniť heslo'
HELLO: Ahoj
SilverStripe\Control\Email\ForgotPasswordEmail_ss:
HELLO: Ahoj
TEXT1: 'Tu je váš'
TEXT2: 'odkaz na resetovanie hesla'
TEXT3: pre
SilverStripe\Control\RequestProcessor:
INVALID_REQUEST: 'Neplatná požiadavka'
REQUEST_ABORTED: 'Požiadavka prerušená'
SilverStripe\Core\Manifest\VersionProvider:
VERSIONUNKNOWN: Neznáma
SilverStripe\Forms\CheckboxField: SilverStripe\Forms\CheckboxField:
NOANSWER: Nie NOANSWER: Nie
YESANSWER: Áno YESANSWER: Áno
SilverStripe\Forms\CheckboxSetField_ss:
NOOPTIONSAVAILABLE: 'Žiadne dostupné možnosti'
SilverStripe\Forms\ConfirmedPasswordField: SilverStripe\Forms\ConfirmedPasswordField:
ATLEAST: 'Heslá musia byť nejmenej {min} znakov dlhé.' ATLEAST: 'Heslá musia byť nejmenej {min} znakov dlhé.'
BETWEEN: 'Heslá musia byť {min} až {max} znakov dlhé.' BETWEEN: 'Heslá musia byť {min} až {max} znakov dlhé.'
@ -34,6 +58,8 @@ sk:
VALIDCURRENCY: 'Prosím zadajte platnú menu' VALIDCURRENCY: 'Prosím zadajte platnú menu'
SilverStripe\Forms\FormField: SilverStripe\Forms\FormField:
NONE: žiadne NONE: žiadne
SilverStripe\Forms\FormScaffolder:
TABMAIN: Hlavné
SilverStripe\Forms\GridField\GridField: SilverStripe\Forms\GridField\GridField:
Add: 'Pridať {name}' Add: 'Pridať {name}'
CSVEXPORT: 'Exportovať do CSV' CSVEXPORT: 'Exportovať do CSV'
@ -66,6 +92,10 @@ sk:
Save: Uložiť Save: Uložiť
SilverStripe\Forms\GridField\GridFieldEditButton_ss: SilverStripe\Forms\GridField\GridFieldEditButton_ss:
EDIT: Editovať EDIT: Editovať
SilverStripe\Forms\GridField\GridFieldPaginator:
Page: Stránka
SilverStripe\Forms\GridField\GridFieldVersionedState:
ONLIVEONLYSHORT: 'Len publikované'
SilverStripe\Forms\MoneyField: SilverStripe\Forms\MoneyField:
FIELDLABELAMOUNT: Množstvo FIELDLABELAMOUNT: Množstvo
FIELDLABELCURRENCY: Mena FIELDLABELCURRENCY: Mena
@ -104,6 +134,8 @@ sk:
INVALIDUSER: '<p>Neplatný užívateľ. <a target="_top" href="{link}">Prosím overte sa znovu tu</a> pre pokračovanie.</p>' INVALIDUSER: '<p>Neplatný užívateľ. <a target="_top" href="{link}">Prosím overte sa znovu tu</a> pre pokračovanie.</p>'
SUCCESS: Úspešné SUCCESS: Úspešné
SUCCESSCONTENT: '<p>Úspešné prihlásenie. Ak nebudete automaticky presmerovaní <a target="_top" href="{link}">kliknite tu</a></p>' SUCCESSCONTENT: '<p>Úspešné prihlásenie. Ak nebudete automaticky presmerovaní <a target="_top" href="{link}">kliknite tu</a></p>'
SilverStripe\Security\DefaultAdminService:
DefaultAdminFirstname: 'Predvolený administrátor'
SilverStripe\Security\Group: SilverStripe\Security\Group:
AddRole: 'Pridať úlohu pre túto skupinu' AddRole: 'Pridať úlohu pre túto skupinu'
Code: 'Kód skupiny' Code: 'Kód skupiny'
@ -113,6 +145,7 @@ sk:
GroupReminder: 'Ak vyberiete nadriadenú skupinu, bude táto skupina mať všetky úlohy' GroupReminder: 'Ak vyberiete nadriadenú skupinu, bude táto skupina mať všetky úlohy'
HierarchyPermsError: 'Nie je možné pripojiť nadriadenú skupinu "{group}" s privilegovanými právami (vyžaduje ADMIN prístup)' HierarchyPermsError: 'Nie je možné pripojiť nadriadenú skupinu "{group}" s privilegovanými právami (vyžaduje ADMIN prístup)'
Locked: 'Zamknuté?' Locked: 'Zamknuté?'
MEMBERS: Členovia
NoRoles: 'Nenašli sa úlohy' NoRoles: 'Nenašli sa úlohy'
PLURALNAME: Skupiny PLURALNAME: Skupiny
Parent: 'Nadradená skupina' Parent: 'Nadradená skupina'
@ -168,7 +201,7 @@ sk:
SINGULARNAME: 'Heslo člena' SINGULARNAME: 'Heslo člena'
SilverStripe\Security\PasswordValidator: SilverStripe\Security\PasswordValidator:
LOWCHARSTRENGTH: 'Prosím posilnite heslo pridaním z týchto niektorých znakov: {chars}' LOWCHARSTRENGTH: 'Prosím posilnite heslo pridaním z týchto niektorých znakov: {chars}'
PREVPASSWORD: 'Už ste použili toto heslo v minulosti, vyberte nové hoslo, prosím' PREVPASSWORD: 'Už ste použili toto heslo v minulosti, vyberte nové heslo, prosím'
TOOSHORT: 'Heslo je príliš krátke, musí byť {minimum} alebo viacej znakov dlhé' TOOSHORT: 'Heslo je príliš krátke, musí byť {minimum} alebo viacej znakov dlhé'
SilverStripe\Security\Permission: SilverStripe\Security\Permission:
AdminGroup: Administrátor AdminGroup: Administrátor

View File

@ -260,7 +260,9 @@ class CSVParser implements Iterator
array($this->enclosure, $this->delimiter), array($this->enclosure, $this->delimiter),
$value $value
); );
// Trim leading tab
// [SS-2017-007] Ensure all cells with leading [@=+] have a leading tab
$value = ltrim($value, "\t");
if (array_key_exists($i, $this->headerRow)) { if (array_key_exists($i, $this->headerRow)) {
if ($this->headerRow[$i]) { if ($this->headerRow[$i]) {
$row[$this->headerRow[$i]] = $value; $row[$this->headerRow[$i]] = $value;

View File

@ -26,18 +26,23 @@ class InstallConfig
* *
* @param array $request Request object * @param array $request Request object
* @param array $databaseClasses Supported database config * @param array $databaseClasses Supported database config
* @param bool $realPassword Set to true to get the real password. If false, any non-posted
* password will be redacted as '********'. Note: Posted passwords are considered disclosed and
* never redacted.
* @return array * @return array
*/ */
public function getDatabaseConfig($request, $databaseClasses) public function getDatabaseConfig($request, $databaseClasses, $realPassword = true)
{ {
// Get config from request // Get config from request
if (isset($request['db']['type'])) { if (isset($request['db']['type'])) {
$type = $request['db']['type']; $type = $request['db']['type'];
if (isset($request['db'][$type])) { if (isset($request['db'][$type])) {
return array_merge( $config = $request['db'][$type];
[ 'type' => $type ], // The posted placeholder must be substituted with the real password
$request['db'][$type] if (isset($config['password']) && $config['password'] === Installer::PASSWORD_PLACEHOLDER) {
); $config['password'] = Environment::getEnv('SS_DATABASE_PASSWORD') ?: '';
}
return array_merge([ 'type' => $type ], $config);
} }
} }
@ -46,8 +51,16 @@ class InstallConfig
'type' => $this->getDatabaseClass($databaseClasses), 'type' => $this->getDatabaseClass($databaseClasses),
'server' => Environment::getEnv('SS_DATABASE_SERVER') ?: 'localhost', 'server' => Environment::getEnv('SS_DATABASE_SERVER') ?: 'localhost',
'username' => Environment::getEnv('SS_DATABASE_USERNAME') ?: 'root', 'username' => Environment::getEnv('SS_DATABASE_USERNAME') ?: 'root',
'password' => Environment::getEnv('SS_DATABASE_PASSWORD') ?: '', 'password' => $realPassword
? (Environment::getEnv('SS_DATABASE_PASSWORD') ?: '')
: Installer::PASSWORD_PLACEHOLDER, // Avoid password disclosure
'database' => Environment::getEnv('SS_DATABASE_NAME') ?: 'SS_mysite', 'database' => Environment::getEnv('SS_DATABASE_NAME') ?: 'SS_mysite',
'path' => Environment::getEnv('SS_DATABASE_PATH')
?: Environment::getEnv('SS_SQLITE_DATABASE_PATH') // sqlite compat
?: null,
'key' => Environment::getEnv('SS_DATABASE_KEY')
?: Environment::getEnv('SS_SQLITE_DATABASE_KEY') // sqlite compat
?: null,
]; ];
} }
@ -55,17 +68,26 @@ class InstallConfig
* Get admin config from the environment * Get admin config from the environment
* *
* @param array $request * @param array $request
* @param bool $realPassword Set to true to get the real password. If false, any non-posted
* password will be redacted as '********'. Note: Posted passwords are considered disclosed and
* never redacted.
* @return array * @return array
*/ */
public function getAdminConfig($request) public function getAdminConfig($request, $realPassword = true)
{ {
if (isset($request['admin'])) { if (isset($request['admin'])) {
$config = $request['admin'];
if (isset($config['password']) && $config['password'] === Installer::PASSWORD_PLACEHOLDER) {
$config['password'] = Environment::getEnv('SS_DEFAULT_ADMIN_PASSWORD') ?: '';
}
return $request['admin']; return $request['admin'];
} }
return [ return [
'username' => Environment::getEnv('SS_DEFAULT_ADMIN_USERNAME') ?: 'admin', 'username' => Environment::getEnv('SS_DEFAULT_ADMIN_USERNAME') ?: 'admin',
'password' => Environment::getEnv('SS_DEFAULT_ADMIN_PASSWORD') ?: '', 'password' => $realPassword
? (Environment::getEnv('SS_DEFAULT_ADMIN_PASSWORD') ?: '')
: Installer::PASSWORD_PLACEHOLDER, // Avoid password disclosure
]; ];
} }

View File

@ -20,6 +20,11 @@ use SilverStripe\Security\Security;
*/ */
class Installer extends InstallRequirements class Installer extends InstallRequirements
{ {
/**
* value='' attribute placeholder for password fields
*/
const PASSWORD_PLACEHOLDER = '********';
protected function installHeader() protected function installHeader()
{ {
?> ?>

View File

@ -123,13 +123,14 @@
echo $checked ? '<div class="dbfields">' : '<div class="dbfields" style="display: none;">'; echo $checked ? '<div class="dbfields">' : '<div class="dbfields" style="display: none;">';
if(isset($details['fields'])) foreach($details['fields'] as $fieldName => $fieldSpec) { if(isset($details['fields'])) foreach($details['fields'] as $fieldName => $fieldSpec) {
$fieldTitle = $fieldSpec['title']; $fieldTitle = $fieldSpec['title'];
$fieldType = ($fieldName == 'password') ? 'password' : 'text'; $fieldType = ($fieldName === 'password') ? 'password' : 'text';
// values // Get display value
$defaultValue = (isset($fieldSpec['default'])) ? $fieldSpec['default'] : null; if (isset($databaseConfig[$fieldName]) && $databaseConfig['type'] == $class) {
if($usingEnv && isset($fieldSpec['envVar']) && $envVar = getenv($fieldSpec['envVar'])) { $value = $databaseConfig[$fieldName];
$value = $envVar; } elseif (isset($fieldSpec['default'])) {
$value = $fieldSpec['default'];
} else { } else {
$value = (isset($databaseConfig[$fieldName]) && $databaseConfig['type'] == $class) ? $databaseConfig[$fieldName] : $defaultValue; $value = null;
} }
// attributes // attributes

View File

@ -56,8 +56,8 @@ foreach ($databaseClasses as $class => $details) {
// Build config from config / environment / request // Build config from config / environment / request
$config = new InstallConfig(); $config = new InstallConfig();
$databaseConfig = $config->getDatabaseConfig($_REQUEST, $databaseClasses); $databaseConfig = $config->getDatabaseConfig($_REQUEST, $databaseClasses, true);
$adminConfig = $config->getAdminConfig($_REQUEST); $adminConfig = $config->getAdminConfig($_REQUEST, true);
$alreadyInstalled = $config->alreadyInstalled(); $alreadyInstalled = $config->alreadyInstalled();
$silverstripe_version = $config->getFrameworkVersion(); $silverstripe_version = $config->getFrameworkVersion();
$sendStats = $config->canSendStats($_REQUEST); $sendStats = $config->canSendStats($_REQUEST);
@ -107,22 +107,6 @@ if ($installFromCli && ($req->hasErrors() || $dbReq->hasErrors())) {
// Path to client resources (copied through silverstripe/vendor-plugin) // Path to client resources (copied through silverstripe/vendor-plugin)
$clientPath = 'resources/silverstripe/framework/src/Dev/Install/client'; $clientPath = 'resources/silverstripe/framework/src/Dev/Install/client';
// config-form.html vars (placeholder to prevent deletion)
[
$theme,
$clientPath,
$adminConfig,
$usingEnv,
$silverstripe_version,
$locale,
$locales,
$webserverConfigFile,
$hasErrorOtherThanDatabase,
$hasOnlyWarnings, // If warnings but not errors
$phpIniLocation
];
// If already installed, ensure the user clicked "reinstall" // If already installed, ensure the user clicked "reinstall"
$expectedArg = $alreadyInstalled ? 'reinstall' : 'go'; $expectedArg = $alreadyInstalled ? 'reinstall' : 'go';
if ((isset($_REQUEST[$expectedArg]) || $installFromCli) if ((isset($_REQUEST[$expectedArg]) || $installFromCli)
@ -142,7 +126,27 @@ if ((isset($_REQUEST[$expectedArg]) || $installFromCli)
'admin' => $adminConfig, 'admin' => $adminConfig,
'stats' => $sendStats, 'stats' => $sendStats,
]); ]);
// Show the config form return;
} else {
include(__DIR__ . '/config-form.html');
} }
// Sanitise config prior to rendering config-form.html
$databaseConfig = $config->getDatabaseConfig($_REQUEST, $databaseClasses, false);
$adminConfig = $config->getAdminConfig($_REQUEST, false);
// config-form.html vars (placeholder to prevent deletion)
[
$theme,
$clientPath,
$adminConfig,
$databaseConfig,
$usingEnv,
$silverstripe_version,
$locale,
$locales,
$webserverConfigFile,
$hasErrorOtherThanDatabase,
$hasOnlyWarnings, // If warnings but not errors
$phpIniLocation,
];
include(__DIR__ . '/config-form.html');

View File

@ -4,6 +4,7 @@ namespace SilverStripe\Forms\GridField;
use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Config\Config;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
/** /**
@ -11,7 +12,6 @@ use SilverStripe\ORM\DataObject;
*/ */
class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler
{ {
/** /**
* @var array Map of a property name on the exported objects, with values being the column title in the CSV file. * @var array Map of a property name on the exported objects, with values being the column title in the CSV file.
* Note that titles are only used when {@link $csvHasHeader} is set to TRUE. * Note that titles are only used when {@link $csvHasHeader} is set to TRUE.
@ -38,6 +38,15 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
*/ */
protected $targetFragment; protected $targetFragment;
/**
* Set to true to disable XLS sanitisation
* [SS-2017-007] Ensure all cells with leading [@=+] have a leading tab
*
* @config
* @var bool
*/
private static $xls_export_disabled = false;
/** /**
* @param string $targetFragment The HTML fragment to write the button into * @param string $targetFragment The HTML fragment to write the button into
* @param array $exportColumns The columns to include in the export * @param array $exportColumns The columns to include in the export
@ -170,7 +179,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
} }
//Remove GridFieldPaginator as we're going to export the entire list. //Remove GridFieldPaginator as we're going to export the entire list.
$gridField->getConfig()->removeComponentsByType('SilverStripe\\Forms\\GridField\\GridFieldPaginator'); $gridField->getConfig()->removeComponentsByType(GridFieldPaginator::class);
$items = $gridField->getManipulatedList(); $items = $gridField->getManipulatedList();
@ -203,6 +212,12 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
} }
} }
// [SS-2017-007] Sanitise XLS executable column values with a leading tab
if (!Config::inst()->get(get_class($this), 'xls_export_disabled')
&& preg_match('/^[-@=+].*/', $value)
) {
$value = "\t" . $value;
}
$columnData[] = $value; $columnData[] = $value;
} }

View File

@ -34,7 +34,7 @@ class TinyMCEConfig extends HTMLEditorConfig
'cy_GB' => 'cy', 'cy_GB' => 'cy',
'da_DK' => 'da', 'da_DK' => 'da',
'da_GL' => 'da', 'da_GL' => 'da',
'de_AT' => 'de', 'de_AT' => 'de_AT',
'de_BE' => 'de', 'de_BE' => 'de',
'de_CH' => 'de', 'de_CH' => 'de',
'de_DE' => 'de', 'de_DE' => 'de',
@ -56,7 +56,7 @@ class TinyMCEConfig extends HTMLEditorConfig
'es_GQ' => 'es', 'es_GQ' => 'es',
'es_GT' => 'es', 'es_GT' => 'es',
'es_HN' => 'es', 'es_HN' => 'es',
'es_MX' => 'es', 'es_MX' => 'es_MX',
'es_NI' => 'es', 'es_NI' => 'es',
'es_PA' => 'es', 'es_PA' => 'es',
'es_PE' => 'es', 'es_PE' => 'es',
@ -69,11 +69,9 @@ class TinyMCEConfig extends HTMLEditorConfig
'es_AD' => 'es', 'es_AD' => 'es',
'es_BZ' => 'es', 'es_BZ' => 'es',
'es_US' => 'es', 'es_US' => 'es',
'fa_AF' => 'fa', 'fa_AF' => 'fa_IR',
'fa_IR' => 'fa', 'fa_IR' => 'fa_IR',
'fa_PK' => 'fa', 'fa_PK' => 'fa_IR',
'fi_FI' => 'fi',
'fi_SE' => 'fi',
'fr_BE' => 'fr_FR', 'fr_BE' => 'fr_FR',
'fr_BF' => 'fr_FR', 'fr_BF' => 'fr_FR',
'fr_BI' => 'fr_FR', 'fr_BI' => 'fr_FR',
@ -117,12 +115,11 @@ class TinyMCEConfig extends HTMLEditorConfig
'fr_YT' => 'fr_FR', 'fr_YT' => 'fr_FR',
'fr_GB' => 'fr_FR', 'fr_GB' => 'fr_FR',
'fr_US' => 'fr_FR', 'fr_US' => 'fr_FR',
'he_IL' => 'he', 'he_IL' => 'he_IL',
'hu_HU' => 'hu', 'hu_HU' => 'hu_HU',
'hu_AT' => 'hu', 'hu_AT' => 'hu_HU',
'hu_RO' => 'hu', 'hu_RO' => 'hu_HU',
'hu_RS' => 'hu', 'hu_RS' => 'hu_HU',
'is_IS' => 'is',
'it_CH' => 'it', 'it_CH' => 'it',
'it_IT' => 'it', 'it_IT' => 'it',
'it_SM' => 'it', 'it_SM' => 'it',
@ -131,28 +128,26 @@ class TinyMCEConfig extends HTMLEditorConfig
'it_US' => 'it', 'it_US' => 'it',
'it_VA' => 'it', 'it_VA' => 'it',
'ja_JP' => 'ja', 'ja_JP' => 'ja',
'ko_KP' => 'ko', 'ko_KP' => 'ko_KR',
'ko_KR' => 'ko', 'ko_KR' => 'ko_KR',
'ko_CN' => 'ko', 'ko_CN' => 'ko_KR',
'mi_NZ' => 'mi_NZ', 'nb_NO' => 'nb_NO',
'nb_NO' => 'nb', 'nb_SJ' => 'nb_NO',
'nb_SJ' => 'nb',
'nl_AN' => 'nl', 'nl_AN' => 'nl',
'nl_AW' => 'nl', 'nl_AW' => 'nl',
'nl_BE' => 'nl', 'nl_BE' => 'nl',
'nl_NL' => 'nl', 'nl_NL' => 'nl',
'nl_SR' => 'nl', 'nl_SR' => 'nl',
'nn_NO' => 'nn',
'pl_PL' => 'pl', 'pl_PL' => 'pl',
'pl_UA' => 'pl', 'pl_UA' => 'pl',
'pt_AO' => 'pt', 'pt_AO' => 'pt_PT',
'pt_BR' => 'pt', 'pt_BR' => 'pt_BR',
'pt_CV' => 'pt', 'pt_CV' => 'pt_PT',
'pt_GW' => 'pt', 'pt_GW' => 'pt_PT',
'pt_MZ' => 'pt', 'pt_MZ' => 'pt_PT',
'pt_PT' => 'pt', 'pt_PT' => 'pt_PT',
'pt_ST' => 'pt', 'pt_ST' => 'pt_PT',
'pt_TL' => 'pt', 'pt_TL' => 'pt_PT',
'ro_MD' => 'ro', 'ro_MD' => 'ro',
'ro_RO' => 'ro', 'ro_RO' => 'ro',
'ro_RS' => 'ro', 'ro_RS' => 'ro',
@ -162,31 +157,26 @@ class TinyMCEConfig extends HTMLEditorConfig
'ru_RU' => 'ru', 'ru_RU' => 'ru',
'ru_SJ' => 'ru', 'ru_SJ' => 'ru',
'ru_UA' => 'ru', 'ru_UA' => 'ru',
'si_LK' => 'si',
'sk_SK' => 'sk', 'sk_SK' => 'sk',
'sk_RS' => 'sk', 'sk_RS' => 'sk',
'sq_AL' => 'sq', 'sv_FI' => 'sv_SE',
'sr_BA' => 'sr', 'sv_SE' => 'sv_SE',
'sr_ME' => 'sr',
'sr_RS' => 'sr',
'sv_FI' => 'sv',
'sv_SE' => 'sv',
'tr_CY' => 'tr', 'tr_CY' => 'tr',
'tr_TR' => 'tr', 'tr_TR' => 'tr_TR',
'tr_DE' => 'tr', 'tr_DE' => 'tr',
'tr_MK' => 'tr', 'tr_MK' => 'tr',
'uk_UA' => 'uk', 'uk_UA' => 'uk_UA',
'vi_VN' => 'vi', 'vi_VN' => 'vi_VN',
'vi_US' => 'vi', 'vi_US' => 'vi_VN',
'zh_CN' => 'zh-cn', 'zh_CN' => 'zh_CN',
'zh_HK' => 'zh-cn', 'zh_HK' => 'zh_CN',
'zh_MO' => 'zh-cn', 'zh_MO' => 'zh_CN',
'zh_SG' => 'zh-cn', 'zh_SG' => 'zh_CN',
'zh_TW' => 'zh-tw', 'zh_TW' => 'zh_TW',
'zh_ID' => 'zh-cn', 'zh_ID' => 'zh_CN',
'zh_MY' => 'zh-cn', 'zh_MY' => 'zh_CN',
'zh_TH' => 'zh-cn', 'zh_TH' => 'zh_CN',
'zh_US' => 'zn-cn', 'zh_US' => 'zh_CN',
]; ];
/** /**

View File

@ -2,6 +2,8 @@
namespace SilverStripe\ORM\Connect; namespace SilverStripe\ORM\Connect;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Convert; use SilverStripe\Core\Convert;
use SilverStripe\ORM\PaginatedList; use SilverStripe\ORM\PaginatedList;
@ -144,7 +146,7 @@ class MySQLDatabase extends Database
* @param bool $booleanSearch * @param bool $booleanSearch
* @param string $alternativeFileFilter * @param string $alternativeFileFilter
* @param bool $invertedMatch * @param bool $invertedMatch
* @return \SilverStripe\ORM\PaginatedList * @return PaginatedList
* @throws Exception * @throws Exception
*/ */
public function searchEngine( public function searchEngine(
@ -158,10 +160,8 @@ class MySQLDatabase extends Database
$alternativeFileFilter = "", $alternativeFileFilter = "",
$invertedMatch = false $invertedMatch = false
) { ) {
$pageClass = 'SilverStripe\\CMS\\Model\\SiteTree'; $pageClass = SiteTree::class;
$fileClass = 'SilverStripe\\Assets\\File'; $fileClass = File::class;
$pageTable = DataObject::getSchema()->tableName($pageClass);
$fileTable = DataObject::getSchema()->tableName($fileClass);
if (!class_exists($pageClass)) { if (!class_exists($pageClass)) {
throw new Exception('MySQLDatabase->searchEngine() requires "SiteTree" class'); throw new Exception('MySQLDatabase->searchEngine() requires "SiteTree" class');
} }
@ -194,12 +194,13 @@ class MySQLDatabase extends Database
// File.ShowInSearch was added later, keep the database driver backwards compatible // File.ShowInSearch was added later, keep the database driver backwards compatible
// by checking for its existence first // by checking for its existence first
$fileTable = DataObject::getSchema()->tableName($fileClass);
$fields = $this->getSchemaManager()->fieldList($fileTable); $fields = $this->getSchemaManager()->fieldList($fileTable);
if (array_key_exists('ShowInSearch', $fields)) { if (array_key_exists('ShowInSearch', $fields)) {
$extraFilters[$fileClass] .= " AND ShowInSearch <> 0"; $extraFilters[$fileClass] .= " AND ShowInSearch <> 0";
} }
$limit = $start . ", " . (int) $pageLength; $limit = (int)$start . ", " . (int)$pageLength;
$notMatch = $invertedMatch $notMatch = $invertedMatch
? "NOT " ? "NOT "
@ -257,7 +258,6 @@ class MySQLDatabase extends Database
$queryParameters = array(); $queryParameters = array();
$totalCount = 0; $totalCount = 0;
foreach ($lists as $class => $list) { foreach ($lists as $class => $list) {
$table = DataObject::getSchema()->tableName($class);
/** @var SQLSelect $query */ /** @var SQLSelect $query */
$query = $list->dataQuery()->query(); $query = $list->dataQuery()->query();

View File

@ -2,35 +2,36 @@
namespace SilverStripe\ORM; namespace SilverStripe\ORM;
use BadMethodCallException;
use Exception;
use InvalidArgumentException;
use LogicException;
use SilverStripe\Control\HTTP;
use SilverStripe\Core\ClassInfo; use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Resettable; use SilverStripe\Core\Resettable;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Dev\Debug; use SilverStripe\Dev\Debug;
use SilverStripe\Control\HTTP; use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FormField; use SilverStripe\Forms\FormField;
use SilverStripe\Forms\FormScaffolder; use SilverStripe\Forms\FormScaffolder;
use SilverStripe\i18n\i18n; use SilverStripe\i18n\i18n;
use SilverStripe\i18n\i18nEntityProvider; use SilverStripe\i18n\i18nEntityProvider;
use SilverStripe\ORM\Connect\MySQLSchemaManager; use SilverStripe\ORM\Connect\MySQLSchemaManager;
use SilverStripe\ORM\Filters\SearchFilter;
use SilverStripe\ORM\Search\SearchContext;
use SilverStripe\ORM\Queries\SQLInsert;
use SilverStripe\ORM\Queries\SQLDelete;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBComposite;
use SilverStripe\ORM\FieldType\DBClassName; use SilverStripe\ORM\FieldType\DBClassName;
use SilverStripe\ORM\FieldType\DBComposite;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\Filters\SearchFilter;
use SilverStripe\ORM\Queries\SQLDelete;
use SilverStripe\ORM\Queries\SQLInsert;
use SilverStripe\ORM\Search\SearchContext;
use SilverStripe\Security\Member; use SilverStripe\Security\Member;
use SilverStripe\Security\Permission; use SilverStripe\Security\Permission;
use SilverStripe\Security\Security; use SilverStripe\Security\Security;
use SilverStripe\View\SSViewer;
use SilverStripe\View\ViewableData; use SilverStripe\View\ViewableData;
use LogicException;
use InvalidArgumentException;
use BadMethodCallException;
use Exception;
use stdClass; use stdClass;
/** /**
@ -2154,6 +2155,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
return $untabbedFields; return $untabbedFields;
} }
public function getViewerTemplates($suffix = '')
{
return SSViewer::get_templates_by_class(static::class, $suffix, $this->baseClass());
}
/** /**
* Gets the value of a field. * Gets the value of a field.
* Called by {@link __get()} and any getFieldName() methods you might create. * Called by {@link __get()} and any getFieldName() methods you might create.

View File

@ -138,11 +138,11 @@ class DataObjectSchema
*/ */
public function baseDataClass($class) public function baseDataClass($class)
{ {
$class = ClassInfo::class_name($class);
$current = $class; $current = $class;
while ($next = get_parent_class($current)) { while ($next = get_parent_class($current)) {
if ($next === DataObject::class) { if ($next === DataObject::class) {
return $current; // Only use ClassInfo::class_name() to format the class if we've not used get_parent_class()
return ($current === $class) ? ClassInfo::class_name($current) : $current;
} }
$current = $next; $current = $next;
} }
@ -201,6 +201,11 @@ class DataObjectSchema
$db = []; $db = [];
$classes = $uninherited ? [$class] : ClassInfo::ancestry($class); $classes = $uninherited ? [$class] : ClassInfo::ancestry($class);
foreach ($classes as $tableClass) { foreach ($classes as $tableClass) {
// Skip irrelevant parent classes
if (!is_subclass_of($tableClass, DataObject::class)) {
continue;
}
// Find all fields on this class // Find all fields on this class
$fields = $this->databaseFields($tableClass, false); $fields = $this->databaseFields($tableClass, false);
// Merge with composite fields // Merge with composite fields
@ -394,6 +399,10 @@ class DataObjectSchema
*/ */
public function classHasTable($class) public function classHasTable($class)
{ {
if (!is_subclass_of($class, DataObject::class)) {
return false;
}
$fields = $this->databaseFields($class, false); $fields = $this->databaseFields($class, false);
return !empty($fields); return !empty($fields);
} }

View File

@ -87,7 +87,7 @@ class PaginatedList extends ListDecorator
*/ */
public function setPageLength($length) public function setPageLength($length)
{ {
$this->pageLength = $length; $this->pageLength = (int)$length;
return $this; return $this;
} }
@ -99,7 +99,7 @@ class PaginatedList extends ListDecorator
*/ */
public function setCurrentPage($page) public function setCurrentPage($page)
{ {
$this->pageStart = ($page - 1) * $this->getPageLength(); $this->pageStart = ((int)$page - 1) * $this->getPageLength();
return $this; return $this;
} }
@ -134,7 +134,7 @@ class PaginatedList extends ListDecorator
*/ */
public function setPageStart($start) public function setPageStart($start)
{ {
$this->pageStart = $start; $this->pageStart = (int)$start;
return $this; return $this;
} }
@ -161,7 +161,7 @@ class PaginatedList extends ListDecorator
*/ */
public function setTotalItems($items) public function setTotalItems($items)
{ {
$this->totalItems = $items; $this->totalItems = (int)$items;
return $this; return $this;
} }

View File

@ -2,6 +2,7 @@
namespace SilverStripe\Security; namespace SilverStripe\Security;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
/** /**
@ -14,11 +15,11 @@ use SilverStripe\ORM\DataObject;
* complies with your privacy standards. We're logging * complies with your privacy standards. We're logging
* username and IP. * username and IP.
* *
* @property string Email Email address used for login attempt * @property string $Email Email address used for login attempt. @deprecated 3.0...5.0
* @property string Status Status of the login attempt, either 'Success' or 'Failure' * @property string $EmailHashed sha1 hashed Email address used for login attempt
* @property string IP IP address of user attempting to login * @property string $Status Status of the login attempt, either 'Success' or 'Failure'
* * @property string $IP IP address of user attempting to login
* @property int MemberID ID of the Member, only if Member with Email exists * @property int $MemberID ID of the Member, only if Member with Email exists
* *
* @method Member Member() Member object of the user trying to log in, only if Member with Email exists * @method Member Member() Member object of the user trying to log in, only if Member with Email exists
*/ */
@ -35,7 +36,8 @@ class LoginAttempt extends DataObject
const FAILURE = 'Failure'; const FAILURE = 'Failure';
private static $db = array( private static $db = array(
'Email' => 'Varchar(255)', 'Email' => 'Varchar(255)', // Remove in 5.0
'EmailHashed' => 'Varchar(255)',
'Status' => "Enum('Success,Failure')", 'Status' => "Enum('Success,Failure')",
'IP' => 'Varchar(255)', 'IP' => 'Varchar(255)',
); );
@ -55,9 +57,37 @@ class LoginAttempt extends DataObject
{ {
$labels = parent::fieldLabels($includerelations); $labels = parent::fieldLabels($includerelations);
$labels['Email'] = _t(__CLASS__.'.Email', 'Email Address'); $labels['Email'] = _t(__CLASS__.'.Email', 'Email Address');
$labels['EmailHashed'] = _t(__CLASS__.'.EmailHashed', 'Email Address (hashed)');
$labels['Status'] = _t(__CLASS__.'.Status', 'Status'); $labels['Status'] = _t(__CLASS__.'.Status', 'Status');
$labels['IP'] = _t(__CLASS__.'.IP', 'IP Address'); $labels['IP'] = _t(__CLASS__.'.IP', 'IP Address');
return $labels; return $labels;
} }
/**
* Set email used for this attempt
*
* @param string $email
* @return $this
*/
public function setEmail($email)
{
// Store hashed email only
$this->EmailHashed = sha1($email);
return $this;
}
/**
* Get all login attempts for the given email address
*
* @param string $email
* @return DataList|LoginAttempt[]
*/
public static function getByEmail($email)
{
return static::get()->filterAny(array(
'Email' => $email,
'EmailHashed' => sha1($email),
));
}
} }

View File

@ -384,8 +384,7 @@ class Member extends DataObject
} }
$idField = static::config()->get('unique_identifier_field'); $idField = static::config()->get('unique_identifier_field');
$attempts = LoginAttempt::get() $attempts = LoginAttempt::getByEmail($this->{$idField})
->filter('Email', $this->{$idField})
->sort('Created', 'DESC') ->sort('Created', 'DESC')
->limit($maxAttempts); ->limit($maxAttempts);

View File

@ -2,25 +2,26 @@
namespace SilverStripe\View; namespace SilverStripe\View;
use ArrayIterator;
use Exception; use Exception;
use InvalidArgumentException;
use IteratorAggregate;
use LogicException;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Extensible; use SilverStripe\Core\Extensible;
use SilverStripe\Core\Injector\Injectable; use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Manifest\ModuleResourceLoader; use SilverStripe\Core\Manifest\ModuleResourceLoader;
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\ArrayLib; use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\Core\ClassInfo; use SilverStripe\View\SSViewer;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Debug;
use IteratorAggregate;
use LogicException;
use InvalidArgumentException;
use UnexpectedValueException; use UnexpectedValueException;
use ArrayIterator;
/** /**
* A ViewableData object is any object that can be rendered into a template/view. * A ViewableData object is any object that can be rendered into a template/view.
@ -578,6 +579,17 @@ class ViewableData implements IteratorAggregate
// UTILITY METHODS ------------------------------------------------------------------------------------------------- // UTILITY METHODS -------------------------------------------------------------------------------------------------
/**
* Find appropriate templates for SSViewer to use to render this object
*
* @param string $suffix
* @return array
*/
public function getViewerTemplates($suffix = '')
{
return SSViewer::get_templates_by_class(static::class, $suffix, self::class);
}
/** /**
* When rendering some objects it is necessary to iterate over the object being rendered, to do this, you need * When rendering some objects it is necessary to iterate over the object being rendered, to do this, you need
* access to itself. * access to itself.

View File

@ -29,31 +29,44 @@ class CSVParserTest extends SapphireTest
$firstNames = $birthdays = $biographies = $registered = array(); $firstNames = $birthdays = $biographies = $registered = array();
foreach ($csv as $record) { foreach ($csv as $record) {
/* Each row in the CSV file will be keyed with the header row */ /* Each row in the CSV file will be keyed with the header row */
$this->assertEquals(array('FirstName','Biography','Birthday','IsRegistered'), array_keys($record)); $this->assertEquals(
['FirstName','Biography','Birthday','IsRegistered'],
array_keys($record)
);
$firstNames[] = $record['FirstName']; $firstNames[] = $record['FirstName'];
$biographies[] = $record['Biography']; $biographies[] = $record['Biography'];
$birthdays[] = $record['Birthday']; $birthdays[] = $record['Birthday'];
$registered[] = $record['IsRegistered']; $registered[] = $record['IsRegistered'];
} }
$this->assertEquals(array('John','Jane','Jamie','Järg'), $firstNames); $this->assertEquals(
['John','Jane','Jamie','Järg','Jacob'],
$firstNames
);
$this->assertEquals( $this->assertEquals(
array( [
"He's a good guy", "He's a good guy",
"She is awesome." . PHP_EOL "She is awesome."
. PHP_EOL
. "So awesome that she gets multiple rows and \"escaped\" strings in her biography", . "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"Pretty old, with an escaped comma", "Pretty old, with an escaped comma",
"Unicode FTW"), "Unicode FTW",
"Likes leading tabs in his biography",
],
$biographies $biographies
); );
$this->assertEquals([ $this->assertEquals([
"1988-01-31", "1988-01-31",
"1982-01-31", "1982-01-31",
"1882-01-31", "1882-01-31",
"1982-06-30" "1982-06-30",
"2000-04-30",
], $birthdays); ], $birthdays);
$this->assertEquals(array('1', '0', '1', '1'), $registered); $this->assertEquals(
['1', '0', '1', '1', '0'],
$registered
);
} }
public function testParsingWithHeadersAndColumnMap() public function testParsingWithHeadersAndColumnMap()
@ -62,41 +75,43 @@ class CSVParserTest extends SapphireTest
$csv = new CSVParser($this->csvPath . 'PlayersWithHeader.csv'); $csv = new CSVParser($this->csvPath . 'PlayersWithHeader.csv');
/* We can set up column remapping. The keys are case-insensitive. */ /* We can set up column remapping. The keys are case-insensitive. */
$csv->mapColumns( $csv->mapColumns([
array(
'FirstName' => '__fn', 'FirstName' => '__fn',
'bIoGrApHy' => '__BG', 'bIoGrApHy' => '__BG',
) ]);
);
$firstNames = $birthdays = $biographies = $registered = array(); $firstNames = $birthdays = $biographies = $registered = array();
foreach ($csv as $record) { foreach ($csv as $record) {
/* Each row in the CSV file will be keyed with the renamed columns. Any unmapped column names will be /* Each row in the CSV file will be keyed with the renamed columns. Any unmapped column names will be
* left as-is. */ * left as-is. */
$this->assertEquals(array('__fn','__BG','Birthday','IsRegistered'), array_keys($record)); $this->assertEquals(['__fn','__BG','Birthday','IsRegistered'], array_keys($record));
$firstNames[] = $record['__fn']; $firstNames[] = $record['__fn'];
$biographies[] = $record['__BG']; $biographies[] = $record['__BG'];
$birthdays[] = $record['Birthday']; $birthdays[] = $record['Birthday'];
$registered[] = $record['IsRegistered']; $registered[] = $record['IsRegistered'];
} }
$this->assertEquals(array('John','Jane','Jamie','Järg'), $firstNames); $this->assertEquals(['John','Jane','Jamie','Järg','Jacob'], $firstNames);
$this->assertEquals( $this->assertEquals(
array( [
"He's a good guy", "He's a good guy",
"She is awesome." "She is awesome."
. PHP_EOL . "So awesome that she gets multiple rows and \"escaped\" strings in her biography", . PHP_EOL
. "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"Pretty old, with an escaped comma", "Pretty old, with an escaped comma",
"Unicode FTW"), "Unicode FTW",
"Likes leading tabs in his biography",
],
$biographies $biographies
); );
$this->assertEquals([ $this->assertEquals([
"1988-01-31", "1988-01-31",
"1982-01-31", "1982-01-31",
"1882-01-31", "1882-01-31",
"1982-06-30" "1982-06-30",
"2000-04-30",
], $birthdays); ], $birthdays);
$this->assertEquals(array('1', '0', '1', '1'), $registered); $this->assertEquals(array('1', '0', '1', '1', '0'), $registered);
} }
public function testParsingWithExplicitHeaderRow() public function testParsingWithExplicitHeaderRow()
@ -117,15 +132,18 @@ class CSVParserTest extends SapphireTest
} }
/* And the first row will be returned in the data */ /* And the first row will be returned in the data */
$this->assertEquals(array('FirstName','John','Jane','Jamie','Järg'), $firstNames); $this->assertEquals(['FirstName','John','Jane','Jamie','Järg','Jacob'], $firstNames);
$this->assertEquals( $this->assertEquals(
array( [
'Biography', 'Biography',
"He's a good guy", "He's a good guy",
"She is awesome." . PHP_EOL "She is awesome."
. PHP_EOL
. "So awesome that she gets multiple rows and \"escaped\" strings in her biography", . "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"Pretty old, with an escaped comma", "Pretty old, with an escaped comma",
"Unicode FTW"), "Unicode FTW",
"Likes leading tabs in his biography"
],
$biographies $biographies
); );
$this->assertEquals([ $this->assertEquals([
@ -133,8 +151,9 @@ class CSVParserTest extends SapphireTest
"1988-01-31", "1988-01-31",
"1982-01-31", "1982-01-31",
"1882-01-31", "1882-01-31",
"1982-06-30" "1982-06-30",
"2000-04-30",
], $birthdays); ], $birthdays);
$this->assertEquals(array('IsRegistered', '1', '0', '1', '1'), $registered); $this->assertEquals(['IsRegistered', '1', '0', '1', '1', '0'], $registered);
} }
} }

View File

@ -50,7 +50,7 @@ class CsvBulkLoaderTest extends SapphireTest
$results = $loader->load($filepath); $results = $loader->load($filepath);
// Test that right amount of columns was imported // Test that right amount of columns was imported
$this->assertEquals(4, $results->Count(), 'Test correct count of imported data'); $this->assertEquals(5, $results->Count(), 'Test correct count of imported data');
// Test that columns were correctly imported // Test that columns were correctly imported
$obj = DataObject::get_one( $obj = DataObject::get_one(
@ -76,7 +76,7 @@ class CsvBulkLoaderTest extends SapphireTest
$filepath = $this->csvPath . 'PlayersWithHeader.csv'; $filepath = $this->csvPath . 'PlayersWithHeader.csv';
$loader->deleteExistingRecords = true; $loader->deleteExistingRecords = true;
$results1 = $loader->load($filepath); $results1 = $loader->load($filepath);
$this->assertEquals(4, $results1->Count(), 'Test correct count of imported data on first load'); $this->assertEquals(5, $results1->Count(), 'Test correct count of imported data on first load');
//delete existing data before doing second CSV import //delete existing data before doing second CSV import
$results2 = $loader->load($filepath); $results2 = $loader->load($filepath);
@ -84,7 +84,7 @@ class CsvBulkLoaderTest extends SapphireTest
$resultDataObject = DataObject::get(Player::class); $resultDataObject = DataObject::get(Player::class);
$this->assertEquals( $this->assertEquals(
4, 5,
$resultDataObject->count(), $resultDataObject->count(),
'Test if existing data is deleted before new data is added' 'Test if existing data is deleted before new data is added'
); );

View File

@ -4,3 +4,4 @@
So awesome that she gets multiple rows and \"escaped\" strings in her biography","1982-01-31","0" So awesome that she gets multiple rows and \"escaped\" strings in her biography","1982-01-31","0"
"Jamie","Pretty old\, with an escaped comma","1882-01-31","1" "Jamie","Pretty old\, with an escaped comma","1882-01-31","1"
"Järg","Unicode FTW","1982-06-30","1" "Järg","Unicode FTW","1982-06-30","1"
"Jacob"," Likes leading tabs in his biography","2000-04-30","0"

Can't render this file because it contains an unexpected character in line 4 and column 45.

View File

@ -12,22 +12,27 @@ use SilverStripe\Forms\GridField\GridFieldConfig;
use SilverStripe\Forms\GridField\GridFieldExportButton; use SilverStripe\Forms\GridField\GridFieldExportButton;
use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldPaginator; use SilverStripe\Forms\GridField\GridFieldPaginator;
use SilverStripe\ORM\FieldType\DBField;
class GridFieldExportButtonTest extends SapphireTest class GridFieldExportButtonTest extends SapphireTest
{ {
/**
* @var DataList
*/
protected $list; protected $list;
/**
* @var GridField
*/
protected $gridField; protected $gridField;
protected $form;
protected static $fixture_file = 'GridFieldExportButtonTest.yml'; protected static $fixture_file = 'GridFieldExportButtonTest.yml';
protected static $extra_dataobjects = array( protected static $extra_dataobjects = [
Team::class, Team::class,
NoView::class, NoView::class,
); ];
protected function setUp() protected function setUp()
{ {
@ -44,7 +49,7 @@ class GridFieldExportButtonTest extends SapphireTest
$list = new DataList(NoView::class); $list = new DataList(NoView::class);
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns(array('Name' => 'My Name')); $button->setExportColumns(['Name' => 'My Name']);
$config = GridFieldConfig::create()->addComponent(new GridFieldExportButton()); $config = GridFieldConfig::create()->addComponent(new GridFieldExportButton());
$gridField = new GridField('testfield', 'testfield', $list, $config); $gridField = new GridField('testfield', 'testfield', $list, $config);
@ -58,7 +63,7 @@ class GridFieldExportButtonTest extends SapphireTest
public function testGenerateFileDataBasicFields() public function testGenerateFileDataBasicFields()
{ {
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns(array('Name' => 'My Name')); $button->setExportColumns(['Name' => 'My Name']);
$this->assertEquals( $this->assertEquals(
'"My Name"'."\n". '"My Name"'."\n".
@ -68,17 +73,32 @@ class GridFieldExportButtonTest extends SapphireTest
); );
} }
public function testXLSSanitisation()
{
// Create risky object
$object = new Team();
$object->Name = '=SUM(1, 2)';
$object->write();
// Export
$button = new GridFieldExportButton();
$button->setExportColumns(['Name' => 'My Name']);
$this->assertEquals(
"\"My Name\"\n\"\t=SUM(1, 2)\"\nTest\nTest2\n",
$button->generateExportFileData($this->gridField)
);
}
public function testGenerateFileDataAnonymousFunctionField() public function testGenerateFileDataAnonymousFunctionField()
{ {
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns( $button->setExportColumns([
array(
'Name' => 'Name', 'Name' => 'Name',
'City' => function ($obj) { 'City' => function (DBField $obj) {
return $obj->getValue() . ' city'; return $obj->getValue() . ' city';
} }
) ]);
);
$this->assertEquals( $this->assertEquals(
'Name,City'."\n". 'Name,City'."\n".
@ -91,12 +111,10 @@ class GridFieldExportButtonTest extends SapphireTest
public function testBuiltInFunctionNameCanBeUsedAsHeader() public function testBuiltInFunctionNameCanBeUsedAsHeader()
{ {
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns( $button->setExportColumns([
array(
'Name' => 'Name', 'Name' => 'Name',
'City' => 'strtolower' 'City' => 'strtolower',
) ]);
);
$this->assertEquals( $this->assertEquals(
'Name,strtolower'."\n". 'Name,strtolower'."\n".
@ -109,12 +127,10 @@ class GridFieldExportButtonTest extends SapphireTest
public function testNoCsvHeaders() public function testNoCsvHeaders()
{ {
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns( $button->setExportColumns([
array(
'Name' => 'Name', 'Name' => 'Name',
'City' => 'City' 'City' => 'City',
) ]);
);
$button->setCsvHasHeader(false); $button->setCsvHasHeader(false);
$this->assertEquals( $this->assertEquals(
@ -132,9 +148,7 @@ class GridFieldExportButtonTest extends SapphireTest
//Create an ArrayList 1 greater the Paginator's default 15 rows //Create an ArrayList 1 greater the Paginator's default 15 rows
$arrayList = new ArrayList(); $arrayList = new ArrayList();
for ($i = 1; $i <= 16; $i++) { for ($i = 1; $i <= 16; $i++) {
$dataobject = new DataObject( $dataobject = new DataObject(['ID' => $i]);
array ( 'ID' => $i )
);
$arrayList->add($dataobject); $arrayList->add($dataobject);
} }
$this->gridField->setList($arrayList); $this->gridField->setList($arrayList);
@ -164,11 +178,9 @@ class GridFieldExportButtonTest extends SapphireTest
public function testZeroValue() public function testZeroValue()
{ {
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns( $button->setExportColumns([
array(
'RugbyTeamNumber' => 'Rugby Team Number' 'RugbyTeamNumber' => 'Rugby Team Number'
) ]);
);
$this->assertEquals( $this->assertEquals(
"\"Rugby Team Number\"\n2\n0\n", "\"Rugby Team Number\"\n2\n0\n",

View File

@ -0,0 +1,40 @@
<?php
namespace SilverStripe\Forms\Tests\HTMLEditor;
use SilverStripe\Control\Director;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\HTMLEditor\TinyMCEConfig;
class TinyMCEConfigTest extends SapphireTest
{
/**
* Ensure that all TinyMCEConfig.tinymce_lang are valid
*/
public function testLanguagesValid()
{
$configDir = TinyMCEConfig::config()->get('base_dir');
if (!$configDir) {
$this->markTestSkipped("Test skipped without TinyMCE resources folder being installed");
}
$langs = Director::baseFolder().'/'.ModuleResourceLoader::resourcePath($configDir).'/langs';
// Test all langs exist as real files
foreach (TinyMCEConfig::config()->get('tinymce_lang') as $locale => $resource) {
// Check valid
$this->assertFileExists(
"{$langs}/{$resource}.js",
"Locale code {$locale} maps to {$resource}.js which exists"
);
// Check we don't simplify to locale when a specific version exists
if (strpos($resource, '_') === false) {
$this->assertFileNotExists(
"{$langs}/{$locale}.js",
"Locale code {$locale} doesn't map to simple {$resource}.js when a better {$locale}.js is available"
);
}
}
}
}

View File

@ -1342,10 +1342,7 @@ class DataObjectTest extends SapphireTest
$this->assertFalse($schema->classHasTable(DataObject::class)); $this->assertFalse($schema->classHasTable(DataObject::class));
$this->assertFalse($schema->classHasTable(ViewableData::class)); $this->assertFalse($schema->classHasTable(ViewableData::class));
// Invalid class /* Invalid class name */
$this->expectException(ReflectionException::class);
$this->expectExceptionMessage('Class ThisIsntADataObject does not exist');
$this->assertFalse($schema->classHasTable("ThisIsntADataObject")); $this->assertFalse($schema->classHasTable("ThisIsntADataObject"));
} }

View File

@ -265,7 +265,8 @@ class MemberAuthenticatorTest extends SapphireTest
$this->assertNull($member); $this->assertNull($member);
$this->assertCount(1, LoginAttempt::get()); $this->assertCount(1, LoginAttempt::get());
$attempt = LoginAttempt::get()->first(); $attempt = LoginAttempt::get()->first();
$this->assertEquals($email, $attempt->Email); $this->assertEmpty($attempt->Email); // Doesn't store potentially sensitive data
$this->assertEquals(sha1($email), $attempt->EmailHashed);
$this->assertEquals(LoginAttempt::FAILURE, $attempt->Status); $this->assertEquals(LoginAttempt::FAILURE, $attempt->Status);
} }

View File

@ -3,7 +3,6 @@
namespace SilverStripe\Security\Tests; namespace SilverStripe\Security\Tests;
use Page; use Page;
use PageController;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Control\Director; use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequest;
@ -615,34 +614,21 @@ class SecurityTest extends FunctionalTest
/* UNSUCCESSFUL ATTEMPTS WITH WRONG PASSWORD FOR EXISTING USER ARE LOGGED */ /* UNSUCCESSFUL ATTEMPTS WITH WRONG PASSWORD FOR EXISTING USER ARE LOGGED */
$this->doTestLoginForm('testuser@example.com', 'wrongpassword'); $this->doTestLoginForm('testuser@example.com', 'wrongpassword');
/** @var LoginAttempt $attempt */ /** @var LoginAttempt $attempt */
$attempt = DataObject::get_one( $attempt = LoginAttempt::getByEmail('testuser@example.com')->first();
LoginAttempt::class,
array(
'"LoginAttempt"."Email"' => 'testuser@example.com'
)
);
$this->assertInstanceOf(LoginAttempt::class, $attempt); $this->assertInstanceOf(LoginAttempt::class, $attempt);
$member = DataObject::get_one( $member = Member::get()->filter('Email', 'testuser@example.com')->first();
Member::class,
array(
'"Member"."Email"' => 'testuser@example.com'
)
);
$this->assertEquals($attempt->Status, 'Failure'); $this->assertEquals($attempt->Status, 'Failure');
$this->assertEquals($attempt->Email, 'testuser@example.com'); $this->assertEmpty($attempt->Email); // Doesn't store potentially sensitive data
$this->assertEquals($attempt->EmailHashed, sha1('testuser@example.com'));
$this->assertEquals($attempt->Member()->toMap(), $member->toMap()); $this->assertEquals($attempt->Member()->toMap(), $member->toMap());
/* UNSUCCESSFUL ATTEMPTS WITH NONEXISTING USER ARE LOGGED */ /* UNSUCCESSFUL ATTEMPTS WITH NONEXISTING USER ARE LOGGED */
$this->doTestLoginForm('wronguser@silverstripe.com', 'wrongpassword'); $this->doTestLoginForm('wronguser@silverstripe.com', 'wrongpassword');
$attempt = DataObject::get_one( $attempt = LoginAttempt::getByEmail('wronguser@silverstripe.com')->first();
LoginAttempt::class, $this->assertInstanceOf(LoginAttempt::class, $attempt);
array(
'"LoginAttempt"."Email"' => 'wronguser@silverstripe.com'
)
);
$this->assertTrue(is_object($attempt));
$this->assertEquals($attempt->Status, 'Failure'); $this->assertEquals($attempt->Status, 'Failure');
$this->assertEquals($attempt->Email, 'wronguser@silverstripe.com'); $this->assertEmpty($attempt->Email); // Doesn't store potentially sensitive data
$this->assertEquals($attempt->EmailHashed, sha1('wronguser@silverstripe.com'));
$this->assertNotEmpty($this->getValidationResult()->getMessages(), 'An invalid email returns a message.'); $this->assertNotEmpty($this->getValidationResult()->getMessages(), 'An invalid email returns a message.');
} }
@ -653,22 +639,12 @@ class SecurityTest extends FunctionalTest
/* SUCCESSFUL ATTEMPTS ARE LOGGED */ /* SUCCESSFUL ATTEMPTS ARE LOGGED */
$this->doTestLoginForm('testuser@example.com', '1nitialPassword'); $this->doTestLoginForm('testuser@example.com', '1nitialPassword');
/** @var LoginAttempt $attempt */ /** @var LoginAttempt $attempt */
$attempt = DataObject::get_one( $attempt = LoginAttempt::getByEmail('testuser@example.com')->first();
LoginAttempt::class, $member = Member::get()->filter('Email', 'testuser@example.com')->first();
array( $this->assertInstanceOf(LoginAttempt::class, $attempt);
'"LoginAttempt"."Email"' => 'testuser@example.com'
)
);
/** @var Member $member */
$member = DataObject::get_one(
Member::class,
array(
'"Member"."Email"' => 'testuser@example.com'
)
);
$this->assertTrue(is_object($attempt));
$this->assertEquals($attempt->Status, 'Success'); $this->assertEquals($attempt->Status, 'Success');
$this->assertEquals($attempt->Email, 'testuser@example.com'); $this->assertEmpty($attempt->Email); // Doesn't store potentially sensitive data
$this->assertEquals($attempt->EmailHashed, sha1('testuser@example.com'));
$this->assertEquals($attempt->Member()->toMap(), $member->toMap()); $this->assertEquals($attempt->Member()->toMap(), $member->toMap());
} }
@ -725,6 +701,7 @@ class SecurityTest extends FunctionalTest
// Ensure page shares the same controller as security // Ensure page shares the same controller as security
$securityClass = Config::inst()->get(Security::class, 'page_class'); $securityClass = Config::inst()->get(Security::class, 'page_class');
/** @var Page $securityPage */
$securityPage = new $securityClass(); $securityPage = new $securityClass();
$this->assertInstanceOf($securityPage->getControllerName(), $result); $this->assertInstanceOf($securityPage->getControllerName(), $result);
$this->assertEquals($request, $result->getRequest()); $this->assertEquals($request, $result->getRequest());