mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '4.3' into 4
This commit is contained in:
commit
76936d863d
13
docs/en/04_Changelogs/rc/4.3.0-rc1.md
Normal file
13
docs/en/04_Changelogs/rc/4.3.0-rc1.md
Normal file
@ -0,0 +1,13 @@
|
||||
# 4.3.0-rc1
|
||||
|
||||
<!--- Changes below this line will be automatically regenerated -->
|
||||
|
||||
## Change Log
|
||||
|
||||
### Features and Enhancements
|
||||
|
||||
* 2018-07-25 [79a5ea3](https://github.com/silverstripe/recipe-cms/commit/79a5ea3dace336558ef4d5be4a86cc1a0e84badc) Add versioned-admin (Luke Edwards)
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* 2018-06-15 [5e4ad34](https://github.com/silverstripe/silverstripe-installer/commit/5e4ad341622565cc998bd8537ad3ec7a6a6a7913) Fix incorrect base recipe dependency (Damian Mooyman)
|
@ -150,7 +150,4 @@ ar:
|
||||
LOGIN: دخول
|
||||
LOSTPASSWORDHEADER: 'كلمة مرور مفقودة'
|
||||
NOTEPAGESECURED: 'هذه الصفحة محمية بكلمة مرور ، أدخل بيانات دخولك بالأسفل ليتم السماح لك بالوصول للصفحة'
|
||||
NOTERESETLINKINVALID: "<p> رابط إعادة تعيين كلمة المرور غير صحيح أو نفذت صلاحيته.</p>\n<p> \nيمكنك طلب رابط جديد <\"{a href=\"{link1\"> هنا </a>\n أو تغيير كلمة المرور الخاصة بك بعد <\"{a href=\"{link2\"> تسجيل دخولك</a>.\n</p>"
|
||||
NOTERESETPASSWORD: 'أدخل بريدك الإلكتروني و سيتم إرسال رابط إعادة تهيئة كلمة المرور '
|
||||
PASSWORDSENTHEADER: 'رابط استعادة كلمة المرور تم إرساله إلى ''{بريدك}'''
|
||||
PASSWORDSENTTEXT: 'شكرا لك! تم إرسال رابط إعادة تعيين إلى ''{بريدك}''، بشرط وجود حساب قائم بالنسبة لعنوان هذا البريد الإلكتروني .'
|
||||
|
@ -313,7 +313,4 @@ bg:
|
||||
LOGOUT: Изход
|
||||
LOSTPASSWORDHEADER: 'Забравена парола'
|
||||
NOTEPAGESECURED: 'Тази страница е защитена. Въведете вашите данни по-долу, за да продължите.'
|
||||
NOTERESETLINKINVALID: '<p>Връзката за нулиране на парола не е вярна или е просрочена.</p><p>Можете да заявите нова <a href="{link1}">тук</a> или да промените паролата си след като <a href="{link2}">влезете</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Въведете вашият email адрес и ще ви изпратим линк, с който ще можете да смените паролата си'
|
||||
PASSWORDSENTHEADER: 'Връзка за нулиране на парола беше изпратена на ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Благодарим ви! Връзка за нулиране на паролата беше изпратен на ''{email}'', ако съществува акаунт с този имейл адрес.'
|
||||
|
@ -194,7 +194,4 @@ cs:
|
||||
LOGIN: Přihlásit
|
||||
LOSTPASSWORDHEADER: 'Zapomenuté heslo'
|
||||
NOTEPAGESECURED: 'Tato stránka je zabezpečená. Vložte své přihlašovací údaje a my Vám zároveň pošleme práva.'
|
||||
NOTERESETLINKINVALID: '<p>Odkaz na resetování hesla není platný nebo je prošlý.</p><p>Můžete požádat o nový <a href="{link1}">zde</a> nebo změňte své heslo až <a href="{link2}">se přihlásíte</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Zadejte svou e-mailovou adresu a bude vám zaslán nulovací odkaz pro Vaše heslo'
|
||||
PASSWORDSENTHEADER: 'Odkaz na resetování hesla byl odeslán na ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Děkujeme! Resetovací odkaz byl odeslán na ''{email}'', pokud účet existuje pro tuto emailovou adresu.'
|
||||
|
325
lang/da.yml
325
lang/da.yml
@ -1,5 +1,328 @@
|
||||
da:
|
||||
SilverStripe\Admin\LeftAndMain:
|
||||
VersionUnknown: ukendt
|
||||
SilverStripe\AssetAdmin\Forms\UploadField:
|
||||
Dimensions: Dimensioner
|
||||
EDIT: Rediger
|
||||
EDITINFO: 'Rediger denne fil'
|
||||
REMOVE: Fjern
|
||||
SilverStripe\Control\ChangePasswordEmail_ss:
|
||||
CHANGEPASSWORDFOREMAIL: 'Koden for kontoen med email addressen {email} er ændret. Hvis du ikke har skiftet din kode, så skift venligst din kode ved at klikke på linket herunder'
|
||||
CHANGEPASSWORDTEXT1: 'Du skiftede dit kodeord for'
|
||||
CHANGEPASSWORDTEXT3: 'Skift kodeord'
|
||||
HELLO: Hej
|
||||
SilverStripe\Control\Email\ForgotPasswordEmail_ss:
|
||||
HELLO: Hej
|
||||
TEXT1: 'Her er din'
|
||||
TEXT2: 'link til at nulstille dit kodeord'
|
||||
TEXT3: for
|
||||
SilverStripe\Control\RequestProcessor:
|
||||
INVALID_REQUEST: 'Ugyldig forespørgsel'
|
||||
REQUEST_ABORTED: 'Forespørgsel annulleret'
|
||||
SilverStripe\Core\Manifest\VersionProvider:
|
||||
VERSIONUNKNOWN: Ukendt
|
||||
SilverStripe\Forms\CheckboxField:
|
||||
NOANSWER: Nej
|
||||
YESANSWER: Ja
|
||||
SilverStripe\Forms\CheckboxSetField_ss:
|
||||
NOOPTIONSAVAILABLE: 'Ingen tilgængelige muligheder'
|
||||
SilverStripe\Forms\ConfirmedPasswordField:
|
||||
ATLEAST: 'Kodeord skal være mindst {min} tegn lang.'
|
||||
BETWEEN: 'Kodeord skal være {min} til {max} karakterer lang.'
|
||||
CURRENT_PASSWORD_ERROR: 'Det nuværende kodeord du har indtastet er ikke korrekt.'
|
||||
CURRENT_PASSWORD_MISSING: 'Du skal indtaste dit nuværende kodeord.'
|
||||
LOGGED_IN_ERROR: 'Du skal være logget ind for at skifte dit kodeord.'
|
||||
MAXIMUM: 'Kodeord må maks være {max} tegn lang'
|
||||
SHOWONCLICKTITLE: 'Skift kodeord'
|
||||
SilverStripe\Forms\CurrencyField:
|
||||
CURRENCYSYMBOL: DKK
|
||||
SilverStripe\Forms\DateField:
|
||||
VALIDDATEFORMAT2: 'Indtats venligst et gyldigt datoformat ({format})'
|
||||
VALIDDATEMAXDATE: 'Din dato skal være ældre end eller matche den maksimalt tilladte dato ({date})'
|
||||
VALIDDATEMINDATE: 'Din dato skal være yngre end eller matche den minimum tilladte dato ({date})'
|
||||
SilverStripe\Forms\DatetimeField:
|
||||
VALIDDATEMAXDATETIME: 'Din dato skal være ældre end eller matche den maksimalt tilladte dato ({datetime})'
|
||||
VALIDDATETIMEFORMAT: 'Indtats venligst et gyldigt dato- og tidsformat ({format})'
|
||||
VALIDDATETIMEMINDATE: 'Din dato skal være yngre end eller matche den minimum tilladte dato og tid ({datetime})'
|
||||
SilverStripe\Forms\DropdownField:
|
||||
CHOOSE: (Vælg)
|
||||
CHOOSE_MODEL: '(Vælg {name})'
|
||||
SOURCE_VALIDATION: 'Venligst vælg en eksisterende værdi fra listen. {value} er ikke en tilladt mulighed'
|
||||
SilverStripe\Forms\EmailField:
|
||||
VALIDATION: 'Indtast venligst en emailadresse'
|
||||
SilverStripe\Forms\FileUploadReceiver:
|
||||
FIELDNOTSET: 'Fil information ikke fundet'
|
||||
SilverStripe\Forms\Form:
|
||||
BAD_METHOD: 'Denne form kræver en {method} indsendelse'
|
||||
CSRF_EXPIRED_MESSAGE: 'Din session er udløbet. Venligst gensend formularen.'
|
||||
CSRF_FAILED_MESSAGE: 'Det ser ud til der har været et teknisk problem. Klik venligst på tilbageknappen, tryk opdater i din browser og prøv igen.'
|
||||
VALIDATIONPASSWORDSDONTMATCH: 'Kodeordene er ikke identiske'
|
||||
VALIDATIONPASSWORDSNOTEMPTY: 'Kodeord kan ikke være tomme'
|
||||
VALIDATIONSTRONGPASSWORD: 'Kodeord skal mindst have et tal og et alfanumerisk tegn'
|
||||
VALIDATOR: Validering
|
||||
VALIDCURRENCY: 'Indtast venligst en gyldig valuta'
|
||||
SilverStripe\Forms\FormField:
|
||||
EXAMPLE: 'f.eks. {format}'
|
||||
NONE: ingen
|
||||
SilverStripe\Forms\FormScaffolder:
|
||||
TABMAIN: Primær
|
||||
SilverStripe\Forms\GridField\GridField:
|
||||
Filter: Filter
|
||||
Add: 'Tilføj {name}'
|
||||
CSVEXPORT: 'Eksporter til CSV'
|
||||
CSVIMPORT: 'Importer CSV'
|
||||
Filter: Filtrer
|
||||
FilterBy: 'Filtrer på'
|
||||
Find: Find
|
||||
LinkExisting: 'Link eksisterende'
|
||||
NewRecord: 'Ny {type}'
|
||||
NoItemsFound: 'Ingen elementer fundet'
|
||||
PRINTEDAT: 'Printet d.'
|
||||
PRINTEDBY: 'Printet af'
|
||||
PlaceHolder: 'Find {type}'
|
||||
PlaceHolderWithLabels: 'Find {type} på {name}'
|
||||
Print: Print
|
||||
RelationSearch: Relationssøgning
|
||||
ResetFilter: Nulstil
|
||||
SilverStripe\Forms\GridField\GridFieldDeleteAction:
|
||||
Delete: Slet
|
||||
DeletePermissionsFailure: 'Ingen slette rettigheder'
|
||||
EditPermissionsFailure: 'Ingen rettighed til at fjerne emnet'
|
||||
UnlinkRelation: Fjern
|
||||
SilverStripe\Forms\GridField\GridFieldDetailForm:
|
||||
CancelBtn: Annuller
|
||||
Create: Opret
|
||||
Delete: Slet
|
||||
DeletePermissionsFailure: 'Ingen slette rettigheder'
|
||||
Deleted: 'Slet {type} {name}'
|
||||
Save: Gem
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Rediger
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Kan ikke fjerne dig selv fra denne gruppe, du vil miste administrator rettigheder'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
OF: af
|
||||
Page: Side
|
||||
View: Vis
|
||||
SilverStripe\Forms\MoneyField:
|
||||
FIELDLABELAMOUNT: Beløb
|
||||
FIELDLABELCURRENCY: Valuta
|
||||
INVALID_CURRENCY: 'Valuta {currency} er ikke i listen over tilladte valutaer'
|
||||
SilverStripe\Forms\MultiSelectField:
|
||||
SOURCE_VALIDATION: 'Vælg venligst eksisterende værdier fra listen. Ugyldig mulighed(er) {value} valgt'
|
||||
SilverStripe\Forms\NullableField:
|
||||
IsNullLabel: 'Er Null'
|
||||
SilverStripe\Forms\NumericField:
|
||||
VALIDATION: '''{value}'' er ikke et tal, kun tal accepteres i dette felt'
|
||||
SilverStripe\Forms\TimeField:
|
||||
VALIDATEFORMAT: 'Indtats venligst et gyldigt tidsformat ({format})'
|
||||
SilverStripe\ORM\DataObject:
|
||||
PLURALNAME: Dataobjekter
|
||||
PLURALS:
|
||||
one: 'Et dataobjekt'
|
||||
other: '{count} dataobjekter'
|
||||
SINGULARNAME: Dataobjekt
|
||||
SilverStripe\ORM\FieldType\DBBoolean:
|
||||
ANY: Enhver
|
||||
NOANSWER: Nej
|
||||
YESANSWER: Ja
|
||||
SilverStripe\ORM\FieldType\DBDate:
|
||||
DAYS_SHORT_PLURALS:
|
||||
one: '{count} dag'
|
||||
other: '{count} dage'
|
||||
HOURS_SHORT_PLURALS:
|
||||
one: '{count} time'
|
||||
other: '{count} timer'
|
||||
LessThanMinuteAgo: 'mindre end et minut'
|
||||
MINUTES_SHORT_PLURALS:
|
||||
one: '{count} minut'
|
||||
other: '{count} minutter'
|
||||
MONTHS_SHORT_PLURALS:
|
||||
one: '{count} måned'
|
||||
other: '{count} måneder'
|
||||
SECONDS_SHORT_PLURALS:
|
||||
one: '{count} sekund'
|
||||
other: '{count} sekunder'
|
||||
TIMEDIFFAGO: '{difference} siden'
|
||||
TIMEDIFFIN: 'i {difference}'
|
||||
YEARS_SHORT_PLURALS:
|
||||
one: '{count} år'
|
||||
other: '{count} år'
|
||||
SilverStripe\ORM\FieldType\DBEnum:
|
||||
ANY: Enhver
|
||||
SilverStripe\ORM\FieldType\DBForeignKey:
|
||||
DROPDOWN_THRESHOLD_FALLBACK_MESSAGE: 'For mange relaterede objekter; fallback felt i brug'
|
||||
SilverStripe\ORM\Hierarchy:
|
||||
LIMITED_TITLE: 'For mange underelementer ({count})'
|
||||
SilverStripe\ORM\Hierarchy\Hierarchy:
|
||||
InfiniteLoopNotAllowed: 'Uendeligt løkke fundet i "{type}" hierarkiet. Ændre venligst det overliggende element for at løse dette'
|
||||
LIMITED_TITLE: 'For mange underelementer ({count})'
|
||||
SilverStripe\ORM\ValidationException:
|
||||
DEFAULT_ERROR: Valideringsfejl
|
||||
SilverStripe\Security\BasicAuth:
|
||||
ENTERINFO: 'Indtast venligst et brugernavn og kodeord.'
|
||||
ERRORNOTADMIN: 'Den bruger er ikke en administrator.'
|
||||
ERRORNOTREC: 'Brugernavn / kodeord kunne ikke genkendes'
|
||||
SilverStripe\Security\CMSMemberLoginForm:
|
||||
PASSWORDEXPIRED: '<p>Dit kodeord er udløbet. <a target="_top" href="{link}">Vælg venligst et nyt.</a></p>'
|
||||
SilverStripe\Security\CMSSecurity:
|
||||
INVALIDUSER: '<p>Ugyldig bruger. <a target="_top" href="{link}">Log venligst ind igen her</a> for at fortsætte.</p>'
|
||||
LOGIN_MESSAGE: '<p>Din session er løbet ud pga. inaktivitet</p>'
|
||||
LOGIN_TITLE: 'Log ind igen, for at fortsætte hvor du slap.'
|
||||
SUCCESS: Succes
|
||||
SUCCESSCONTENT: '<p>Logget ind. Hvis du ikke automatisk viderestilles så <a target="_top" href="{link}">klik her</a></p>'
|
||||
SUCCESS_TITLE: 'Logget ind med sucess'
|
||||
SilverStripe\Security\DefaultAdminService:
|
||||
DefaultAdminFirstname: 'Standard admin'
|
||||
SilverStripe\Security\Group:
|
||||
AddRole: 'Tilføj en rolle for denne gruppe'
|
||||
Code: 'Gruppe kode'
|
||||
DefaultGroupTitleAdministrators: Administratorer
|
||||
DefaultGroupTitleContentAuthors: Indholdsforfattere
|
||||
Description: Beskrivelse
|
||||
GROUPNAME: Gruppenavn
|
||||
GroupReminder: 'Hvis du vælger en overliggende gruppe, får denne gruppe alle dens roller'
|
||||
HierarchyPermsError: 'Kan ikke tildele overliggende gruppe "{group}" med fortrinsrettigheder (kræver ADMIN adgang)'
|
||||
Locked: 'Låst?'
|
||||
MEMBERS: Brugere
|
||||
NEWGROUP: 'Ny gruppe'
|
||||
NoRoles: 'Ingen roller fundet'
|
||||
PERMISSIONS: Rettigheder
|
||||
PLURALNAME: Grupper
|
||||
PLURALS:
|
||||
one: 'En gruppe'
|
||||
other: '{count} grupper'
|
||||
Parent: 'Overliggende gruppe'
|
||||
ROLES: Roller
|
||||
ROLESDESCRIPTION: 'Roller er et prædefineret sæt af rettigheder, som kan tildeles grupper.<br />De bliver nedarvet fra en overliggende grupper hvis krævet.'
|
||||
RolesAddEditLink: 'Administrer roller'
|
||||
SINGULARNAME: Gruppe
|
||||
Sort: Sortering
|
||||
has_many_Permissions: Rettigheder
|
||||
many_many_Members: Brugere
|
||||
SilverStripe\Security\LoginAttempt:
|
||||
Email: 'Email adresse'
|
||||
EmailHashed: 'Email adresse (hashed)'
|
||||
IP: 'IP addresse'
|
||||
PLURALNAME: Loginforsøg
|
||||
PLURALS:
|
||||
one: 'Et loginforsøg'
|
||||
other: '{count} loginforsøg'
|
||||
SINGULARNAME: 'Login forsøg'
|
||||
Status: Status
|
||||
SilverStripe\Security\Member:
|
||||
ADDGROUP: 'Tilføj gruppe'
|
||||
BUTTONCHANGEPASSWORD: 'Skift kodeord'
|
||||
BUTTONLOGIN: 'Log ind'
|
||||
BUTTONLOGINOTHER: 'Log ind med en anden bruger'
|
||||
BUTTONLOGOUT: 'Log ud'
|
||||
BUTTONLOSTPASSWORD: 'Jeg har glemt mit kodeord'
|
||||
CONFIRMNEWPASSWORD: 'Bekræft nyt kodeord'
|
||||
CONFIRMPASSWORD: 'Bekræft kodeord'
|
||||
CURRENT_PASSWORD: 'Nuværende kodeord'
|
||||
EDIT_PASSWORD: 'Nyt kodeord'
|
||||
EMAIL: Email
|
||||
EMPTYNEWPASSWORD: 'Det nye kodeord kan ikke være tom, prøv venligst igen'
|
||||
ENTEREMAIL: 'Indtast venligst en email adresse for at få et nulstillingslink.'
|
||||
ERRORLOCKEDOUT2: 'Din konto er blevet midlertidigt deaktiveret pga. for mange fejlslagne loginforsøg. Forsøg venligst igen om {count} minutter.'
|
||||
ERRORNEWPASSWORD: 'Du har indtastet dit nye kodeord forskelligt, forsøg igen'
|
||||
ERRORPASSWORDNOTMATCH: 'Dit nuværende kodeord matcher ikke, forsøg venligst igen'
|
||||
ERRORWRONGCRED: 'De indtastede værdier ser ikke ud til at være korrekte. Forsøg venligst igen.'
|
||||
FIRSTNAME: Fornavn
|
||||
INTERFACELANG: 'Sprog i brugerfladen'
|
||||
KEEPMESIGNEDIN: 'Hold mig logget ind'
|
||||
LOGGEDINAS: 'Du er logget ind som {name}.'
|
||||
NEWPASSWORD: 'Nyt kodeord'
|
||||
PASSWORD: Kodeord
|
||||
PASSWORDEXPIRED: 'Dit kodeord er udløbet. Vælg venligst et nyt.'
|
||||
PLURALNAME: Brugere
|
||||
PLURALS:
|
||||
one: 'En bruger'
|
||||
other: '{count} brugere'
|
||||
REMEMBERME: 'Husk mig til næste gang? (i {count} dage på denne enhed)'
|
||||
SINGULARNAME: Bruger
|
||||
SUBJECTPASSWORDCHANGED: 'Dit kodeord er blevet ændret'
|
||||
SUBJECTPASSWORDRESET: 'Link til at nulstille dit kodeord'
|
||||
SURNAME: Efternavn
|
||||
VALIDATIONADMINLOSTACCESS: 'Kan ikke fjerne alle admin grupper fra din profil'
|
||||
ValidationIdentifierFailed: 'Kan ikke overskrive eksisterende bruger #{id} med identisk identifikator ({name} = {value}))'
|
||||
WELCOMEBACK: 'Velkommen tilbage, {firstname}'
|
||||
YOUROLDPASSWORD: 'Dit gamle kodeord'
|
||||
belongs_many_many_Groups: Grupper
|
||||
db_Locale: 'Sprog i brugerfladen'
|
||||
db_LockedOutUntil: 'Låst ude indtil'
|
||||
db_Password: Kodeord
|
||||
db_PasswordExpiry: Kodeordsudløbsdato
|
||||
SilverStripe\Security\MemberAuthenticator\CMSMemberLoginForm:
|
||||
AUTHENTICATORNAME: 'CMS bruger loginform'
|
||||
BUTTONFORGOTPASSWORD: 'Glemt kodeord'
|
||||
BUTTONLOGIN: 'Log mig ind igen'
|
||||
BUTTONLOGOUT: 'Log ud'
|
||||
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
|
||||
ERRORWRONGCRED: 'De indtastede værdier ser ikke ud til at være korrekte. Forsøg venligst igen.'
|
||||
NoPassword: 'Der er ikke en kode på denne bruger.'
|
||||
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
|
||||
AUTHENTICATORNAME: 'Email og kodeord'
|
||||
SilverStripe\Security\MemberPassword:
|
||||
PLURALNAME: 'Bruger kodeord'
|
||||
PLURALS:
|
||||
one: 'Et bruger kodeord'
|
||||
other: '{count} bruger kodeord'
|
||||
SINGULARNAME: 'Bruger kodeord'
|
||||
SilverStripe\Security\PasswordValidator:
|
||||
LOWCHARSTRENGTH: 'Forøg venligst kodeordets styrke, ved at tilføje nogle af følgende tegn: {chars}'
|
||||
PREVPASSWORD: 'Du har tidligere brugt dette kodeord, vælg venligst et nyt kodeord'
|
||||
TOOSHORT: 'Kodeordet er for kort, det skal mindst være {minimum} eller flere tegn langt'
|
||||
SilverStripe\Security\Permission:
|
||||
AdminGroup: Administrator
|
||||
CMS_ACCESS_CATEGORY: 'CMS Adgang'
|
||||
CONTENT_CATEGORY: Indholdsrettigheder
|
||||
FULLADMINRIGHTS: 'Fuld administrator rettighed'
|
||||
FULLADMINRIGHTS_HELP: 'Indebærer og overskriver alle andre tildelte rettigheder.'
|
||||
PERMISSIONS_CATEGORY: 'Roller og adgangsrettigheder'
|
||||
PLURALNAME: Rettigheder
|
||||
PLURALS:
|
||||
one: 'En rettighed'
|
||||
other: '{count} rettigheder'
|
||||
SINGULARNAME: Rettighed
|
||||
UserPermissionsIntro: 'Tildeling af grupper til denne bruger, ændrer de rettigheder brugeren har. Se gruppe området for rettigheds detaljer på de individuelle grupper.'
|
||||
SilverStripe\Security\PermissionCheckboxSetField:
|
||||
AssignedTo: 'tildelt til "{title}"'
|
||||
FromGroup: 'nedarvet fra gruppen "{title}"'
|
||||
FromRole: 'nedarvet fra rollen "{title}"'
|
||||
FromRoleOnGroup: 'nedarvet fra rollen "{roletitle}" på gruppen "{grouptitle}"'
|
||||
SilverStripe\Security\PermissionRole:
|
||||
OnlyAdminCanApply: 'Kun administratorer kan tilføje'
|
||||
PLURALNAME: Roller
|
||||
PLURALS:
|
||||
one: 'En rolle'
|
||||
other: '{count} roller'
|
||||
SINGULARNAME: Rolle
|
||||
Title: Titel
|
||||
SilverStripe\Security\PermissionRoleCode:
|
||||
PLURALNAME: 'Rettigheds rolle koder'
|
||||
PLURALS:
|
||||
one: 'En rettigheds rolle kode'
|
||||
other: '{count} rettigheds rolle koder'
|
||||
PermsError: 'Kan ikke tildele koden "{code}" med fortrinsrettigheder (kræver ADMIN adgang)'
|
||||
SINGULARNAME: 'Rettighed rolle kode'
|
||||
SilverStripe\Security\RememberLoginHash:
|
||||
PLURALNAME: 'Login hashes'
|
||||
PLURALS:
|
||||
one: 'Et login hash'
|
||||
other: '{count} Login Hashes'
|
||||
SINGULARNAME: 'Login hash'
|
||||
SilverStripe\Security\Security:
|
||||
ALREADYLOGGEDIN: 'Du har ikke adgang til denne side. Hvis du har en anden bruger der har adgang til denne side, kan du logge ind med denne herunder.'
|
||||
BUTTONSEND: 'Send mig linket til at nulstille kodeordet'
|
||||
CHANGEPASSWORDBELOW: 'Du kan ændre dit kodeord herunder.'
|
||||
CHANGEPASSWORDHEADER: 'Skift dit kodeord'
|
||||
CONFIRMLOGOUT: 'Klik venligst på knappen herunder, for at bekræfte at du vil logge ud.'
|
||||
ENTERNEWPASSWORD: 'Indtast venligst et nyt kodeord.'
|
||||
ERRORPASSWORDPERMISSION: 'Du skal være logget ind, for at kunne ændre dit kodeord!'
|
||||
LOGIN: 'Log ind'
|
||||
LOGOUT: 'Log ud'
|
||||
LOSTPASSWORDHEADER: 'Glemt kodeord'
|
||||
NOTEPAGESECURED: 'Denne side er beskyttet. Indtast dine loginoplysninger herunder for at få adgang.'
|
||||
NOTERESETPASSWORD: 'Indtast din email adresse, så sender vi dig et link som du kan nulstille dit kodeord med'
|
||||
PASSWORDRESETSENTHEADER: 'link til at nulstille kodeord afsendt'
|
||||
PASSWORDRESETSENTTEXT: 'Tak for det. Et link til at nulstille dit kodeord er afsendt, hvis der findes en bruger med denne email adresse.'
|
||||
|
@ -190,7 +190,4 @@ de:
|
||||
LOGIN: Anmelden
|
||||
LOSTPASSWORDHEADER: 'Passwort vergessen'
|
||||
NOTEPAGESECURED: 'Diese Seite ist geschützt. Bitte melden Sie sich an und Sie werden sofort weitergeleitet.'
|
||||
NOTERESETLINKINVALID: '<p>Der Link zum Zurücksetzen des Passworts ist entweder nicht korrekt oder abgelaufen</p><p>Sie können <a href="{link1}">einen neuen Link anfordern</a> oder Ihr Passwort nach dem <a href="{link2}">einloggen</a> ändern.</p>'
|
||||
NOTERESETPASSWORD: 'Geben Sie Ihre E-Mail-Adresse ein und wir werden Ihnen einen Link zuschicken, mit dem Sie Ihr Passwort zurücksetzen können.'
|
||||
PASSWORDSENTHEADER: 'Der Link zum Zurücksetzen des Passworts wurde an ''{email}'' gesendet'
|
||||
PASSWORDSENTTEXT: 'Vielen Dank! Wenn ein Account zu der E-Mail Adresse ''{email}'' existiert, wurde eine E-Mail mit dem Link zum Zurücksetzen des Passworts verschickt.'
|
||||
|
@ -78,6 +78,7 @@ en:
|
||||
LinkExisting: 'Link Existing'
|
||||
NewRecord: 'New {type}'
|
||||
NoItemsFound: 'No items found'
|
||||
OpenFilter: 'Open search and filter'
|
||||
PRINTEDAT: 'Printed at'
|
||||
PRINTEDBY: 'Printed by'
|
||||
PlaceHolder: 'Find {type}'
|
||||
@ -85,10 +86,6 @@ en:
|
||||
Print: Print
|
||||
RelationSearch: 'Relation search'
|
||||
ResetFilter: Reset
|
||||
OpenFilter: 'Open search and filter'
|
||||
SilverStripe\Forms\GridField\GridFieldFilterHeader:
|
||||
Search: 'Search "{name}"'
|
||||
SearchFormFaliure: 'No search form could be generated'
|
||||
SilverStripe\Forms\GridField\GridFieldDeleteAction:
|
||||
Delete: Delete
|
||||
DeletePermissionsFailure: 'No delete permissions'
|
||||
@ -103,6 +100,9 @@ en:
|
||||
Save: Save
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Edit
|
||||
SilverStripe\Forms\GridField\GridFieldFilterHeader:
|
||||
Search: 'Search "{name}"'
|
||||
SearchFormFaliure: 'No search form could be generated'
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Cannot remove yourself from this group, you will lose admin rights'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
|
@ -95,6 +95,8 @@ eo:
|
||||
DeletePermissionsFailure: 'Mankas permeso forigi'
|
||||
Deleted: 'Forigita {type} {name}'
|
||||
Save: Konservi
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Redakti
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Ne povas forigi vin el ĉi tiu grupo; vi perdus administrajn rajtojn'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
@ -147,6 +149,8 @@ eo:
|
||||
other: '{count} jaroj'
|
||||
SilverStripe\ORM\FieldType\DBEnum:
|
||||
ANY: Ajna
|
||||
SilverStripe\ORM\FieldType\DBForeignKey:
|
||||
DROPDOWN_THRESHOLD_FALLBACK_MESSAGE: 'Tro multaj objektoj; retropaŝa kampo uzata'
|
||||
SilverStripe\ORM\Hierarchy:
|
||||
LIMITED_TITLE: 'Tro da idoj ({count})'
|
||||
SilverStripe\ORM\Hierarchy\Hierarchy:
|
||||
@ -319,7 +323,6 @@ eo:
|
||||
LOGOUT: Elsaluti
|
||||
LOSTPASSWORDHEADER: 'Perdis pasvorton'
|
||||
NOTEPAGESECURED: 'Tiu paĝo estas sekurigita. Enigu viajn akreditaĵojn sube kaj vi aliros pluen.'
|
||||
NOTERESETLINKINVALID: '<p>La pasvorta reagorda ligilo estas malvalida aŭ finiĝis.</p><p>Vi povas peti novan <a href="{link1}">ĉi tie</a> aŭ ŝanĝi vian pasvorton post <a href="{link2}">vi ensalutis</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Enigu vian retpoŝtan adreson kaj ni sendos al vi ligilon per kiu vi povas reagordi vian pasvorton'
|
||||
PASSWORDSENTHEADER: 'Pasvorta reagorda ligilo sendiĝis al ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Dankon! Reagordita ligilo sendiĝis al ''{email}'', kondiĉe ke konto ekzistas por tiu retadreso.'
|
||||
PASSWORDRESETSENTHEADER: 'Pasvorta reagorda ligilo sendiĝis'
|
||||
PASSWORDRESETSENTTEXT: 'Dankon. Reagorda ligilo sendiĝis, kondiĉe ke konto ekzistas por ĉi tiu retadreso.'
|
||||
|
@ -249,7 +249,4 @@ es:
|
||||
LOGIN: Entrar
|
||||
LOSTPASSWORDHEADER: '¿Contraseña Perdida?'
|
||||
NOTEPAGESECURED: 'Esa página está protegida. Introduzca sus datos de acreditación a continuación y lo enviaremos a ella en un momento.'
|
||||
NOTERESETLINKINVALID: '<p>El enlace para restablecer la contraseña es inválido o ha expirado.</p><p>Usted puede solicitar uno nuevo <a href="{link1}">aqui</a> o cambiar su contraseña después de que se haya <a href="{link2}">conectado</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Introduzca su dirección de e-mail, y le enviaremos un enlace, con el cual podrá restaurar su contraseña'
|
||||
PASSWORDSENTHEADER: 'Un enlace para restablecer la contraseña ha sido enviado a ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Gracias! Un enlace para restablecer la contraseña ha sido enviado a ''{email}'', siempre que una cuenta exista para la dirección de email indicada.'
|
||||
|
@ -139,7 +139,4 @@ et_EE:
|
||||
ERRORPASSWORDPERMISSION: 'Pead olema sisseloginud, et parooli muuta!'
|
||||
LOGIN: 'Logi sisse'
|
||||
NOTEPAGESECURED: 'See leht on turvatud. Sisesta enda andmed allpool ja me saadame sind otse edasi'
|
||||
NOTERESETLINKINVALID: '<p>Parooli lähtestamise link on kehtetu või aegunud.</p><p>Saate taotleda uut linki <a href="{link1}">siin</a> või muuta parooli pärast <a href="{link2}">sisselogimist</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Sisesta oma email ja me saadame sulle lingi kus saad oma parooli tühistada.'
|
||||
PASSWORDSENTHEADER: 'Parooli lähtestamise link saadeti aadressile ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Aitäh! Lähtestamislink saadeti aadressile ''{email}'' eeldusel, et selle e-posti aadressiga seotud konto on olemas.'
|
||||
|
@ -168,4 +168,3 @@ fa_IR:
|
||||
ERRORPASSWORDPERMISSION: 'جهت تغییر گذرواژه خود باید وارد شده باشید!'
|
||||
LOGIN: ورود
|
||||
LOSTPASSWORDHEADER: 'فراموشی گذرواژه'
|
||||
PASSWORDSENTHEADER: 'لینک ازنوسازی گذرواژه به ''{email}'' ارسال شد'
|
||||
|
17
lang/fi.yml
17
lang/fi.yml
@ -76,6 +76,7 @@ fi:
|
||||
LinkExisting: 'Linkitä olemassaoleva'
|
||||
NewRecord: 'Uusi {type}'
|
||||
NoItemsFound: 'Ei kohteita'
|
||||
OpenFilter: 'Avaa haku ja suodatus'
|
||||
PRINTEDAT: Tulostettu
|
||||
PRINTEDBY: Tulostaja
|
||||
PlaceHolder: 'Etsi {type}'
|
||||
@ -95,12 +96,19 @@ fi:
|
||||
DeletePermissionsFailure: 'Ei oikeuksia poistamiseen'
|
||||
Deleted: 'Poistettiin {type} {name}'
|
||||
Save: Tallenna
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Muokkaa
|
||||
SilverStripe\Forms\GridField\GridFieldFilterHeader:
|
||||
Search: 'Haku "{name}"'
|
||||
SearchFormFaliure: 'Hakulomaketta ei pystytty luomaan.'
|
||||
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:
|
||||
OF: /
|
||||
Page: Sivu
|
||||
View: Näytä
|
||||
SilverStripe\Forms\GridField\GridFieldViewButton:
|
||||
VIEW: Avaa
|
||||
SilverStripe\Forms\MoneyField:
|
||||
FIELDLABELAMOUNT: Määrä
|
||||
FIELDLABELCURRENCY: Valuutta
|
||||
@ -147,6 +155,8 @@ fi:
|
||||
other: '{count} vuotta'
|
||||
SilverStripe\ORM\FieldType\DBEnum:
|
||||
ANY: Yhtään
|
||||
SilverStripe\ORM\FieldType\DBForeignKey:
|
||||
DROPDOWN_THRESHOLD_FALLBACK_MESSAGE: 'Liian monta samaan liittyvää objektia: oletuskenttä käytössä'
|
||||
SilverStripe\ORM\Hierarchy:
|
||||
LIMITED_TITLE: 'Liian monta lapsiobjektia ({count}}'
|
||||
SilverStripe\ORM\Hierarchy\Hierarchy:
|
||||
@ -197,6 +207,7 @@ fi:
|
||||
many_many_Members: Jäsenet
|
||||
SilverStripe\Security\LoginAttempt:
|
||||
Email: Sähköpostiosoite
|
||||
EmailHashed: 'Sähköpostiosoite (tiivistetty)'
|
||||
IP: IP-osoite
|
||||
PLURALNAME: Kirjautumisyritykset
|
||||
PLURALS:
|
||||
@ -255,6 +266,8 @@ fi:
|
||||
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
|
||||
ERRORWRONGCRED: 'Antamasi tiedot eivät näytä oikeilta. Yritä uudelleen.'
|
||||
NoPassword: 'Tällä käyttäjällä ei ole salasanaa'
|
||||
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
|
||||
AUTHENTICATORNAME: 'Sähköpostiosoite & salasana'
|
||||
SilverStripe\Security\MemberPassword:
|
||||
PLURALNAME: 'Käyttäjän salasanat'
|
||||
PLURALS:
|
||||
@ -318,5 +331,5 @@ fi:
|
||||
NOTEPAGESECURED: 'Tämä sivu on suojattu. Syötä tunnistetietosi alle niin pääset eteenpäin.'
|
||||
NOTERESETLINKINVALID: '<p>Salasanan palautuslinkki on virheellinen tai vanhentunut.</p><p>Voit pyytää uuden <a href="{link1}">napsauttamalla tästä</a> tai vaihtaa salasanasi <a href="{link2}">kirjautumisen jälkeen</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Syötä sähköpostiosoitteesi ja lähetämme sinulle linkin, jonka avulla saat palautettua salasanasi'
|
||||
PASSWORDSENTHEADER: 'Salasanan palautuslinkki lähetettiin osoitteeseen ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Kiitos! Salasanan palautuslinkki lähetettiin osoitteeseen ''{email}'', joka on liitettynä tähän käyttäjätiliin.'
|
||||
PASSWORDRESETSENTHEADER: 'Salasanan palautuslinkki lähetetty'
|
||||
PASSWORDRESETSENTTEXT: 'Kiitos, palautuslinkki on lähetetty käyttäjätilille asetettuun sähköpostiosoitteeseen.'
|
||||
|
@ -84,7 +84,6 @@ fr:
|
||||
RelationSearch: 'Rechercher relations'
|
||||
ResetFilter: Réinitialiser
|
||||
SilverStripe\Forms\GridField\GridFieldDeleteAction:
|
||||
DELETE_DESCRIPTION: Supprimer
|
||||
Delete: Supprimer
|
||||
DeletePermissionsFailure: 'Vous n’avez pas les autorisations pour supprimer'
|
||||
EditPermissionsFailure: 'Pas de permissions pour délier l''enregistrement'
|
||||
@ -96,8 +95,6 @@ fr:
|
||||
DeletePermissionsFailure: 'Vous n’avez pas les autorisations pour supprimer'
|
||||
Deleted: '{type} {name} supprimés'
|
||||
Save: Enregistrer
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton_ss:
|
||||
EDIT: Éditer
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Impossible de retirer votre propre profil de ce groupe, vous perdriez vos droits d''administration'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
@ -322,7 +319,4 @@ fr:
|
||||
LOGOUT: 'Se déconnecter'
|
||||
LOSTPASSWORDHEADER: 'Mot de passe oublié'
|
||||
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 n’est 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: '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}\_»"
|
||||
PASSWORDSENTTEXT: "Merci\_! Un lien de réinitialisation vient d’être envoyé à «\_{email}\_», à condition que cette adresse existe."
|
||||
|
@ -167,7 +167,4 @@ id:
|
||||
LOGIN: Masuk
|
||||
LOSTPASSWORDHEADER: 'Kata Kunci yang Terlupa'
|
||||
NOTEPAGESECURED: 'Laman ini diamankan. Isikan data berikut untuk dikirimkan hak akses Anda.'
|
||||
NOTERESETLINKINVALID: '<p>Tautan penggantian kata kunci tidak valid atau sudah kadaluarsa.</p><p>Anda dapat meminta yang baru <a href="{link1}">di sini</a> atau mengganti kata kunci setelah Anda <a href="{link2}">masuk</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Isikan alamat email Anda untuk mendapatkan tautan penggantian kata kunci'
|
||||
PASSWORDSENTHEADER: 'Tautan penggantian kata kunci dikirimkan ke ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Terimakasih! Tautan reset telah dikirim ke ''{email}'', berisi informasi akun untuk alamat email ini.'
|
||||
|
@ -166,7 +166,4 @@ id_ID:
|
||||
LOGIN: Masuk
|
||||
LOSTPASSWORDHEADER: 'Kata Kunci yang Terlupa'
|
||||
NOTEPAGESECURED: 'Laman ini diamankan. Isikan data berikut untuk dikirimkan hak akses Anda.'
|
||||
NOTERESETLINKINVALID: '<p>Tautan penggantian kata kunci tidak valid atau sudah kadaluarsa.</p><p>Anda dapat meminta yang baru <a href="{link1}">di sini</a> atau mengganti kata kunci setelah Anda <a href="{link2}">masuk</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Isikan alamat email Anda untuk mendapatkan tautan penggantian kata kunci'
|
||||
PASSWORDSENTHEADER: 'Tautan penggantian kata kunci dikirimkan ke ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Terimakasih! Tautan reset telah dikirim ke ''{email}'', berisi informasi akun untuk alamat email ini.'
|
||||
|
15
lang/it.yml
15
lang/it.yml
@ -95,6 +95,10 @@ it:
|
||||
DeletePermissionsFailure: 'Non hai i permessi per eliminare'
|
||||
Deleted: 'Eliminato {type} {name}'
|
||||
Save: Salva
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Modifica
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Non è possibile rimuovere te stesso da questo gruppo, perderesti i diritti di admin'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
OF: di
|
||||
Page: Pagina
|
||||
@ -145,6 +149,8 @@ it:
|
||||
other: '{count} anni'
|
||||
SilverStripe\ORM\FieldType\DBEnum:
|
||||
ANY: Qualsiasi
|
||||
SilverStripe\ORM\FieldType\DBForeignKey:
|
||||
DROPDOWN_THRESHOLD_FALLBACK_MESSAGE: 'Troppi oggetti correlati; campo di fallback in uso'
|
||||
SilverStripe\ORM\Hierarchy:
|
||||
LIMITED_TITLE: 'Troppi figli ({count})'
|
||||
SilverStripe\ORM\Hierarchy\Hierarchy:
|
||||
@ -195,6 +201,7 @@ it:
|
||||
many_many_Members: Membri
|
||||
SilverStripe\Security\LoginAttempt:
|
||||
Email: 'Indirizzo e-mail'
|
||||
EmailHashed: 'Indirizzo email (hash)'
|
||||
IP: 'Indirizzo IP'
|
||||
PLURALNAME: 'Tentativi d''accesso'
|
||||
PLURALS:
|
||||
@ -236,6 +243,7 @@ it:
|
||||
SUBJECTPASSWORDCHANGED: 'La tua password è stata cambiata'
|
||||
SUBJECTPASSWORDRESET: 'Link per azzerare la tua password'
|
||||
SURNAME: Cognome
|
||||
VALIDATIONADMINLOSTACCESS: 'Non è possibile rimuovere tutti i gruppi admin dal tuo profilo'
|
||||
ValidationIdentifierFailed: 'Non posso sovrascrivere l''utente esistente #{id} con identificatore identico ({name} = {value}))'
|
||||
WELCOMEBACK: 'Bentornato, {firstname}'
|
||||
YOUROLDPASSWORD: 'La tua vecchia password'
|
||||
@ -252,6 +260,8 @@ it:
|
||||
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
|
||||
ERRORWRONGCRED: 'I dettagli forniti non sembrano corretti. Per favore riprovare.'
|
||||
NoPassword: 'Manca la password per questo utente.'
|
||||
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
|
||||
AUTHENTICATORNAME: 'E-mail & Password'
|
||||
SilverStripe\Security\MemberPassword:
|
||||
PLURALNAME: 'Password utenti'
|
||||
PLURALS:
|
||||
@ -313,7 +323,6 @@ it:
|
||||
LOGOUT: Scollegati
|
||||
LOSTPASSWORDHEADER: 'Password smarrita'
|
||||
NOTEPAGESECURED: 'La pagina è protetta. Inserisci le credenziali qui sotto per poter andare avanti.'
|
||||
NOTERESETLINKINVALID: '<p>Il link per azzerare la password non è valido o è scaduto.</p><p>Puoi richiederne uno nuovo <a href="{link1}">qui</a> o cambiare la tua password dopo che ti sei <a href="{link2}">connesso</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Inserisci il tuo indirizzo e-mail e ti verrà inviato un link per poter azzerare la tua password.'
|
||||
PASSWORDSENTHEADER: 'Link per azzeramento della password inviato a ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Grazie! Un link di azzeramento è stato inviato a ''{email}'', fornito un account esistente per questo indirizzo e-mail.'
|
||||
PASSWORDRESETSENTHEADER: 'Link di azzeramento password inviato'
|
||||
PASSWORDRESETSENTTEXT: 'Grazie! Un link di azzeramento è stato inviato, supponendo un account esista a quell''indirizzo e-mail.'
|
||||
|
@ -146,7 +146,4 @@ ja:
|
||||
ERRORPASSWORDPERMISSION: パスワードを変更する為に、ログインしなければなりません!
|
||||
LOGIN: ログイン
|
||||
NOTEPAGESECURED: このページはセキュリティで保護されております証明書キーを下記に入力してください。こちらからすぐに送信します
|
||||
NOTERESETLINKINVALID: '<p>パスワードのリセットリンクは有効でないか期限切れです。</p><p> 新しいパスワードを要求することができます <a href="{link1}"> ここ </a> もしくはパスワードを変更することができます <a href="{link2}"> ログインした後 </a>.</p>'
|
||||
NOTERESETPASSWORD: メールアドレスを入力してください、パスワードをリセットするURLを送信致します
|
||||
PASSWORDSENTHEADER: 'パスワードリセットリンクは ''{email}'' に送信されました'
|
||||
PASSWORDSENTTEXT: 'ありがとうございました! リセットリンクは、''{email}'' に、このアカウントが存在することを前提として送信されました。'
|
||||
|
@ -167,7 +167,4 @@ lt:
|
||||
LOGIN: Prisijungti
|
||||
LOSTPASSWORDHEADER: 'Slaptažodžio atstatymas'
|
||||
NOTEPAGESECURED: 'Šis puslapis yra apsaugotas. Įveskite savo duomenis į žemiau esančius laukelius.'
|
||||
NOTERESETLINKINVALID: '<p>Neteisinga arba negaliojanti slaptažodžio atstatymo nuoroda.</p><p>Galite atsisiųsti naują <a href="{link1}">čia</a> arba pasikeisti slaptažodį po to, kai <a href="{link2}">prisijungsite</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Įveskite savo e. pašto adresą ir atsiųsime slaptažodžio atstatymui skirtą nuorodą'
|
||||
PASSWORDSENTHEADER: 'Slaptažodžio atstatymo nuoroda nusiųsta į ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Atstatymo nuoroda nusiųsta į ''{email}'''
|
||||
|
@ -149,7 +149,4 @@ mi:
|
||||
LOGIN: Takiuru
|
||||
LOSTPASSWORDHEADER: 'Kupuhipa Ngaro'
|
||||
NOTEPAGESECURED: 'Kua ngita tēnā whārangi. Tāurua ō taipitoptio tuakiri ki raro, ā, mā mātou koe e tuku kia haere tonu.'
|
||||
NOTERESETLINKINVALID: '<p>He muhu, kua mōnehu rānei te hono tautuhi kupuhipa anō.</p><p>Ka taea te tono i te mea hōu<a href="{link1}">i konei</a> ka huri rānei i tō kupuhipa ā muri i tō<a href="{link2}">takiuru</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Tāurua tō wāhitau īmēra, mā mātou e tuku tētahi hono ki a koe e taea ai te tautuhi anō i tō kupuhipa'
|
||||
PASSWORDSENTHEADER: 'I tukuna he hono tautuhi kupuhipa anō ki ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Kia ora! Kua tukuna he hono tautuhi anō ki ''{email}'',engari rā kei te tīariari he pūkete mō taua wāhitau īmēra.'
|
||||
|
@ -152,7 +152,4 @@ nb:
|
||||
LOGIN: 'Logg inn'
|
||||
LOSTPASSWORDHEADER: 'Mistet passord'
|
||||
NOTEPAGESECURED: 'Den siden er sikret. Skriv inn gyldig innloggingsinfo så kommer du inn.'
|
||||
NOTERESETLINKINVALID: '<p>Lenken for å nullstille passordet er ugyldig eller utgått.</p><p>Du kan kreve en ny <a href="{link1}">her</a> eller endre passordet etter at du har <a href="{link2}">logget inn</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Skriv inn epostadressen din og vi vil sende deg en lenke som nullstiller passordet.'
|
||||
PASSWORDSENTHEADER: 'Lenke for nullstilling av passord ble sendt til ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Takk! En lenke for å lage nytt passord er sendt til ''{email}'', forutsatt at det eksisterer en konto for denne epostadressen.'
|
||||
|
160
lang/nl.yml
160
lang/nl.yml
@ -1,4 +1,26 @@
|
||||
nl:
|
||||
SilverStripe\Admin\LeftAndMain:
|
||||
VersionUnknown: onbekend
|
||||
SilverStripe\AssetAdmin\Forms\UploadField:
|
||||
Dimensions: Afmetingen
|
||||
EDIT: Bewerken
|
||||
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.'
|
||||
CHANGEPASSWORDTEXT1: 'U heeft het wachtwoord veranderd voor'
|
||||
CHANGEPASSWORDTEXT3: 'Wachtwoord veranderen'
|
||||
HELLO: Hallo
|
||||
SilverStripe\Control\Email\ForgotPasswordEmail_ss:
|
||||
HELLO: Hallo
|
||||
TEXT1: 'Hier is uw'
|
||||
TEXT2: 'link om uw wachtwoord opnieuw aan te maken'
|
||||
TEXT3: voor
|
||||
SilverStripe\Control\RequestProcessor:
|
||||
INVALID_REQUEST: 'Fout bij verwerken'
|
||||
REQUEST_ABORTED: 'Fout bij verwerken (geannuleerd)'
|
||||
SilverStripe\Core\Manifest\VersionProvider:
|
||||
VERSIONUNKNOWN: Onbekend
|
||||
SilverStripe\Forms\CheckboxField:
|
||||
NOANSWER: Nee
|
||||
YESANSWER: Ja
|
||||
@ -8,6 +30,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!'
|
||||
MAXIMUM: 'Een wachtwoord mag maximaal {max} karakters hebben.'
|
||||
SHOWONCLICKTITLE: 'Verander wachtwoord'
|
||||
SilverStripe\Forms\CurrencyField:
|
||||
@ -16,12 +40,20 @@ nl:
|
||||
VALIDDATEFORMAT2: 'Vul een geldig datumformaat in ({format})'
|
||||
VALIDDATEMAXDATE: 'De datum moet ouder of gelijk zijn aan de maximale datum ({date})'
|
||||
VALIDDATEMINDATE: 'De datum moet nieuwer of gelijk zijn aan de minimale datum ({date})'
|
||||
SilverStripe\Forms\DatetimeField:
|
||||
VALIDDATEMAXDATETIME: 'De datum moet ouder of gelijk zijn aan de maximale datum ({datetime})'
|
||||
VALIDDATETIMEFORMAT: 'Vul een geldige datum in ({format})'
|
||||
VALIDDATETIMEMINDATE: 'De datum moet nieuwer of gelijk zijn aan de minimale datum ({datetime})'
|
||||
SilverStripe\Forms\DropdownField:
|
||||
CHOOSE: (Kies)
|
||||
CHOOSE_MODEL: '(Selecteer {name})'
|
||||
SOURCE_VALIDATION: 'Selecteer een optie uit de lijst. {value} is geen geldige keuze.'
|
||||
SilverStripe\Forms\EmailField:
|
||||
VALIDATION: 'Gelieve een e-mailadres in te voeren.'
|
||||
SilverStripe\Forms\FileUploadReceiver:
|
||||
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_FAILED_MESSAGE: 'Er lijkt een technisch probleem te zijn. Klik op de knop terug, vernieuw uw browser, en probeer het opnieuw.'
|
||||
VALIDATIONPASSWORDSDONTMATCH: 'Wachtwoorden komen niet overeen'
|
||||
@ -30,7 +62,10 @@ nl:
|
||||
VALIDATOR: Validator
|
||||
VALIDCURRENCY: 'Vul een geldige munteenheid in'
|
||||
SilverStripe\Forms\FormField:
|
||||
EXAMPLE: 'bijv. {format}'
|
||||
NONE: geen
|
||||
SilverStripe\Forms\FormScaffolder:
|
||||
TABMAIN: Hoofdgedeelte
|
||||
SilverStripe\Forms\GridField\GridField:
|
||||
Add: '{name} toevoegen'
|
||||
CSVEXPORT: 'Exporteer naar CSV'
|
||||
@ -41,6 +76,7 @@ nl:
|
||||
LinkExisting: 'Koppel een bestaand item'
|
||||
NewRecord: 'Nieuw {type}'
|
||||
NoItemsFound: 'Geen items gevonden.'
|
||||
OpenFilter: 'Zoeken en filteren openen'
|
||||
PRINTEDAT: 'Geprint op'
|
||||
PRINTEDBY: 'Geprint door'
|
||||
PlaceHolder: 'Zoek {type}'
|
||||
@ -60,27 +96,72 @@ nl:
|
||||
DeletePermissionsFailure: 'Onvoldoende rechten om te verwijderen'
|
||||
Deleted: '{type} {name} verwijderd'
|
||||
Save: Opslaan
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Bewerken
|
||||
SilverStripe\Forms\GridField\GridFieldFilterHeader:
|
||||
Search: 'Zoek naar "{name}"'
|
||||
SearchFormFaliure: 'Er kon geen zoekformulier worden aangemaakt'
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'U kunt uzelf niet verwijderen van deze groep, omdat u dan geen admin-rechten meer heeft.'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
OF: van
|
||||
Page: Pagina
|
||||
View: Bekijk
|
||||
SilverStripe\Forms\GridField\GridFieldViewButton:
|
||||
VIEW: Bekijk
|
||||
SilverStripe\Forms\MoneyField:
|
||||
FIELDLABELAMOUNT: Aantal
|
||||
FIELDLABELCURRENCY: Munteenheid
|
||||
INVALID_CURRENCY: 'Valuta {currency} is niet toegestaan'
|
||||
SilverStripe\Forms\MultiSelectField:
|
||||
SOURCE_VALIDATION: 'Selecteer een optie uit de lijst. {value} is geen geldige keuze.'
|
||||
SilverStripe\Forms\NullableField:
|
||||
IsNullLabel: 'Is null'
|
||||
SilverStripe\Forms\NumericField:
|
||||
VALIDATION: '''{value}'' is geen getal, enkel getallen worden door dit veld geaccepteerd'
|
||||
SilverStripe\Forms\TimeField:
|
||||
VALIDATEFORMAT: 'Vul een geldig datumformaat in ({format})'
|
||||
SilverStripe\ORM\DataObject:
|
||||
PLURALNAME: 'Data objecten'
|
||||
PLURALS:
|
||||
one: 'Data object'
|
||||
other: '{count} Data objecten'
|
||||
SINGULARNAME: 'Data object'
|
||||
SilverStripe\ORM\FieldType\DBBoolean:
|
||||
ANY: Elke
|
||||
NOANSWER: Nee
|
||||
YESANSWER: Ja
|
||||
SilverStripe\ORM\FieldType\DBDate:
|
||||
DAYS_SHORT_PLURALS:
|
||||
one: '{count} dag'
|
||||
other: '{count} dagen'
|
||||
HOURS_SHORT_PLURALS:
|
||||
one: '{count} uur'
|
||||
other: '{count} uren'
|
||||
LessThanMinuteAgo: 'minder dan één minuut'
|
||||
MINUTES_SHORT_PLURALS:
|
||||
one: '{count} minuut'
|
||||
other: '{count} minuten'
|
||||
MONTHS_SHORT_PLURALS:
|
||||
one: '{count} maand'
|
||||
other: '{count} maanden'
|
||||
SECONDS_SHORT_PLURALS:
|
||||
one: '{count} seconde'
|
||||
other: '{count} seconden'
|
||||
TIMEDIFFAGO: '{difference} geleden'
|
||||
TIMEDIFFIN: 'in {difference}'
|
||||
YEARS_SHORT_PLURALS:
|
||||
one: '{count} jaar'
|
||||
other: '{count} jaren'
|
||||
SilverStripe\ORM\FieldType\DBEnum:
|
||||
ANY: Elke
|
||||
SilverStripe\ORM\FieldType\DBForeignKey:
|
||||
DROPDOWN_THRESHOLD_FALLBACK_MESSAGE: 'Teveel keuzes in de lijst; een alternatief veld wordt getoond.'
|
||||
SilverStripe\ORM\Hierarchy:
|
||||
LIMITED_TITLE: 'Teveel onderliggende items ({count})'
|
||||
SilverStripe\ORM\Hierarchy\Hierarchy:
|
||||
InfiniteLoopNotAllowed: 'Oneindige lus gevonden in "{type}" hiërarchie. Wijzig het hogere niveau om dit op te lossen'
|
||||
LIMITED_TITLE: 'Teveel onderliggende items ({count})'
|
||||
SilverStripe\ORM\ValidationException:
|
||||
DEFAULT_ERROR: Validatiefout
|
||||
SilverStripe\Security\BasicAuth:
|
||||
@ -91,34 +172,60 @@ nl:
|
||||
PASSWORDEXPIRED: '<p>Uw wachtwoord is verlopen. <a target="_top" href="{link}">Kies een nieuw wachtwoord.</a></p>'
|
||||
SilverStripe\Security\CMSSecurity:
|
||||
INVALIDUSER: '<p>Ongeldige gebruiker <a target="_top" href="{link}">Log hier opnieuw in</a> om verder te gaan.</p>'
|
||||
LOGIN_MESSAGE: 'Sessie is verlopen'
|
||||
LOGIN_TITLE: '<p>U kunt verder met wat u aan het doen was, door opnieuw in te loggen.</p>'
|
||||
SUCCESS: Succes
|
||||
SUCCESSCONTENT: '<p>U bent ingelogd. <a target="_top" href="{link}">Klik hier</a> als u niet automatisch wordt doorgestuurd.</p>'
|
||||
SUCCESS_TITLE: 'Inloggen is gelukt'
|
||||
SilverStripe\Security\DefaultAdminService:
|
||||
DefaultAdminFirstname: 'Standaard Beheerder'
|
||||
SilverStripe\Security\Group:
|
||||
AddRole: 'Voeg een rol toe aan deze groep'
|
||||
Code: 'Groep code'
|
||||
DefaultGroupTitleAdministrators: Beheerders
|
||||
DefaultGroupTitleContentAuthors: 'Inhoud Auteurs'
|
||||
Description: 'Omschrijving '
|
||||
GROUPNAME: 'Groep naam'
|
||||
GroupReminder: 'Als u de bovenliggende groep selecteert, neemt deze groep alle rollen over'
|
||||
HierarchyPermsError: 'U moet (ADMIN) rechten hebben om de bovenliggende groep "{group}" toe te kennen'
|
||||
Locked: 'Gesloten?'
|
||||
MEMBERS: Leden
|
||||
NEWGROUP: 'Nieuwe groep'
|
||||
NoRoles: 'Geen rollen gevonden'
|
||||
PERMISSIONS: Rechten
|
||||
PLURALNAME: Groepen
|
||||
PLURALS:
|
||||
one: 'Een groep'
|
||||
other: '{count} groepen'
|
||||
Parent: 'Bovenliggende groep'
|
||||
ROLES: Rollen
|
||||
ROLESDESCRIPTION: 'Rollen zijn logische groeperingen van rechten die in het Rollen tabblad gewijzigd kunnen worden.<br />Rollen worden automatisch overgenomen van bovenliggende groepen.'
|
||||
RolesAddEditLink: 'Rollen beheren'
|
||||
SINGULARNAME: Groep
|
||||
Sort: Sorteer-richting
|
||||
has_many_Permissions: Rechten
|
||||
many_many_Members: Leden
|
||||
SilverStripe\Security\LoginAttempt:
|
||||
Email: 'E-mailadres '
|
||||
EmailHashed: 'E-mailadres (versleuteld)'
|
||||
IP: 'IP adres'
|
||||
PLURALNAME: Inlogpogingen
|
||||
PLURALS:
|
||||
one: 'Een inlogpoging'
|
||||
other: '{count} inlogpogingen'
|
||||
SINGULARNAME: Inlogpogingen
|
||||
Status: Status
|
||||
SilverStripe\Security\Member:
|
||||
ADDGROUP: 'Groep toevoegen'
|
||||
BUTTONCHANGEPASSWORD: 'Wachtwoord veranderen'
|
||||
BUTTONLOGIN: Inloggen
|
||||
BUTTONLOGINOTHER: 'Als iemand anders inloggen'
|
||||
BUTTONLOGOUT: Uitloggen
|
||||
BUTTONLOSTPASSWORD: 'Ik ben mijn wachtwoord vergeten'
|
||||
CONFIRMNEWPASSWORD: 'Bevestig het nieuwe wachtwoord'
|
||||
CONFIRMPASSWORD: 'Bevestig wachtwoord'
|
||||
CURRENT_PASSWORD: 'Huidige wachtwoord'
|
||||
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.'
|
||||
@ -128,13 +235,21 @@ nl:
|
||||
ERRORWRONGCRED: 'De ingevulde gegevens lijken niet correct. Probeer het nog een keer.'
|
||||
FIRSTNAME: Voornaam
|
||||
INTERFACELANG: 'Interface taal'
|
||||
KEEPMESIGNEDIN: 'Houd mij ingelogd'
|
||||
LOGGEDINAS: 'U bent ingelogd als {name}.'
|
||||
NEWPASSWORD: 'Nieuw wachtwoord'
|
||||
PASSWORD: Wachtwoord
|
||||
PASSWORDEXPIRED: 'Uw wachtwoord is verlopen. Kies een nieuw wachtwoord.'
|
||||
PLURALNAME: Leden
|
||||
PLURALS:
|
||||
one: 'Een lid'
|
||||
other: '{count} leden'
|
||||
REMEMBERME: 'Onthoud mij voor volgende keer? (voor {count} dagen op dit apparaat)'
|
||||
SINGULARNAME: Lid
|
||||
SUBJECTPASSWORDCHANGED: 'Uw wachtwoord is veranderd'
|
||||
SUBJECTPASSWORDRESET: 'Link om uw wachtwoord opnieuw aan te maken'
|
||||
SURNAME: Achternaam
|
||||
VALIDATIONADMINLOSTACCESS: 'Niet mogelijk om alle admin-groepen te verwijderen van uw profiel'
|
||||
ValidationIdentifierFailed: 'Een bestaande gebruiker #{id} kan niet dezelfde unieke velden hebben ({name} = {value}))'
|
||||
WELCOMEBACK: 'Welkom terug, {firstname}'
|
||||
YOUROLDPASSWORD: 'Uw oude wachtwoord'
|
||||
@ -143,15 +258,38 @@ nl:
|
||||
db_LockedOutUntil: 'Gesloten tot'
|
||||
db_Password: Wachtwoord
|
||||
db_PasswordExpiry: 'Wachtwoord vervaldatum'
|
||||
SilverStripe\Security\MemberAuthenticator\CMSMemberLoginForm:
|
||||
AUTHENTICATORNAME: Inlogformulier
|
||||
BUTTONFORGOTPASSWORD: 'Wachtwoord vergeten'
|
||||
BUTTONLOGIN: 'Opnieuw inloggen'
|
||||
BUTTONLOGOUT: Uitloggen
|
||||
SilverStripe\Security\MemberAuthenticator\MemberAuthenticator:
|
||||
ERRORWRONGCRED: 'De ingevulde gegevens lijken niet correct. Probeer het nog een keer.'
|
||||
NoPassword: 'Er is geen wachtwoord voor deze gebruiker.'
|
||||
SilverStripe\Security\MemberAuthenticator\MemberLoginForm:
|
||||
AUTHENTICATORNAME: 'E-mail & wachtwoord'
|
||||
SilverStripe\Security\MemberPassword:
|
||||
PLURALNAME: Gebruikerswachtwoorden
|
||||
PLURALS:
|
||||
one: 'Een gebruikerswachtwoord'
|
||||
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.'
|
||||
TOOSHORT: 'Het wachtwoord is te kort, het moet minimaal {minimum} karakters hebben'
|
||||
SilverStripe\Security\Permission:
|
||||
AdminGroup: Beheerder
|
||||
CMS_ACCESS_CATEGORY: 'CMS toegang'
|
||||
CONTENT_CATEGORY: Inhoudsrechten
|
||||
FULLADMINRIGHTS: 'Volledige admin rechten'
|
||||
FULLADMINRIGHTS_HELP: 'Impliceert en overstemt alle andere toegewezen rechten.'
|
||||
PERMISSIONS_CATEGORY: 'Rollen en toegangsrechten'
|
||||
PLURALNAME: Rechten
|
||||
PLURALS:
|
||||
one: Machtiging
|
||||
other: '{count} rechten'
|
||||
SINGULARNAME: Machtiging
|
||||
UserPermissionsIntro: 'Groepen aan deze gebruiker toewijzen zullen diens permissies aanpassen. Zie de sectie Groepen voor meer informatie over machtigingen voor afzonderlijke groepen.'
|
||||
SilverStripe\Security\PermissionCheckboxSetField:
|
||||
AssignedTo: 'toegewezen aan "{title}"'
|
||||
@ -161,21 +299,37 @@ nl:
|
||||
SilverStripe\Security\PermissionRole:
|
||||
OnlyAdminCanApply: 'Alleen admin kan doorvoeren'
|
||||
PLURALNAME: Rollen
|
||||
PLURALS:
|
||||
one: 'Een rol'
|
||||
other: '{count} rollen'
|
||||
SINGULARNAME: Rol
|
||||
Title: Titel
|
||||
SilverStripe\Security\PermissionRoleCode:
|
||||
PLURALNAME: 'Permissie codes'
|
||||
PLURALS:
|
||||
one: 'Een permissiecode'
|
||||
other: '{count} permissiecodes'
|
||||
PermsError: 'U moet (ADMIN) rechten hebben om de code "{code}" toe te kennen'
|
||||
SINGULARNAME: Permissiecode
|
||||
SilverStripe\Security\RememberLoginHash:
|
||||
PLURALNAME: 'Versleutelde logins'
|
||||
PLURALS:
|
||||
one: 'Een versleutelde login'
|
||||
other: '{count} versleutelde logins'
|
||||
SINGULARNAME: 'Versleutelde login'
|
||||
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'
|
||||
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!'
|
||||
LOGIN: 'Meld aan'
|
||||
LOGOUT: Uitloggen
|
||||
LOSTPASSWORDHEADER: 'Wachtwoord vergeten'
|
||||
NOTEPAGESECURED: 'Deze pagina is beveiligd. Voer uw gegevens in en u wordt automatisch doorgestuurd.'
|
||||
NOTERESETLINKINVALID: '<p>De link om uw wachtwoord te kunnen wijzigen is niet meer geldig.</p><p>U kunt <a href="{link1}">een nieuwe link aanvragen</a> of uw wachtwoord aanpassen door <a href="{link2}">in te loggen</a>.</p>'
|
||||
NOTERESETLINKINVALID: '<p>De reset link is ongeldig of komen te vervallen.</p><p>Je kan <a href="{link1}">hier</a> een nieuwe link aanvragen of het wachtwoord veranderen nadat je bent <a href="{link2}">ingelogd</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Voer uw e-mailadres in en we sturen een link waarmee u een nieuw wachtwoord kunt instellen.'
|
||||
PASSWORDSENTHEADER: 'Wachtwoord herstel link verzonden naar {email}'
|
||||
PASSWORDSENTTEXT: 'Bedankt! Er is een link verstuurd naar {email} om uw wachtwoord opnieuw in te stellen, in de veronderstelling dat er een account bestaat voor dit e-mailadres.'
|
||||
PASSWORDRESETSENTHEADER: 'link om uw wachtwoord opnieuw aan te maken'
|
||||
PASSWORDRESETSENTTEXT: 'Bedankt! Er is een link verstuurd om uw wachtwoord opnieuw in te stellen (mits het mailadres reeds bekend is bij ons).'
|
||||
|
@ -84,7 +84,6 @@ pl:
|
||||
RelationSearch: 'Wyszukiwanie powiązań'
|
||||
ResetFilter: Resetuj
|
||||
SilverStripe\Forms\GridField\GridFieldDeleteAction:
|
||||
DELETE_DESCRIPTION: Usuń
|
||||
Delete: Usuń
|
||||
DeletePermissionsFailure: 'Brak uprawnień do usuwania'
|
||||
EditPermissionsFailure: 'Nie masz uprawnień, aby odłączyć rekord'
|
||||
@ -96,8 +95,6 @@ pl:
|
||||
DeletePermissionsFailure: 'Brak uprawnień do usuwania'
|
||||
Deleted: 'Usunięto {type} {name}'
|
||||
Save: Zapisz
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton_ss:
|
||||
EDIT: Edytuj
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Nie możesz usunąć siebie z tej grupy, stracone zostałby prawa administratora'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
@ -352,7 +349,4 @@ pl:
|
||||
LOGOUT: 'Wyloguj się'
|
||||
LOSTPASSWORDHEADER: 'Nie pamiętam hasła'
|
||||
NOTEPAGESECURED: 'Ta strona jest zabezpieczona. Wpisz swoje dane a my wyślemy Ci potwierdzenie niebawem'
|
||||
NOTERESETLINKINVALID: '<p>Link resetujący hasło wygasł lub jest nieprawidłowy.</p><p>Możesz poprosić o nowy <a href="{link1}">tutaj</a> lub zmień swoje hasło po <a href="{link2}">zalogowaniu się</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Wpisz adres e-mail, na który mamy wysłać link gdzie możesz zresetować swoje hasło'
|
||||
PASSWORDSENTHEADER: 'Link resetujący hasła został wysłany do ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Dziękujemy! Link resetujący hasło został wysłany do ''{email}'', o ile konto użytkownika dla takiego e-maila istnieje.'
|
||||
|
@ -339,7 +339,4 @@ ru:
|
||||
LOGOUT: Выйти
|
||||
LOSTPASSWORDHEADER: 'Восстановление пароля'
|
||||
NOTEPAGESECURED: 'Эта страница защищена. Пожалуйста, введите свои учетные данные для входа.'
|
||||
NOTERESETLINKINVALID: '<p>Неверная ссылка переустановки пароля или время действия ссылки истекло.</p><p>Вы можете повторно запросить ссылку, щелкнув <a href="{link1}">здесь</a>, или поменять пароль, <a href="{link2}">войдя в систему</a>.</p> '
|
||||
NOTERESETPASSWORD: 'Введите Ваш адрес email, и Вам будет отправлена ссылка, по которой Вы сможете переустановить свой пароль'
|
||||
PASSWORDSENTHEADER: 'Ссылка для переустановки пароля выслана на ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Ссылка переустановки пароля была выслана на адрес ''{email}'' (письмо дойдет до получателя только в том случае, если аккаунт с таким электронным адресом действительно зарегистрирован).'
|
||||
|
@ -228,7 +228,4 @@ sk:
|
||||
LOGIN: Prihlásiť
|
||||
LOSTPASSWORDHEADER: 'Zabudnuté heslo'
|
||||
NOTEPAGESECURED: 'Táto stránka je zabezpečená. Zadajte svoje prihlasovacie údaje a my Vám zároveň pošleme práva.'
|
||||
NOTERESETLINKINVALID: '<p>Odkaz na resetovanie hesla nie je platný alebo je vypršala jeho platnosť.</p><p>Môžete požiadať o nový <a href="{link1}">tu</a> alebo zmeňte svoje heslo po <a href="{link2}">prihlásení</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Zadajte svoju e-mailovú adresu a my Vám pošleme odkaz na resetovanie hesla'
|
||||
PASSWORDSENTHEADER: 'Odkaz na resetovanie hesla bol odoslaný na ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Ďakujeme! Resetovací odkaz bol odoslaný na ''''{email}'''', pokiaľ účet existuje pre túto emailovú adresu.'
|
||||
|
@ -135,7 +135,4 @@ sl:
|
||||
LOGIN: Prijava
|
||||
LOSTPASSWORDHEADER: 'Izgubljeno geslo'
|
||||
NOTEPAGESECURED: 'Stran je zaščitena. Da bi lahko nadaljevali, vpišite svoje podatke.'
|
||||
NOTERESETLINKINVALID: '<p>Povezava za ponastavitev gesla je napačna ali pa je njena veljavnost potekla.</p><p><a href="{link1}">Tukaj</a> lahko zaprosite za novo povezavo or pa zamenjate geslo, ko <a href="{link2}">se prijavite v sistem</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Vpišite e-naslov, na katerega vam bomo poslali povezavo za ponastavitev gesla'
|
||||
PASSWORDSENTHEADER: 'Povezava za ponastavitev gesla je bila poslana na e-naslov ''{email}''.'
|
||||
PASSWORDSENTTEXT: 'Hvala! Povezava za ponastavitev gesla je bila poslana na e-naslov ''{email}'', ki je naveden kot e-naslov vašega računa. '
|
||||
|
@ -151,7 +151,4 @@ sr:
|
||||
ERRORPASSWORDPERMISSION: 'Морате да будете пријављени да бисте променили своју лозинку!'
|
||||
LOGIN: Пријављивање
|
||||
NOTEPAGESECURED: 'Ова страна је обезбеђена. Унесите своје податке и ми ћемо вам послати садржај.'
|
||||
NOTERESETLINKINVALID: '<p>Линк за ресетовање лозинке је погрешан или је истекло време за његово коришћење.</p><p>Можете да захтевате нови <a href="{link1}">овде</a> или да промените Вашу лозинку након што се <a href="{link2}">пријавите</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Унесите своју адресу е-поште и ми ћемо вам послати линк помоћу којег можете да промените своју лозинку'
|
||||
PASSWORDSENTHEADER: 'Линк за ресетовање лозинке послат је на адресу е-поште: ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Хвала Вам! Линк за ресетовање лозинке је послат не адресу е-поште ''{email}''. Порука ће стићи примаоцу само ако постоји регистрован налог са том адресом е-поште.'
|
||||
|
@ -150,7 +150,4 @@ sr@latin:
|
||||
ERRORPASSWORDPERMISSION: 'Morate da budete prijavljeni da biste promenili svoju lozinku!'
|
||||
LOGIN: Prijavljivanje
|
||||
NOTEPAGESECURED: 'Ova strana je obezbeđena. Unesite svoje podatke i mi ćemo vam poslati sadržaj.'
|
||||
NOTERESETLINKINVALID: '<p>Link za resetovanje lozinke je pogrešan ili je isteklo vreme za njegovo korišćenje.</p><p>Možete da zahtevate novi <a href="{link1}">ovde</a> ili da promenite Vašu lozinku nakon što se <a href="{link2}">prijavite</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Unesite svoju adresu e-pošte i mi ćemo vam poslati link pomoću kojeg možete da promenite svoju lozinku'
|
||||
PASSWORDSENTHEADER: 'Link za resetovanje lozinke poslat je na adresu e-pošte: ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Hvala Vam! Link za resetovanje lozinke je poslat ne adresu e-pošte ''{email}''. Poruka će stići primaocu samo ako postoji registrovan nalog sa tom adresom e-pošte.'
|
||||
|
@ -150,7 +150,4 @@ sr_RS:
|
||||
ERRORPASSWORDPERMISSION: 'Морате да будете пријављени да бисте променили своју лозинку!'
|
||||
LOGIN: Пријављивање
|
||||
NOTEPAGESECURED: 'Ова страна је обезбеђена. Унесите своје податке и ми ћемо вам послати садржај.'
|
||||
NOTERESETLINKINVALID: '<p>Линк за ресетовање лозинке је погрешан или је истекло време за његово коришћење.</p><p>Можете да захтевате нови <a href="{link1}">овде</a> или да промените Вашу лозинку након што се <a href="{link2}">пријавите</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Унесите своју адресу е-поште и ми ћемо вам послати линк помоћу којег можете да промените своју лозинку'
|
||||
PASSWORDSENTHEADER: 'Линк за ресетовање лозинке послат је на адресу е-поште: ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Хвала Вам! Линк за ресетовање лозинке је послат не адресу е-поште ''{email}''. Порука ће стићи примаоцу само ако постоји регистрован налог са том адресом е-поште.'
|
||||
|
@ -151,7 +151,4 @@ sr_RS@latin:
|
||||
ERRORPASSWORDPERMISSION: 'Morate da budete prijavljeni da biste promenili svoju lozinku!'
|
||||
LOGIN: Prijavljivanje
|
||||
NOTEPAGESECURED: 'Ova strana je obezbeđena. Unesite svoje podatke i mi ćemo vam poslati sadržaj.'
|
||||
NOTERESETLINKINVALID: '<p>Link za resetovanje lozinke je pogrešan ili je isteklo vreme za njegovo korišćenje.</p><p>Možete da zahtevate novi <a href="{link1}">ovde</a> ili da promenite Vašu lozinku nakon što se <a href="{link2}">prijavite</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Unesite svoju adresu e-pošte i mi ćemo vam poslati link pomoću kojeg možete da promenite svoju lozinku'
|
||||
PASSWORDSENTHEADER: 'Link za resetovanje lozinke poslat je na adresu e-pošte: ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Hvala Vam! Link za resetovanje lozinke je poslat ne adresu e-pošte ''{email}''. Poruka će stići primaocu samo ako postoji registrovan nalog sa tom adresom e-pošte.'
|
||||
|
19
lang/sv.yml
19
lang/sv.yml
@ -93,7 +93,12 @@ sv:
|
||||
DeletePermissionsFailure: 'Rättighet för att radera saknas'
|
||||
Deleted: 'Raderade {type} {name}'
|
||||
Save: Spara
|
||||
SilverStripe\Forms\GridField\GridFieldEditButton:
|
||||
EDIT: Ändra
|
||||
SilverStripe\Forms\GridField\GridFieldGroupDeleteAction:
|
||||
UnlinkSelfFailure: 'Du kan inte radera dig själv från den här gruppen, då du då kommer att förlora dina admin-rättigheter'
|
||||
SilverStripe\Forms\GridField\GridFieldPaginator:
|
||||
OF: av
|
||||
Page: Sida
|
||||
View: Visa
|
||||
SilverStripe\Forms\MoneyField:
|
||||
@ -108,6 +113,12 @@ sv:
|
||||
VALIDATION: '''{value}'' är inget nummer, bara siffror (utan mellanslag) kan accepteras för det här fältet'
|
||||
SilverStripe\Forms\TimeField:
|
||||
VALIDATEFORMAT: 'Var god att ange tid i ett giltigt format ({format})'
|
||||
SilverStripe\ORM\DataObject:
|
||||
PLURALNAME: Dataobjekt
|
||||
PLURALS:
|
||||
one: 'Ett dataobjekt'
|
||||
other: '{count} Dataobjekt'
|
||||
SINGULARNAME: Dataobjekt
|
||||
SilverStripe\ORM\FieldType\DBBoolean:
|
||||
ANY: 'Vilken som helst'
|
||||
NOANSWER: Nej
|
||||
@ -136,6 +147,8 @@ sv:
|
||||
other: '{count} år'
|
||||
SilverStripe\ORM\FieldType\DBEnum:
|
||||
ANY: 'Vilken som helst'
|
||||
SilverStripe\ORM\FieldType\DBForeignKey:
|
||||
DROPDOWN_THRESHOLD_FALLBACK_MESSAGE: 'För många relaterade objekt; använder fallback-fält'
|
||||
SilverStripe\ORM\Hierarchy:
|
||||
LIMITED_TITLE: 'För många barn ({count})'
|
||||
SilverStripe\ORM\Hierarchy\Hierarchy:
|
||||
@ -221,6 +234,7 @@ sv:
|
||||
PLURALS:
|
||||
one: 'En medlem'
|
||||
other: '{count} medlemmar'
|
||||
REMEMBERME: 'Kom ihåg mig nästa gång? (i {count} dagar på denna enhet)'
|
||||
SINGULARNAME: Medlem
|
||||
SUBJECTPASSWORDCHANGED: 'Ditt lösenord har ändrats'
|
||||
SUBJECTPASSWORDRESET: 'Din återställningslänk'
|
||||
@ -288,7 +302,6 @@ sv:
|
||||
LOGOUT: 'Logga ut'
|
||||
LOSTPASSWORDHEADER: 'Bortglömt lösenord'
|
||||
NOTEPAGESECURED: 'Den här sidan är låst. Fyll i dina uppgifter nedan så skickar vi dig vidare.'
|
||||
NOTERESETLINKINVALID: '<p>Återställningslänk för lösenord är felaktig eller för gammal.</p><p>Du kan begära en ny <a href="{link1}">här</a> eller ändra ditt lösenord när du <a href="{link2}">loggat in</a>.</p>'
|
||||
NOTERESETPASSWORD: 'Ange din e-postadress så skickar vi en länk med vilken du kan återställa ditt lösenord'
|
||||
PASSWORDSENTHEADER: 'Återställningslänk för lösenord har skickats till ''{email}'''
|
||||
PASSWORDSENTTEXT: 'Tack en återställningslänk har skickats till ''{email}'', förutsatt att ett konto med den addressen finns.'
|
||||
PASSWORDRESETSENTHEADER: 'Återställningslänk för lösenord skickad'
|
||||
PASSWORDRESETSENTTEXT: 'Tack. En återställningslänk har skickats, förutsatt att ett konto med denna adress existerar.'
|
||||
|
@ -166,7 +166,4 @@ zh:
|
||||
LOGIN: 登录
|
||||
LOSTPASSWORDHEADER: 忘记密码
|
||||
NOTEPAGESECURED: 该页面受安全保护。请在下面输入您的证书,然后我们会立即将您引导至该页面。
|
||||
NOTERESETLINKINVALID: '<p>密码重设链接无效或已过期。</p><p>您可以在<a href="{link1}">这里</a> 要求一个新的或在<a href="{link2}">登录</a>后更改您的密码。</p>'
|
||||
NOTERESETPASSWORD: 请输入您的电子邮件地址,然后我们会将一个链接发送给您,您可以用它来重设您的密码
|
||||
PASSWORDSENTHEADER: '密码重设链接已发送至''{email}'''
|
||||
PASSWORDSENTTEXT: '谢谢!复位链接已发送到 ''{email}'',假定此电子邮件地址存在一个帐户。'
|
||||
|
@ -41,7 +41,7 @@ class HTTPApplication implements Application
|
||||
*/
|
||||
public function handle(HTTPRequest $request)
|
||||
{
|
||||
$flush = array_key_exists('flush', $request->getVars()) || strpos($request->getURL(), 'dev/build') === 0;
|
||||
$flush = array_key_exists('flush', $request->getVars()) || ($request->getURL() === 'dev/build');
|
||||
|
||||
// Ensure boot is invoked
|
||||
return $this->execute($request, function (HTTPRequest $request) {
|
||||
|
192
src/Core/Startup/AbstractConfirmationToken.php
Normal file
192
src/Core/Startup/AbstractConfirmationToken.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Startup;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Security\RandomGenerator;
|
||||
|
||||
/**
|
||||
* Shared functionality for token-based authentication of potentially dangerous URLs or query
|
||||
* string parameters
|
||||
*
|
||||
* @internal This class is designed specifically for use pre-startup and may change without warning
|
||||
*/
|
||||
abstract class AbstractConfirmationToken
|
||||
{
|
||||
/**
|
||||
* @var HTTPRequest
|
||||
*/
|
||||
protected $request = null;
|
||||
|
||||
/**
|
||||
* The validated and checked token for this parameter
|
||||
*
|
||||
* @var string|null A string value, or null if either not provided or invalid
|
||||
*/
|
||||
protected $token = null;
|
||||
|
||||
/**
|
||||
* Given a list of token names, suppress all tokens that have not been validated, and
|
||||
* return the non-validated token with the highest priority
|
||||
*
|
||||
* @param array $keys List of token keys in ascending priority (low to high)
|
||||
* @param HTTPRequest $request
|
||||
* @return static The token container for the unvalidated $key given with the highest priority
|
||||
*/
|
||||
public static function prepare_tokens($keys, HTTPRequest $request)
|
||||
{
|
||||
$target = null;
|
||||
foreach ($keys as $key) {
|
||||
$token = new static($key, $request);
|
||||
// Validate this token
|
||||
if ($token->reloadRequired() || $token->reloadRequiredIfError()) {
|
||||
$token->suppress();
|
||||
$target = $token;
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a local filesystem path to store a token
|
||||
*
|
||||
* @param $token
|
||||
* @return string
|
||||
*/
|
||||
protected function pathForToken($token)
|
||||
{
|
||||
return TEMP_PATH . DIRECTORY_SEPARATOR . 'token_' . preg_replace('/[^a-z0-9]+/', '', $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new random token and store it
|
||||
*
|
||||
* @return string Token name
|
||||
*/
|
||||
protected function genToken()
|
||||
{
|
||||
// Generate a new random token (as random as possible)
|
||||
$rg = new RandomGenerator();
|
||||
$token = $rg->randomToken('md5');
|
||||
|
||||
// Store a file in the session save path (safer than /tmp, as open_basedir might limit that)
|
||||
file_put_contents($this->pathForToken($token), $token);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the necessary token provided for this parameter?
|
||||
* A value must be provided for the token
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function tokenProvided()
|
||||
{
|
||||
return !empty($this->token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a token
|
||||
*
|
||||
* @param string $token
|
||||
* @return boolean True if the token is valid
|
||||
*/
|
||||
protected function checkToken($token)
|
||||
{
|
||||
if (!$token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->pathForToken($token);
|
||||
$content = null;
|
||||
|
||||
if (file_exists($file)) {
|
||||
$content = file_get_contents($file);
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
return $content === $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get redirect url, excluding querystring
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function currentURL()
|
||||
{
|
||||
return Controller::join_links(Director::baseURL(), $this->request->getURL(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a reload of the request with the token included
|
||||
*
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function reloadWithToken()
|
||||
{
|
||||
$location = $this->redirectURL();
|
||||
$locationJS = Convert::raw2js($location);
|
||||
$locationATT = Convert::raw2att($location);
|
||||
$body = <<<HTML
|
||||
<script>location.href='$locationJS';</script>
|
||||
<noscript><meta http-equiv="refresh" content="0; url=$locationATT"></noscript>
|
||||
You are being redirected. If you are not redirected soon, <a href="$locationATT">click here to continue</a>
|
||||
HTML;
|
||||
|
||||
// Build response
|
||||
$result = new HTTPResponse($body);
|
||||
$result->redirect($location);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this parameter requested without a valid token?
|
||||
*
|
||||
* @return bool True if the parameter is given without a valid token
|
||||
*/
|
||||
abstract public function reloadRequired();
|
||||
|
||||
/**
|
||||
* Check if this token is provided either in the backurl, or directly,
|
||||
* but without a token
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function reloadRequiredIfError();
|
||||
|
||||
/**
|
||||
* Suppress the current parameter for the duration of this request
|
||||
*/
|
||||
abstract public function suppress();
|
||||
|
||||
/**
|
||||
* Determine the querystring parameters to include
|
||||
*
|
||||
* @param bool $includeToken Include the token value?
|
||||
* @return array List of querystring parameters, possibly including token parameter
|
||||
*/
|
||||
abstract public function params($includeToken = true);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getRedirectUrlBase();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getRedirectUrlParams();
|
||||
|
||||
/**
|
||||
* Get redirection URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function redirectURL();
|
||||
}
|
178
src/Core/Startup/ConfirmationTokenChain.php
Normal file
178
src/Core/Startup/ConfirmationTokenChain.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Startup;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Convert;
|
||||
|
||||
/**
|
||||
* A chain of confirmation tokens to be validated on each request. This allows the application to
|
||||
* check multiple tokens at once without having to potentially redirect the user for each of them
|
||||
*
|
||||
* @internal This class is designed specifically for use pre-startup and may change without warning
|
||||
*/
|
||||
class ConfirmationTokenChain
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokens = [];
|
||||
|
||||
/**
|
||||
* @param AbstractConfirmationToken $token
|
||||
*/
|
||||
public function pushToken(AbstractConfirmationToken $token)
|
||||
{
|
||||
$this->tokens[] = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all tokens that require a redirect
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
protected function filteredTokens()
|
||||
{
|
||||
foreach ($this->tokens as $token) {
|
||||
if ($token->reloadRequired() || $token->reloadRequiredIfError()) {
|
||||
yield $token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function suppressionRequired()
|
||||
{
|
||||
foreach ($this->tokens as $token) {
|
||||
if ($token->reloadRequired()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppress URLs & GET vars from tokens that require a redirect
|
||||
*/
|
||||
public function suppressTokens()
|
||||
{
|
||||
foreach ($this->filteredTokens() as $token) {
|
||||
$token->suppress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function reloadRequired()
|
||||
{
|
||||
foreach ($this->tokens as $token) {
|
||||
if ($token->reloadRequired()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function reloadRequiredIfError()
|
||||
{
|
||||
foreach ($this->tokens as $token) {
|
||||
if ($token->reloadRequiredIfError()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $includeToken
|
||||
* @return array
|
||||
*/
|
||||
public function params($includeToken = true)
|
||||
{
|
||||
$params = [];
|
||||
foreach ($this->tokens as $token) {
|
||||
$params = array_merge($params, $token->params($includeToken));
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the URL we want to redirect to, excluding query string parameters. This may
|
||||
* be the same URL (with a token to be added outside this method), or to a different
|
||||
* URL if the current one has been suppressed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRedirectUrlBase()
|
||||
{
|
||||
// URLConfirmationTokens may alter the URL to suppress the URL they're protecting,
|
||||
// so we need to ensure they're inspected last and therefore take priority
|
||||
$tokens = iterator_to_array($this->filteredTokens(), false);
|
||||
usort($tokens, function ($a, $b) {
|
||||
return ($a instanceof URLConfirmationToken) ? 1 : 0;
|
||||
});
|
||||
|
||||
$urlBase = Director::baseURL();
|
||||
foreach ($tokens as $token) {
|
||||
$urlBase = $token->getRedirectUrlBase();
|
||||
}
|
||||
|
||||
return $urlBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collate GET vars from all token providers that need to apply a token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRedirectUrlParams()
|
||||
{
|
||||
$params = [];
|
||||
foreach ($this->filteredTokens() as $token) {
|
||||
$params = array_merge($params, $token->getRedirectUrlParams());
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function redirectURL()
|
||||
{
|
||||
$params = http_build_query($this->getRedirectUrlParams());
|
||||
return Controller::join_links($this->getRedirectUrlBase(), '?' . $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function reloadWithTokens()
|
||||
{
|
||||
$location = $this->redirectURL();
|
||||
$locationJS = Convert::raw2js($location);
|
||||
$locationATT = Convert::raw2att($location);
|
||||
$body = <<<HTML
|
||||
<script>location.href='$locationJS';</script>
|
||||
<noscript><meta http-equiv="refresh" content="0; url=$locationATT"></noscript>
|
||||
You are being redirected. If you are not redirected soon, <a href="$locationATT">click here to continue</a>
|
||||
HTML;
|
||||
|
||||
// Build response
|
||||
$result = new HTTPResponse($body);
|
||||
$result->redirect($location);
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -15,8 +15,7 @@ use Exception;
|
||||
* $chain = new ErrorControlChain();
|
||||
* $chain->then($callback1)->then($callback2)->thenIfErrored($callback3)->execute();
|
||||
*
|
||||
* WARNING: This class is experimental and designed specifically for use pre-startup.
|
||||
* It will likely be heavily refactored before the release of 3.2
|
||||
* @internal This class is designed specifically for use pre-startup and may change without warning
|
||||
*/
|
||||
class ErrorControlChain
|
||||
{
|
||||
|
@ -12,6 +12,8 @@ use SilverStripe\Security\Security;
|
||||
|
||||
/**
|
||||
* Decorates application bootstrapping with errorcontrolchain
|
||||
*
|
||||
* @internal This class is designed specifically for use pre-startup and may change without warning
|
||||
*/
|
||||
class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
{
|
||||
@ -30,27 +32,42 @@ class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HTTPRequest $request
|
||||
* @return ConfirmationTokenChain
|
||||
*/
|
||||
protected function prepareConfirmationTokenChain(HTTPRequest $request)
|
||||
{
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken(new URLConfirmationToken('dev/build', $request));
|
||||
|
||||
foreach (['isTest', 'isDev', 'flush'] as $parameter) {
|
||||
$chain->pushToken(new ParameterConfirmationToken($parameter, $request));
|
||||
}
|
||||
|
||||
return $chain;
|
||||
}
|
||||
|
||||
public function process(HTTPRequest $request, callable $next)
|
||||
{
|
||||
$result = null;
|
||||
|
||||
// Prepare tokens and execute chain
|
||||
$reloadToken = ParameterConfirmationToken::prepare_tokens(
|
||||
['isTest', 'isDev', 'flush'],
|
||||
$request
|
||||
);
|
||||
$chain = new ErrorControlChain();
|
||||
$chain
|
||||
->then(function () use ($request, $chain, $reloadToken, $next, &$result) {
|
||||
// If no redirection is necessary then we can disable error supression
|
||||
if (!$reloadToken) {
|
||||
$chain->setSuppression(false);
|
||||
$confirmationTokenChain = $this->prepareConfirmationTokenChain($request);
|
||||
$errorControlChain = new ErrorControlChain();
|
||||
$errorControlChain
|
||||
->then(function () use ($request, $errorControlChain, $confirmationTokenChain, $next, &$result) {
|
||||
if ($confirmationTokenChain->suppressionRequired()) {
|
||||
$confirmationTokenChain->suppressTokens();
|
||||
} else {
|
||||
// If no redirection is necessary then we can disable error supression
|
||||
$errorControlChain->setSuppression(false);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if a token is requesting a redirect
|
||||
if ($reloadToken && $reloadToken->reloadRequired()) {
|
||||
$result = $this->safeReloadWithToken($request, $reloadToken);
|
||||
if ($confirmationTokenChain && $confirmationTokenChain->reloadRequired()) {
|
||||
$result = $this->safeReloadWithTokens($request, $confirmationTokenChain);
|
||||
} else {
|
||||
// If no reload necessary, process application
|
||||
$result = call_user_func($next, $request);
|
||||
@ -60,10 +77,16 @@ class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
}
|
||||
})
|
||||
// Finally if a token was requested but there was an error while figuring out if it's allowed, do it anyway
|
||||
->thenIfErrored(function () use ($reloadToken) {
|
||||
if ($reloadToken && $reloadToken->reloadRequiredIfError()) {
|
||||
$result = $reloadToken->reloadWithToken();
|
||||
$result->output();
|
||||
->thenIfErrored(function () use ($confirmationTokenChain) {
|
||||
if ($confirmationTokenChain && $confirmationTokenChain->reloadRequiredIfError()) {
|
||||
try {
|
||||
// Reload requires manual boot
|
||||
$this->getApplication()->getKernel()->boot(false);
|
||||
} finally {
|
||||
// Given we're in an error state here, try to continue even if the kernel boot fails
|
||||
$result = $confirmationTokenChain->reloadWithTokens();
|
||||
$result->output();
|
||||
}
|
||||
}
|
||||
})
|
||||
->execute();
|
||||
@ -75,10 +98,10 @@ class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
* or authentication is impossible.
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @param ParameterConfirmationToken $reloadToken
|
||||
* @param ConfirmationTokenChain $confirmationTokenChain
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
protected function safeReloadWithToken(HTTPRequest $request, $reloadToken)
|
||||
protected function safeReloadWithTokens(HTTPRequest $request, ConfirmationTokenChain $confirmationTokenChain)
|
||||
{
|
||||
// Safe reload requires manual boot
|
||||
$this->getApplication()->getKernel()->boot(false);
|
||||
@ -87,9 +110,9 @@ class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
$request->getSession()->init($request);
|
||||
|
||||
// Request with ErrorDirector
|
||||
$result = ErrorDirector::singleton()->handleRequestWithToken(
|
||||
$result = ErrorDirector::singleton()->handleRequestWithTokenChain(
|
||||
$request,
|
||||
$reloadToken,
|
||||
$confirmationTokenChain,
|
||||
$this->getApplication()->getKernel()
|
||||
);
|
||||
if ($result) {
|
||||
@ -97,8 +120,8 @@ class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
}
|
||||
|
||||
// Fail and redirect the user to the login page
|
||||
$params = array_merge($request->getVars(), $reloadToken->params(false));
|
||||
$backURL = $request->getURL() . '?' . http_build_query($params);
|
||||
$params = array_merge($request->getVars(), $confirmationTokenChain->params(false));
|
||||
$backURL = $confirmationTokenChain->getRedirectUrlBase() . '?' . http_build_query($params);
|
||||
$loginPage = Director::absoluteURL(Security::config()->get('login_url'));
|
||||
$loginPage .= "?BackURL=" . urlencode($backURL);
|
||||
$result = new HTTPResponse();
|
||||
|
@ -21,18 +21,21 @@ class ErrorDirector extends Director
|
||||
* Redirect with token if allowed, or null if not allowed
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @param ParameterConfirmationToken $token
|
||||
* @param ConfirmationTokenChain $confirmationTokenChain
|
||||
* @param Kernel $kernel
|
||||
* @return null|HTTPResponse Redirection response, or null if not able to redirect
|
||||
*/
|
||||
public function handleRequestWithToken(HTTPRequest $request, ParameterConfirmationToken $token, Kernel $kernel)
|
||||
{
|
||||
public function handleRequestWithTokenChain(
|
||||
HTTPRequest $request,
|
||||
ConfirmationTokenChain $confirmationTokenChain,
|
||||
Kernel $kernel
|
||||
) {
|
||||
Injector::inst()->registerService($request, HTTPRequest::class);
|
||||
|
||||
// Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
|
||||
$reload = function (HTTPRequest $request) use ($token, $kernel) {
|
||||
$reload = function (HTTPRequest $request) use ($confirmationTokenChain, $kernel) {
|
||||
if ($kernel->getEnvironment() === Kernel::DEV || !Security::database_is_ready() || Permission::check('ADMIN')) {
|
||||
return $token->reloadWithToken();
|
||||
return $confirmationTokenChain->reloadWithTokens();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -3,24 +3,21 @@
|
||||
namespace SilverStripe\Core\Startup;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Security\RandomGenerator;
|
||||
|
||||
/**
|
||||
* Class ParameterConfirmationToken
|
||||
* This is used to protect dangerous GET parameters that need to be detected early in the request
|
||||
* lifecycle by generating a one-time-use token & redirecting with that token included in the
|
||||
* redirected URL
|
||||
*
|
||||
* When you need to use a dangerous GET parameter that needs to be set before core/Core.php is
|
||||
* established, this class takes care of allowing some other code of confirming the parameter,
|
||||
* by generating a one-time-use token & redirecting with that token included in the redirected URL
|
||||
*
|
||||
* WARNING: This class is experimental and designed specifically for use pre-startup.
|
||||
* It will likely be heavily refactored before the release of 3.2
|
||||
* @internal This class is designed specifically for use pre-startup and may change without warning
|
||||
*/
|
||||
class ParameterConfirmationToken
|
||||
class ParameterConfirmationToken extends AbstractConfirmationToken
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the parameter
|
||||
*
|
||||
@ -28,11 +25,6 @@ class ParameterConfirmationToken
|
||||
*/
|
||||
protected $parameterName = null;
|
||||
|
||||
/**
|
||||
* @var HTTPRequest
|
||||
*/
|
||||
protected $request = null;
|
||||
|
||||
/**
|
||||
* The parameter given in the main request
|
||||
*
|
||||
@ -48,60 +40,6 @@ class ParameterConfirmationToken
|
||||
protected $parameterBackURL = null;
|
||||
|
||||
/**
|
||||
* The validated and checked token for this parameter
|
||||
*
|
||||
* @var string|null A string value, or null if either not provided or invalid
|
||||
*/
|
||||
protected $token = null;
|
||||
|
||||
protected function pathForToken($token)
|
||||
{
|
||||
return TEMP_PATH . DIRECTORY_SEPARATOR . 'token_' . preg_replace('/[^a-z0-9]+/', '', $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new random token and store it
|
||||
*
|
||||
* @return string Token name
|
||||
*/
|
||||
protected function genToken()
|
||||
{
|
||||
// Generate a new random token (as random as possible)
|
||||
$rg = new RandomGenerator();
|
||||
$token = $rg->randomToken('md5');
|
||||
|
||||
// Store a file in the session save path (safer than /tmp, as open_basedir might limit that)
|
||||
file_put_contents($this->pathForToken($token), $token);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a token
|
||||
*
|
||||
* @param string $token
|
||||
* @return boolean True if the token is valid
|
||||
*/
|
||||
protected function checkToken($token)
|
||||
{
|
||||
if (!$token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->pathForToken($token);
|
||||
$content = null;
|
||||
|
||||
if (file_exists($file)) {
|
||||
$content = file_get_contents($file);
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
return $content == $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ParameterConfirmationToken
|
||||
*
|
||||
* @param string $parameterName Name of the querystring parameter to check
|
||||
* @param HTTPRequest $request
|
||||
*/
|
||||
@ -176,54 +114,23 @@ class ParameterConfirmationToken
|
||||
return $this->parameterBackURL !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the necessary token provided for this parameter?
|
||||
* A value must be provided for the token
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function tokenProvided()
|
||||
{
|
||||
return !empty($this->token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this parameter requested without a valid token?
|
||||
*
|
||||
* @return bool True if the parameter is given without a valid token
|
||||
*/
|
||||
public function reloadRequired()
|
||||
{
|
||||
return $this->parameterProvided() && !$this->tokenProvided();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this token is provided either in the backurl, or directly,
|
||||
* but without a token
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function reloadRequiredIfError()
|
||||
{
|
||||
// Don't reload if token exists
|
||||
return $this->reloadRequired() || $this->existsInReferer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppress the current parameter by unsetting it from $_GET
|
||||
*/
|
||||
public function suppress()
|
||||
{
|
||||
unset($_GET[$this->parameterName]);
|
||||
$this->request->offsetUnset($this->parameterName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the querystring parameters to include
|
||||
*
|
||||
* @param bool $includeToken Include the token value as well?
|
||||
* @return array List of querystring parameters with name and token parameters
|
||||
*/
|
||||
public function params($includeToken = true)
|
||||
{
|
||||
$params = array(
|
||||
@ -235,80 +142,21 @@ class ParameterConfirmationToken
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get redirect url, excluding querystring
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function currentURL()
|
||||
public function getRedirectUrlBase()
|
||||
{
|
||||
return Controller::join_links(
|
||||
BASE_URL ?: '/',
|
||||
$this->request->getURL(false)
|
||||
);
|
||||
return ($this->existsInReferer() && !$this->parameterProvided()) ? Director::baseURL() : $this->currentURL();
|
||||
}
|
||||
|
||||
public function getRedirectUrlParams()
|
||||
{
|
||||
return ($this->existsInReferer() && !$this->parameterProvided())
|
||||
? $this->params()
|
||||
: array_merge($this->request->getVars(), $this->params());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get redirection URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function redirectURL()
|
||||
{
|
||||
// If url is encoded via BackURL, defer to home page (prevent redirect to form action)
|
||||
if ($this->existsInReferer() && !$this->parameterProvided()) {
|
||||
$url = BASE_URL ?: '/';
|
||||
$params = $this->params();
|
||||
} else {
|
||||
$url = $this->currentURL();
|
||||
$params = array_merge($this->request->getVars(), $this->params());
|
||||
}
|
||||
|
||||
// Merge get params with current url
|
||||
return Controller::join_links($url, '?' . http_build_query($params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a reload of the request with the token included
|
||||
*
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function reloadWithToken()
|
||||
{
|
||||
$location = $this->redirectURL();
|
||||
$locationJS = Convert::raw2js($location);
|
||||
$locationATT = Convert::raw2att($location);
|
||||
$body = <<<HTML
|
||||
<script>location.href='$locationJS';</script>
|
||||
<noscript><meta http-equiv="refresh" content="0; url=$locationATT"></noscript>
|
||||
You are being redirected. If you are not redirected soon, <a href="$locationATT">click here to continue the flush</a>
|
||||
HTML;
|
||||
|
||||
// Build response
|
||||
$result = new HTTPResponse($body);
|
||||
$result->redirect($location);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of token names, suppress all tokens that have not been validated, and
|
||||
* return the non-validated token with the highest priority
|
||||
*
|
||||
* @param array $keys List of token keys in ascending priority (low to high)
|
||||
* @param HTTPRequest $request
|
||||
* @return ParameterConfirmationToken The token container for the unvalidated $key given with the highest priority
|
||||
*/
|
||||
public static function prepare_tokens($keys, HTTPRequest $request)
|
||||
{
|
||||
$target = null;
|
||||
foreach ($keys as $key) {
|
||||
$token = new ParameterConfirmationToken($key, $request);
|
||||
// Validate this token
|
||||
if ($token->reloadRequired() || $token->reloadRequiredIfError()) {
|
||||
$token->suppress();
|
||||
$target = $token;
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
$query = http_build_query($this->getRedirectUrlParams());
|
||||
return Controller::join_links($this->getRedirectUrlBase(), '?' . $query);
|
||||
}
|
||||
}
|
||||
|
139
src/Core/Startup/URLConfirmationToken.php
Normal file
139
src/Core/Startup/URLConfirmationToken.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Startup;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
|
||||
/**
|
||||
* This is used to protect dangerous URLs that need to be detected early in the request lifecycle
|
||||
* by generating a one-time-use token & redirecting with that token included in the redirected URL
|
||||
*
|
||||
* @internal This class is designed specifically for use pre-startup and may change without warning
|
||||
*/
|
||||
class URLConfirmationToken extends AbstractConfirmationToken
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $urlToCheck;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $currentURL;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tokenParameterName;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $urlExistsInBackURL;
|
||||
|
||||
/**
|
||||
* @param string $urlToCheck URL to check
|
||||
* @param HTTPRequest $request
|
||||
*/
|
||||
public function __construct($urlToCheck, HTTPRequest $request)
|
||||
{
|
||||
$this->urlToCheck = $urlToCheck;
|
||||
$this->request = $request;
|
||||
$this->currentURL = $request->getURL(false);
|
||||
|
||||
$this->tokenParameterName = preg_replace('/[^a-z0-9]/i', '', $urlToCheck) . 'token';
|
||||
$this->urlExistsInBackURL = $this->getURLExistsInBackURL($request);
|
||||
|
||||
// If the token provided is valid, mark it as such
|
||||
$token = $request->getVar($this->tokenParameterName);
|
||||
if ($this->checkToken($token)) {
|
||||
$this->token = $token;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HTTPRequest $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function getURLExistsInBackURL(HTTPRequest $request)
|
||||
{
|
||||
$backURL = ltrim($request->getVar('BackURL'), '/');
|
||||
return (strpos($backURL, $this->urlToCheck) === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function urlMatches()
|
||||
{
|
||||
return ($this->currentURL === $this->urlToCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getURLToCheck()
|
||||
{
|
||||
return $this->urlToCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function urlExistsInBackURL()
|
||||
{
|
||||
return $this->urlExistsInBackURL;
|
||||
}
|
||||
|
||||
public function reloadRequired()
|
||||
{
|
||||
return $this->urlMatches() && !$this->tokenProvided();
|
||||
}
|
||||
|
||||
public function reloadRequiredIfError()
|
||||
{
|
||||
return $this->reloadRequired() || $this->urlExistsInBackURL();
|
||||
}
|
||||
|
||||
public function suppress()
|
||||
{
|
||||
$_SERVER['REQUEST_URI'] = '/';
|
||||
$this->request->setURL('/');
|
||||
}
|
||||
|
||||
public function params($includeToken = true)
|
||||
{
|
||||
$params = [];
|
||||
if ($includeToken) {
|
||||
$params[$this->tokenParameterName] = $this->genToken();
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
public function currentURL()
|
||||
{
|
||||
return Controller::join_links(Director::baseURL(), $this->currentURL);
|
||||
}
|
||||
|
||||
public function getRedirectUrlBase()
|
||||
{
|
||||
return ($this->urlExistsInBackURL && !$this->urlMatches()) ? Director::baseURL() : $this->currentURL();
|
||||
}
|
||||
|
||||
public function getRedirectUrlParams()
|
||||
{
|
||||
return ($this->urlExistsInBackURL && !$this->urlMatches())
|
||||
? $this->params()
|
||||
: array_merge($this->request->getVars(), $this->params());
|
||||
}
|
||||
|
||||
protected function redirectURL()
|
||||
{
|
||||
$query = http_build_query($this->getRedirectUrlParams());
|
||||
return Controller::join_links($this->getRedirectUrlBase(), '?' . $query);
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ class Backtrace
|
||||
array('PDO', '__construct'),
|
||||
array('mysqli', 'mysqli'),
|
||||
array('mysqli', 'select_db'),
|
||||
array('mysqli', 'real_connect'),
|
||||
array('SilverStripe\\ORM\\DB', 'connect'),
|
||||
array('SilverStripe\\Security\\Security', 'check_default_admin'),
|
||||
array('SilverStripe\\Security\\Security', 'encrypt_password'),
|
||||
|
185
tests/php/Core/Startup/ConfirmationTokenChainTest.php
Normal file
185
tests/php/Core/Startup/ConfirmationTokenChainTest.php
Normal file
@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Startup;
|
||||
|
||||
use SilverStripe\Core\Startup\ConfirmationTokenChain;
|
||||
use SilverStripe\Core\Startup\ParameterConfirmationToken;
|
||||
use SilverStripe\Core\Startup\URLConfirmationToken;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class ConfirmationTokenChainTest extends SapphireTest
|
||||
{
|
||||
protected function getTokenRequiringReload($requiresReload = true, $extraMethods = [])
|
||||
{
|
||||
$methods = array_merge(['reloadRequired'], $extraMethods);
|
||||
$mock = $this->createPartialMock(ParameterConfirmationToken::class, $methods);
|
||||
$mock->expects($this->any())
|
||||
->method('reloadRequired')
|
||||
->will($this->returnValue($requiresReload));
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function getTokenRequiringReloadIfError($requiresReload = true, $extraMethods = [])
|
||||
{
|
||||
$methods = array_merge(['reloadRequired', 'reloadRequiredIfError'], $extraMethods);
|
||||
$mock = $this->createPartialMock(ParameterConfirmationToken::class, $methods);
|
||||
$mock->expects($this->any())
|
||||
->method('reloadRequired')
|
||||
->will($this->returnValue(false));
|
||||
$mock->expects($this->any())
|
||||
->method('reloadRequiredIfError')
|
||||
->will($this->returnValue($requiresReload));
|
||||
return $mock;
|
||||
}
|
||||
|
||||
public function testFilteredTokens()
|
||||
{
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($tokenRequiringReload = $this->getTokenRequiringReload());
|
||||
$chain->pushToken($tokenNotRequiringReload = $this->getTokenRequiringReload(false));
|
||||
$chain->pushToken($tokenRequiringReloadIfError = $this->getTokenRequiringReloadIfError());
|
||||
$chain->pushToken($tokenNotRequiringReloadIfError = $this->getTokenRequiringReloadIfError(false));
|
||||
|
||||
$reflectionMethod = new \ReflectionMethod(ConfirmationTokenChain::class, 'filteredTokens');
|
||||
$reflectionMethod->setAccessible(true);
|
||||
$tokens = iterator_to_array($reflectionMethod->invoke($chain));
|
||||
|
||||
$this->assertContains($tokenRequiringReload, $tokens, 'Token requiring a reload was not returned');
|
||||
$this->assertNotContains($tokenNotRequiringReload, $tokens, 'Token not requiring a reload was returned');
|
||||
$this->assertContains($tokenRequiringReloadIfError, $tokens, 'Token requiring a reload on error was not returned');
|
||||
$this->assertNotContains($tokenNotRequiringReloadIfError, $tokens, 'Token not requiring a reload on error was returned');
|
||||
}
|
||||
|
||||
public function testSuppressionRequired()
|
||||
{
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($this->getTokenRequiringReload(false));
|
||||
$this->assertFalse($chain->suppressionRequired(), 'Suppression incorrectly marked as required');
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($this->getTokenRequiringReloadIfError(false));
|
||||
$this->assertFalse($chain->suppressionRequired(), 'Suppression incorrectly marked as required');
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($this->getTokenRequiringReload());
|
||||
$this->assertTrue($chain->suppressionRequired(), 'Suppression not marked as required');
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($this->getTokenRequiringReloadIfError());
|
||||
$this->assertFalse($chain->suppressionRequired(), 'Suppression incorrectly marked as required');
|
||||
}
|
||||
|
||||
public function testSuppressTokens()
|
||||
{
|
||||
$mockToken = $this->getTokenRequiringReload(true, ['suppress']);
|
||||
$mockToken->expects($this->once())
|
||||
->method('suppress');
|
||||
$secondMockToken = $this->getTokenRequiringReloadIfError(true, ['suppress']);
|
||||
$secondMockToken->expects($this->once())
|
||||
->method('suppress');
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockToken);
|
||||
$chain->pushToken($secondMockToken);
|
||||
$chain->suppressTokens();
|
||||
}
|
||||
|
||||
public function testReloadRequired()
|
||||
{
|
||||
$mockToken = $this->getTokenRequiringReload(true);
|
||||
$secondMockToken = $this->getTokenRequiringReload(false);
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockToken);
|
||||
$chain->pushToken($secondMockToken);
|
||||
$this->assertTrue($chain->reloadRequired());
|
||||
}
|
||||
|
||||
public function testReloadRequiredIfError()
|
||||
{
|
||||
$mockToken = $this->getTokenRequiringReloadIfError(true);
|
||||
$secondMockToken = $this->getTokenRequiringReloadIfError(false);
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockToken);
|
||||
$chain->pushToken($secondMockToken);
|
||||
$this->assertTrue($chain->reloadRequiredIfError());
|
||||
}
|
||||
|
||||
public function testParams()
|
||||
{
|
||||
$mockToken = $this->getTokenRequiringReload(true, ['params']);
|
||||
$mockToken->expects($this->once())
|
||||
->method('params')
|
||||
->with($this->isTrue())
|
||||
->will($this->returnValue(['mockTokenParam' => '1']));
|
||||
$secondMockToken = $this->getTokenRequiringReload(true, ['params']);
|
||||
$secondMockToken->expects($this->once())
|
||||
->method('params')
|
||||
->with($this->isTrue())
|
||||
->will($this->returnValue(['secondMockTokenParam' => '2']));
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockToken);
|
||||
$chain->pushToken($secondMockToken);
|
||||
$this->assertEquals(['mockTokenParam' => '1', 'secondMockTokenParam' => '2'], $chain->params(true));
|
||||
|
||||
$mockToken = $this->getTokenRequiringReload(true, ['params']);
|
||||
$mockToken->expects($this->once())
|
||||
->method('params')
|
||||
->with($this->isFalse())
|
||||
->will($this->returnValue(['mockTokenParam' => '1']));
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockToken);
|
||||
$this->assertEquals(['mockTokenParam' => '1'], $chain->params(false));
|
||||
}
|
||||
|
||||
public function testGetRedirectUrlBase()
|
||||
{
|
||||
$mockUrlToken = $this->createPartialMock(URLConfirmationToken::class, ['reloadRequired', 'getRedirectUrlBase']);
|
||||
$mockUrlToken->expects($this->any())
|
||||
->method('reloadRequired')
|
||||
->will($this->returnValue(true));
|
||||
$mockUrlToken->expects($this->any())
|
||||
->method('getRedirectUrlBase')
|
||||
->will($this->returnValue('url-base'));
|
||||
|
||||
$mockParameterToken = $this->createPartialMock(ParameterConfirmationToken::class, ['reloadRequired', 'getRedirectUrlBase']);
|
||||
$mockParameterToken->expects($this->any())
|
||||
->method('reloadRequired')
|
||||
->will($this->returnValue(true));
|
||||
$mockParameterToken->expects($this->any())
|
||||
->method('getRedirectUrlBase')
|
||||
->will($this->returnValue('parameter-base'));
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockParameterToken);
|
||||
$chain->pushToken($mockUrlToken);
|
||||
$this->assertEquals('url-base', $chain->getRedirectUrlBase(), 'URLConfirmationToken url base should take priority');
|
||||
|
||||
// Push them in reverse order to check priority still correct
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockUrlToken);
|
||||
$chain->pushToken($mockParameterToken);
|
||||
$this->assertEquals('url-base', $chain->getRedirectUrlBase(), 'URLConfirmationToken url base should take priority');
|
||||
}
|
||||
|
||||
public function testGetRedirectUrlParams()
|
||||
{
|
||||
$mockToken = $this->getTokenRequiringReload(true, ['getRedirectUrlParams']);
|
||||
$mockToken->expects($this->once())
|
||||
->method('getRedirectUrlParams')
|
||||
->will($this->returnValue(['mockTokenParam' => '1']));
|
||||
|
||||
$secondMockToken = $this->getTokenRequiringReload(true, ['getRedirectUrlParams']);
|
||||
$secondMockToken->expects($this->once())
|
||||
->method('getRedirectUrlParams')
|
||||
->will($this->returnValue(['secondMockTokenParam' => '2']));
|
||||
|
||||
$chain = new ConfirmationTokenChain();
|
||||
$chain->pushToken($mockToken);
|
||||
$chain->pushToken($secondMockToken);
|
||||
$this->assertEquals(['mockTokenParam' => '1', 'secondMockTokenParam' => '2'], $chain->getRedirectUrlParams());
|
||||
}
|
||||
}
|
@ -73,4 +73,104 @@ class ErrorControlChainMiddlewareTest extends SapphireTest
|
||||
$this->assertNotContains('?flush=1&flushtoken=', $location);
|
||||
$this->assertContains('Security/login', $location);
|
||||
}
|
||||
|
||||
public function testLiveBuildAdmin()
|
||||
{
|
||||
// Mock admin
|
||||
$adminID = $this->logInWithPermission('ADMIN');
|
||||
$this->logOut();
|
||||
|
||||
// Mock app
|
||||
$app = new HTTPApplication(new BlankKernel(BASE_PATH));
|
||||
$app->getKernel()->setEnvironment(Kernel::LIVE);
|
||||
|
||||
// Test being logged in as admin
|
||||
$chain = new ErrorControlChainMiddleware($app);
|
||||
$request = new HTTPRequest('GET', '/dev/build/');
|
||||
$request->setSession(new Session(['loggedInAs' => $adminID]));
|
||||
$result = $chain->process($request, function () {
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||
$location = $result->getHeader('Location');
|
||||
$this->assertContains('/dev/build', $location);
|
||||
$this->assertContains('?devbuildtoken=', $location);
|
||||
$this->assertNotContains('Security/login', $location);
|
||||
}
|
||||
|
||||
public function testLiveBuildUnauthenticated()
|
||||
{
|
||||
// Mock app
|
||||
$app = new HTTPApplication(new BlankKernel(BASE_PATH));
|
||||
$app->getKernel()->setEnvironment(Kernel::LIVE);
|
||||
|
||||
// Test being logged in as no one
|
||||
Security::setCurrentUser(null);
|
||||
$chain = new ErrorControlChainMiddleware($app);
|
||||
$request = new HTTPRequest('GET', '/dev/build');
|
||||
$request->setSession(new Session(['loggedInAs' => 0]));
|
||||
$result = $chain->process($request, function () {
|
||||
return null;
|
||||
});
|
||||
|
||||
// Should be directed to login, not to flush
|
||||
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||
$location = $result->getHeader('Location');
|
||||
$this->assertNotContains('/dev/build', $location);
|
||||
$this->assertNotContains('?devbuildtoken=', $location);
|
||||
$this->assertContains('Security/login', $location);
|
||||
}
|
||||
|
||||
public function testLiveBuildAndFlushAdmin()
|
||||
{
|
||||
// Mock admin
|
||||
$adminID = $this->logInWithPermission('ADMIN');
|
||||
$this->logOut();
|
||||
|
||||
// Mock app
|
||||
$app = new HTTPApplication(new BlankKernel(BASE_PATH));
|
||||
$app->getKernel()->setEnvironment(Kernel::LIVE);
|
||||
|
||||
// Test being logged in as admin
|
||||
$chain = new ErrorControlChainMiddleware($app);
|
||||
$request = new HTTPRequest('GET', '/dev/build/', ['flush' => '1']);
|
||||
$request->setSession(new Session(['loggedInAs' => $adminID]));
|
||||
$result = $chain->process($request, function () {
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||
$location = $result->getHeader('Location');
|
||||
$this->assertContains('/dev/build', $location);
|
||||
$this->assertContains('flush=1', $location);
|
||||
$this->assertContains('devbuildtoken=', $location);
|
||||
$this->assertContains('flushtoken=', $location);
|
||||
$this->assertNotContains('Security/login', $location);
|
||||
}
|
||||
|
||||
public function testLiveBuildAndFlushUnauthenticated()
|
||||
{
|
||||
// Mock app
|
||||
$app = new HTTPApplication(new BlankKernel(BASE_PATH));
|
||||
$app->getKernel()->setEnvironment(Kernel::LIVE);
|
||||
|
||||
// Test being logged in as no one
|
||||
Security::setCurrentUser(null);
|
||||
$chain = new ErrorControlChainMiddleware($app);
|
||||
$request = new HTTPRequest('GET', '/dev/build', ['flush' => '1']);
|
||||
$request->setSession(new Session(['loggedInAs' => 0]));
|
||||
$result = $chain->process($request, function () {
|
||||
return null;
|
||||
});
|
||||
|
||||
// Should be directed to login, not to flush
|
||||
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||
$location = $result->getHeader('Location');
|
||||
$this->assertNotContains('/dev/build', $location);
|
||||
$this->assertNotContains('flush=1', $location);
|
||||
$this->assertNotContains('devbuildtoken=', $location);
|
||||
$this->assertNotContains('flushtoken=', $location);
|
||||
$this->assertContains('Security/login', $location);
|
||||
}
|
||||
}
|
||||
|
@ -149,14 +149,14 @@ class ParameterConfirmationTokenTest extends SapphireTest
|
||||
}
|
||||
|
||||
/**
|
||||
* currentAbsoluteURL needs to handle base or url being missing, or any combination of slashes.
|
||||
* currentURL needs to handle base or url being missing, or any combination of slashes.
|
||||
*
|
||||
* There should always be exactly one slash between each part in the result, and any trailing slash
|
||||
* should be preserved.
|
||||
*
|
||||
* @dataProvider dataProviderURLs
|
||||
*/
|
||||
public function testCurrentAbsoluteURLHandlesSlashes($url)
|
||||
public function testCurrentURLHandlesSlashes($url)
|
||||
{
|
||||
$this->request->setUrl($url);
|
||||
|
||||
|
148
tests/php/Core/Startup/URLConfirmationTokenTest.php
Normal file
148
tests/php/Core/Startup/URLConfirmationTokenTest.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Startup;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Core\Startup\URLConfirmationToken;
|
||||
use SilverStripe\Core\Tests\Startup\URLConfirmationTokenTest\StubToken;
|
||||
use SilverStripe\Core\Tests\Startup\URLConfirmationTokenTest\StubValidToken;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class URLConfirmationTokenTest extends SapphireTest
|
||||
{
|
||||
public function testValidToken()
|
||||
{
|
||||
$request = new HTTPRequest('GET', 'token/test/url', ['tokentesturltoken' => 'value']);
|
||||
$validToken = new StubValidToken('token/test/url', $request);
|
||||
$this->assertTrue($validToken->urlMatches());
|
||||
$this->assertFalse($validToken->urlExistsInBackURL());
|
||||
$this->assertTrue($validToken->tokenProvided()); // Actually forced to true for this test
|
||||
$this->assertFalse($validToken->reloadRequired());
|
||||
$this->assertFalse($validToken->reloadRequiredIfError());
|
||||
$this->assertStringStartsWith(Controller::join_links(BASE_URL, '/', 'token/test/url'), $validToken->redirectURL());
|
||||
}
|
||||
|
||||
public function testTokenWithLeadingSlashInUrl()
|
||||
{
|
||||
$request = new HTTPRequest('GET', '/leading/slash/url', []);
|
||||
$leadingSlash = new StubToken('leading/slash/url', $request);
|
||||
$this->assertTrue($leadingSlash->urlMatches());
|
||||
$this->assertFalse($leadingSlash->urlExistsInBackURL());
|
||||
$this->assertFalse($leadingSlash->tokenProvided());
|
||||
$this->assertTrue($leadingSlash->reloadRequired());
|
||||
$this->assertTrue($leadingSlash->reloadRequiredIfError());
|
||||
$this->assertContains('leading/slash/url', $leadingSlash->redirectURL());
|
||||
$this->assertContains('leadingslashurltoken', $leadingSlash->redirectURL());
|
||||
}
|
||||
|
||||
public function testTokenWithTrailingSlashInUrl()
|
||||
{
|
||||
$request = new HTTPRequest('GET', 'trailing/slash/url/', []);
|
||||
$trailingSlash = new StubToken('trailing/slash/url', $request);
|
||||
$this->assertTrue($trailingSlash->urlMatches());
|
||||
$this->assertFalse($trailingSlash->urlExistsInBackURL());
|
||||
$this->assertFalse($trailingSlash->tokenProvided());
|
||||
$this->assertTrue($trailingSlash->reloadRequired());
|
||||
$this->assertTrue($trailingSlash->reloadRequiredIfError());
|
||||
$this->assertContains('trailing/slash/url', $trailingSlash->redirectURL());
|
||||
$this->assertContains('trailingslashurltoken', $trailingSlash->redirectURL());
|
||||
}
|
||||
|
||||
public function testTokenWithUrlMatchedInBackUrl()
|
||||
{
|
||||
$request = new HTTPRequest('GET', '/', ['BackURL' => 'back/url']);
|
||||
$backUrl = new StubToken('back/url', $request);
|
||||
$this->assertFalse($backUrl->urlMatches());
|
||||
$this->assertTrue($backUrl->urlExistsInBackURL());
|
||||
$this->assertFalse($backUrl->tokenProvided());
|
||||
$this->assertFalse($backUrl->reloadRequired());
|
||||
$this->assertTrue($backUrl->reloadRequiredIfError());
|
||||
$home = (BASE_URL ?: '/') . '?';
|
||||
$this->assertStringStartsWith($home, $backUrl->redirectURL());
|
||||
$this->assertContains('backurltoken', $backUrl->redirectURL());
|
||||
}
|
||||
|
||||
public function testUrlSuppressionWhenTokenMissing()
|
||||
{
|
||||
// Check suppression
|
||||
$request = new HTTPRequest('GET', 'test/url', []);
|
||||
$token = new StubToken('test/url', $request);
|
||||
$this->assertEquals('test/url', $request->getURL(false));
|
||||
$token->suppress();
|
||||
$this->assertEquals('', $request->getURL(false));
|
||||
}
|
||||
|
||||
public function testPrepareTokens()
|
||||
{
|
||||
$request = new HTTPRequest('GET', 'test/url', []);
|
||||
$token = URLConfirmationToken::prepare_tokens(
|
||||
[
|
||||
'test/url',
|
||||
'test',
|
||||
'url'
|
||||
],
|
||||
$request
|
||||
);
|
||||
// Test no invalid tokens
|
||||
$this->assertEquals('test/url', $token->getURLToCheck());
|
||||
$this->assertNotEquals('test/url', $request->getURL(false), 'prepare_tokens() did not suppress URL');
|
||||
}
|
||||
|
||||
public function testPrepareTokensDoesntSuppressWhenNotMatched()
|
||||
{
|
||||
$request = new HTTPRequest('GET', 'test/url', []);
|
||||
$token = URLConfirmationToken::prepare_tokens(
|
||||
['another/url'],
|
||||
$request
|
||||
);
|
||||
$this->assertEmpty($token);
|
||||
$this->assertEquals('test/url', $request->getURL(false), 'prepare_tokens() incorrectly suppressed URL');
|
||||
}
|
||||
|
||||
public function testPrepareTokensWithUrlMatchedInBackUrl()
|
||||
{
|
||||
// Test backurl token
|
||||
$request = new HTTPRequest('GET', '/', ['BackURL' => 'back/url']);
|
||||
$token = URLConfirmationToken::prepare_tokens(
|
||||
[ 'back/url' ],
|
||||
$request
|
||||
);
|
||||
$this->assertNotEmpty($token);
|
||||
$this->assertEquals('back/url', $token->getURLToCheck());
|
||||
$this->assertNotEquals('back/url', $request->getURL(false), 'prepare_tokens() did not suppress URL');
|
||||
}
|
||||
|
||||
public function dataProviderURLs()
|
||||
{
|
||||
return [
|
||||
[''],
|
||||
['/'],
|
||||
['bar'],
|
||||
['bar/'],
|
||||
['/bar'],
|
||||
['/bar/'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* currentURL needs to handle base or url being missing, or any combination of slashes.
|
||||
*
|
||||
* There should always be exactly one slash between each part in the result, and any trailing slash
|
||||
* should be preserved.
|
||||
*
|
||||
* @dataProvider dataProviderURLs
|
||||
*/
|
||||
public function testCurrentURLHandlesSlashes($url)
|
||||
{
|
||||
$request = new HTTPRequest('GET', $url, []);
|
||||
|
||||
$token = new StubToken(
|
||||
'another/url',
|
||||
$request
|
||||
);
|
||||
$expected = rtrim(Controller::join_links(BASE_URL, '/', $url), '/') ?: '/';
|
||||
$this->assertEquals($expected, $token->currentURL(), "Invalid redirect for request url $url");
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Startup\URLConfirmationTokenTest;
|
||||
|
||||
use SilverStripe\Core\Startup\URLConfirmationToken;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
|
||||
/**
|
||||
* Dummy url token
|
||||
*/
|
||||
class StubToken extends URLConfirmationToken implements TestOnly
|
||||
{
|
||||
public function urlMatches()
|
||||
{
|
||||
return parent::urlMatches();
|
||||
}
|
||||
|
||||
public function currentURL()
|
||||
{
|
||||
return parent::currentURL();
|
||||
}
|
||||
|
||||
public function redirectURL()
|
||||
{
|
||||
return parent::redirectURL();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Startup\URLConfirmationTokenTest;
|
||||
|
||||
/**
|
||||
* A token that always validates a given token
|
||||
*/
|
||||
class StubValidToken extends StubToken
|
||||
{
|
||||
|
||||
protected function checkToken($token)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user