Merge remote-tracking branch 'origin/3.1.0' into 3.1

This commit is contained in:
Ingo Schommer 2013-09-12 17:24:42 +02:00
commit c2b312d76f
25 changed files with 575 additions and 66 deletions

View File

@ -87,7 +87,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
return $item->getBreadcrumbs(' > ');
}
));
$fields = new FieldList(
$root = new TabSet(
'Root',
@ -101,34 +101,42 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
. ' database'
)
)
),
new HeaderField(_t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3),
new LiteralField(
'MemberImportFormIframe',
sprintf(
'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" frameBorder="0">'
. '</iframe>',
$this->Link('memberimport')
)
)
),
$groupsTab = new Tab('Groups', singleton('Group')->i18n_plural_name(),
$groupList,
new HeaderField(_t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3),
new LiteralField(
'GroupImportFormIframe',
sprintf(
'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" frameBorder="0">'
. '</iframe>',
$this->Link('groupimport')
)
)
$groupList
)
),
// necessary for tree node selection in LeftAndMain.EditForm.js
new HiddenField('ID', false, 0)
);
// Add import capabilities. Limit to admin since the import logic can affect assigned permissions
if(Permission::check('ADMIN')) {
$fields->addFieldsToTab('Root.Users', array(
new HeaderField(_t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3),
new LiteralField(
'MemberImportFormIframe',
sprintf(
'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" frameBorder="0">'
. '</iframe>',
$this->Link('memberimport')
)
)
));
$fields->addFieldsToTab('Root.Groups', array(
new HeaderField(_t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3),
new LiteralField(
'GroupImportFormIframe',
sprintf(
'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" frameBorder="0">'
. '</iframe>',
$this->Link('groupimport')
)
)
));
}
// Tab nav in CMS is rendered through separate template
$root->setTemplate('CMSTabSet');
@ -196,6 +204,8 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
* @return Form
*/
public function MemberImportForm() {
if(!Permission::check('ADMIN')) return false;
$group = $this->currentPage();
$form = new MemberImportForm(
$this,
@ -226,6 +236,8 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
* @return Form
*/
public function GroupImportForm() {
if(!Permission::check('ADMIN')) return false;
$form = new GroupImportForm(
$this,
'GroupImportForm'
@ -307,7 +319,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
/**
* The permissions represented in the $codes will not appearing in the form
* containing {@link PermissionCheckboxSetField} so as not to be checked / unchecked.
*
*
* @deprecated 3.1 Use "Permission.hidden_permissions" config setting instead
* @param $codes String|Array
*/

View File

@ -224,15 +224,31 @@
return this;
},
/**
* Return a style element we can use in IE8 to fix fonts (see readystatechange binding in onadd below)
*/
getOrAppendFontFixStyleElement: function() {
var style = $('#FontFixStyleElement');
if (!style.length) {
style = $(
'<style type="text/css" id="FontFixStyleElement" disabled="disabled">'+
':before,:after{content:none !important}'+
'</style>'
).appendTo('head');
}
return style;
},
/**
* Initialise the preview element.
*/
onadd: function() {
var self = this, layoutContainer = this.parent();
var self = this, layoutContainer = this.parent(), iframe = this.find('iframe');
// Create layout and controls
this.find('iframe').addClass('center');
this.find('iframe').bind('load', function() {
iframe.addClass('center');
iframe.bind('load', function() {
self._adjustIframeForPreview();
// Load edit view for new page, but only if the preview is activated at the moment.
@ -241,7 +257,17 @@
$(this).removeClass('loading');
});
// If there's any webfonts in the preview, IE8 will start glitching. This fixes that.
if ($.browser.msie && 8 === parseInt($.browser.version, 10)) {
iframe.bind('readystatechange', function(e) {
if(iframe[0].readyState == 'interactive') {
self.getOrAppendFontFixStyleElement().removeAttr('disabled');
setTimeout(function(){ self.getOrAppendFontFixStyleElement().attr('disabled', 'disabled'); }, 0);
}
});
}
// Preview might not be available in all admin interfaces - block/disable when necessary
this.append('<div class="cms-preview-overlay ui-widget-overlay-light"></div>');
this.find('.cms-preview-overlay').hide();

View File

@ -343,7 +343,23 @@ class HTTP {
$responseHeaders['Vary'] = 'Cookie, X-Forwarded-Protocol, User-Agent, Accept';
}
else {
$responseHeaders["Cache-Control"] = "no-cache, max-age=0, must-revalidate, no-transform";
// Grab header for checking. Unfortunately HTTPRequest uses a mistyped variant.
$contentDisposition = $body->getHeader('Content-disposition');
if (!$contentDisposition) $contentDisposition = $body->getHeader('Content-Disposition');
if(
Director::is_https() &&
strstr($_SERVER["HTTP_USER_AGENT"], 'MSIE')==true &&
strstr($contentDisposition, 'attachment;')==true
) {
// IE6-IE8 have problems saving files when https and no-cache are used
// (http://support.microsoft.com/kb/323308)
// Note: this is also fixable by ticking "Do not save encrypted pages to disk" in advanced options.
$responseHeaders["Cache-Control"] = "max-age=3, must-revalidate, no-transform";
$responseHeaders["Pragma"] = "";
} else {
$responseHeaders["Cache-Control"] = "no-cache, max-age=0, must-revalidate, no-transform";
}
}
if(self::$modification_date && self::$cache_age > 0) {

View File

@ -391,13 +391,9 @@ class SS_HTTPRequest implements ArrayAccess {
}
$response = new SS_HTTPResponse($fileData);
$response->addHeader("Content-Type", "$mimeType; name=\"" . addslashes($fileName) . "\"");
// Note a IE-only fix that inspects this header in HTTP::add_cache_headers().
$response->addHeader("Content-disposition", "attachment; filename=" . addslashes($fileName));
$response->addHeader("Content-Length", strlen($fileData));
$response->addHeader("Pragma", ""); // Necessary because IE has issues sending files over SSL
if(strstr($_SERVER["HTTP_USER_AGENT"],"MSIE") == true) {
$response->addHeader('Cache-Control', 'max-age=3, must-revalidate'); // Workaround for IE6 and 7
}
return $response;
}

View File

@ -7,7 +7,7 @@
## Details
### Security: Require ADMIN for ?flush=1
### Security: Require ADMIN for ?flush=1 (SS-2013-001)
Flushing the various manifests (class, template, config) is performed through a GET
parameter (`flush=1`). Since this action requires more server resources than normal requests,
@ -23,6 +23,18 @@ This applies to both `flush=1` and `flush=all` (technically we only check for th
but only through web requests made through main.php - CLI requests, or any other request that goes through
a custom start up script will still process all flush requests as normal.
### Security: Privilege escalation through Group hierarchy setting (SS-2013-003)
See [announcement](http://www.silverstripe.org/ss-2013-003-privilege-escalation-through-group-hierarchy-setting/)
### Security: Privilege escalation through Group and Member CSV upload (SS-2013-004)
See [announcement](http://www.silverstripe.org/ss-2013-004-privilege-escalation-through-group-and-member-csv-upload/)
### Security: Privilege escalation through APPLY_ROLES assignment (SS-2013-005)
See [announcement](http://www.silverstripe.org/ss-2013-005-privilege-escalation-through-apply-roles-assignment/)
## Upgrading
* If you have created your own composite database fields, then you should amend the setValue() to allow the passing of

View File

@ -0,0 +1,38 @@
# 3.1.0-rc1
## Overview
### Security: Privilege escalation through Group hierarchy setting (SS-2013-003)
See [announcement](http://www.silverstripe.org/ss-2013-003-privilege-escalation-through-group-hierarchy-setting/)
### Security: Privilege escalation through Group and Member CSV upload (SS-2013-004)
See [announcement](http://www.silverstripe.org/ss-2013-004-privilege-escalation-through-group-and-member-csv-upload/)
### Security: Privilege escalation through APPLY_ROLES assignment (SS-2013-005)
See [announcement](http://www.silverstripe.org/ss-2013-005-privilege-escalation-through-apply-roles-assignment/)
## Changelog
### Bugfixes
* 2013-08-30 [091c096](https://github.com/silverstripe/sapphire/commit/091c096) Disallow permissions assign for APPLY_ROLES (SS-2013-005) (Ingo Schommer)
* 2013-08-30 [cfa88ad](https://github.com/silverstripe/sapphire/commit/cfa88ad) Privilege escalation through APPLY_ROLES assignment (SS-2013-005) (Ingo Schommer)
* 2013-08-30 [46556b6](https://github.com/silverstripe/sapphire/commit/46556b6) Privilege escalation through Group and Member CSV upload (SS-2013-004) (Ingo Schommer)
* 2013-08-30 [68ca47b](https://github.com/silverstripe/sapphire/commit/68ca47b) Privilege escalation through Group hierarchy setting (SS-2013-003) (Ingo Schommer)
* 2013-08-23 [1461ae9](https://github.com/silverstripe/sapphire/commit/1461ae9) Fix regression in IE no-cache https file downloads. (Mateusz Uzdowski)
* 2013-08-22 [45c1d2b](https://github.com/silverstripe/sapphire/commit/45c1d2b) webfonts in preview iframe breaking admin fonts (Hamish Friedlander)
* 2013-08-21 [a2026ad](https://github.com/silverstripe/sapphire/commit/a2026ad) flushing on non-dev when Session::cookie_secure is true (Hamish Friedlander)
* 2013-08-20 [1c31c09](https://github.com/silverstripe/sapphire/commit/1c31c09) Correct Zend_Locale fallbacks in i18n/DateField/DateTimeField (Ingo Schommer)
* 2013-08-20 [68d8ec3](https://github.com/silverstripe/sapphire/commit/68d8ec3) Memory leaks in jstree drag & drop (Hamish Friedlander)
* 2013-08-20 [fda4b91](https://github.com/silverstripe/sapphire/commit/fda4b91) Make sure CurrentXHR is set back to null on completion (Hamish Friedlander)
* 2013-08-20 [e282f0b](https://github.com/silverstripe/sapphire/commit/e282f0b) Two memory leaks with HtmlEditorField (Hamish Friedlander)
* 2013-08-19 [4a7aef0](https://github.com/silverstripe/sapphire/commit/4a7aef0) Double slashes in ParameterConfirmationToken (Hamish Friedlander)
* 2013-08-15 [0ca4969](https://github.com/silverstripe/sapphire/commit/0ca4969) Dont update preview iframe if hidden (Hamish Friedlander)
* 2013-08-12 [c59305d](https://github.com/silverstripe/sapphire/commit/c59305d) Multiple redraw calls on navigation (Hamish Friedlander)
* 2013-08-09 [b1664f8](https://github.com/silverstripe/silverstripe-cms/commit/b1664f8) Check for stage and drafts in SiteTree::canView() (Simon Welsh)
* 2013-08-08 [2fae928](https://github.com/silverstripe/silverstripe-cms/commit/2fae928) ArchiveDate enforcement (Hamish Friedlander)
* 2013-08-07 [fb67181](https://github.com/silverstripe/sapphire/commit/fb67181) Context menu too long - CSS only (Fixes CMS #811) (Naomi Guyer)
* 2013-08-07 [71608f0](https://github.com/silverstripe/silverstripe-cms/commit/71608f0) Add SiteTree link tracking as an extension, and apply to SiteTree itself (Hamish Friedlander)

View File

@ -217,6 +217,8 @@ class DateField extends TextField {
* @param String|Array $val
*/
public function setValue($val) {
$locale = new Zend_Locale($this->locale);
if(empty($val)) {
$this->value = null;
$this->valueObj = null;
@ -226,7 +228,7 @@ class DateField extends TextField {
if(is_array($val) && $this->validateArrayValue($val)) {
// set() gets confused with custom date formats when using array notation
if(!(empty($val['day']) || empty($val['month']) || empty($val['year']))) {
$this->valueObj = new Zend_Date($val, null, $this->locale);
$this->valueObj = new Zend_Date($val, null, $locale);
$this->value = $this->valueObj->toArray();
} else {
$this->value = $val;
@ -234,8 +236,8 @@ class DateField extends TextField {
}
}
// load ISO date from database (usually through Form->loadDataForm())
else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $this->locale)) {
$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'), $this->locale);
else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $locale)) {
$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'), $locale);
$this->value = $this->valueObj->toArray();
}
else {
@ -247,15 +249,15 @@ class DateField extends TextField {
// Caution: Its important to have this check *before* the ISO date fallback,
// as some dates are falsely detected as ISO by isDate(), e.g. '03/04/03'
// (en_NZ for 3rd of April, definetly not yyyy-MM-dd)
if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $this->locale)) {
$this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $this->locale);
$this->value = $this->valueObj->get($this->getConfig('dateformat'), $this->locale);
if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('dateformat'), $locale)) {
$this->valueObj = new Zend_Date($val, $this->getConfig('dateformat'), $locale);
$this->value = $this->valueObj->get($this->getConfig('dateformat'), $locale);
}
// load ISO date from database (usually through Form->loadDataForm())
else if(!empty($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'))) {
$this->valueObj = new Zend_Date($val, $this->getConfig('datavalueformat'));
$this->value = $this->valueObj->get($this->getConfig('dateformat'), $this->locale);
$this->value = $this->valueObj->get($this->getConfig('dateformat'), $locale);
}
else {
$this->value = $val;

View File

@ -115,6 +115,8 @@ class DatetimeField extends FormField {
* the 'date' value may contain array notation was well (see {@link DateField->setValue()}).
*/
public function setValue($val) {
$locale = new Zend_Locale($this->locale);
// If timezones are enabled, assume user data needs to be reverted to server timezone
if($this->getConfig('usertimezone')) {
// Accept user input on timezone, but only when timezone support is enabled
@ -130,7 +132,7 @@ class DatetimeField extends FormField {
$this->timeField->setValue(null);
} else {
// Case 1: String setting from database, in ISO date format
if(is_string($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $this->locale)) {
if(is_string($val) && Zend_Date::isDate($val, $this->getConfig('datavalueformat'), $locale)) {
$this->value = $val;
}
// Case 2: Array form submission with user date format
@ -145,13 +147,13 @@ class DatetimeField extends FormField {
$this->dateField->setValue($val['date']);
$this->timeField->setValue($val['time']);
if($this->dateField->dataValue() && $this->timeField->dataValue()) {
$userValueObj = new Zend_Date(null, null, $this->locale);
$userValueObj = new Zend_Date(null, null, $locale);
$userValueObj->setDate($this->dateField->dataValue(),
$this->dateField->getConfig('datavalueformat'));
$userValueObj->setTime($this->timeField->dataValue(),
$this->timeField->getConfig('datavalueformat'));
if($userTz) $userValueObj->setTimezone($dataTz);
$this->value = $userValueObj->get($this->getConfig('datavalueformat'), $this->locale);
$this->value = $userValueObj->get($this->getConfig('datavalueformat'), $locale);
unset($userValueObj);
} else {
// Validation happens later, so set the raw string in case Zend_Date doesn't accept it
@ -168,8 +170,8 @@ class DatetimeField extends FormField {
}
// view settings (dates might differ from $this->value based on user timezone settings)
if (Zend_Date::isDate($this->value, $this->getConfig('datavalueformat'), $this->locale)) {
$valueObj = new Zend_Date($this->value, $this->getConfig('datavalueformat'), $this->locale);
if (Zend_Date::isDate($this->value, $this->getConfig('datavalueformat'), $locale)) {
$valueObj = new Zend_Date($this->value, $this->getConfig('datavalueformat'), $locale);
if($userTz) $valueObj->setTimezone($userTz);
// Set view values in sub-fields
@ -177,9 +179,9 @@ class DatetimeField extends FormField {
$this->dateField->setValue($valueObj->toArray());
} else {
$this->dateField->setValue(
$valueObj->get($this->dateField->getConfig('dateformat'), $this->locale));
$valueObj->get($this->dateField->getConfig('dateformat'), $locale));
}
$this->timeField->setValue($valueObj->get($this->timeField->getConfig('timeformat'), $this->locale));
$this->timeField->setValue($valueObj->get($this->timeField->getConfig('timeformat'), $locale));
}
}

View File

@ -2233,13 +2233,11 @@ class i18n extends Object implements TemplateGlobalProvider {
// TODO Replace with CLDR list of actually available languages/regions
// Only allow explicitly registered locales, otherwise we'll get into trouble
// if the locale doesn't exist in Zend's CLDR data
$labelLocale = str_replace('-', '_', self::get_locale_from_lang($locale));
if(isset(self::$all_locales[$locale])) {
$locales[$locale] = self::$all_locales[$locale];
} else if(isset(self::$all_locales[$labelLocale])) {
$locales[$locale] = self::$all_locales[$labelLocale];
}
}
$fullLocale = self::get_locale_from_lang($locale);
if(isset($allLocales[$fullLocale])) {
$locales[$fullLocale] = $allLocales[$fullLocale];
}
}
}
}
@ -2356,7 +2354,7 @@ class i18n extends Object implements TemplateGlobalProvider {
public static function get_locale_from_lang($lang) {
$subtags = Config::inst()->get('i18n', 'likely_subtags');
if(preg_match('/\-|_/', $lang)) {
return $lang;
return str_replace('-', '_', $lang);
} else if(isset($subtags[$lang])) {
return $subtags[$lang];
} else {

View File

@ -13,12 +13,13 @@ cs:
SIZE: 'Velikost'
TITLE: Titulek
TYPE: 'Typ'
URL: URL
AssetUploadField:
ChooseFiles: 'Vyberte soubory'
DRAGFILESHERE: 'Táhni soubory sem'
DROPAREA: 'Oblast upustění'
EDITALL: 'Editovat vše'
EDITANDORGANIZE: 'Editovat & organizovat'
EDITANDORGANIZE: 'Editovat a organizovat'
EDITINFO: 'Editovat soubory'
FILES: Soubory
FROMCOMPUTER: 'Vyberte soubory z vašeho počítače'
@ -73,6 +74,7 @@ cs:
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Vaše heslo bylo změněno pro'
CHANGEPASSWORDTEXT2: 'Nyní můžete použít následující přihlašovací údaje pro přihlášení:'
EMAIL: E-mail
HELLO: Dobrý den
PASSWORD: Heslo
ComplexTableField:
@ -304,6 +306,7 @@ cs:
LINKOPENNEWWIN: 'Otevřít odkaz v novém okně?'
LINKTO: 'Odkázat na'
PAGE: Stránku
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' nemůže být vloženo do zdroje médií.'
UpdateMEDIA: 'Aktualizovat média'
BUTTONADDURL: 'Přidat url'
@ -342,6 +345,7 @@ cs:
IP: 'IP adresy'
PLURALNAME: 'Pokusy přihlášení'
SINGULARNAME: 'Pokus přihlášení'
Status: Stav
Member:
ADDGROUP: 'Přidat skupinu'
BUTTONCHANGEPASSWORD: 'Změnit heslo'
@ -354,9 +358,11 @@ cs:
DATEFORMAT: 'Formát datumu'
DefaultAdminFirstname: 'Implicitní Admin'
DefaultDateTime: výchozí
EMAIL: E-mail
EMPTYNEWPASSWORD: 'Nové heslo nesmí být prázdné, zkuste to znovu'
ENTEREMAIL: 'Zadejte e-mailovou adresu pro získání odkazu na restart hesla.'
ERRORLOCKEDOUT: 'Váš účet byl dočasně zablokován, kvůli příliš velkému množství nezdařených pokusů o přihlášení. Zkuste se prosím přihlásit za 20 minut.'
ERRORLOCKEDOUT2: 'Váš účet byl dočasně zablokován, kvůli příliš velkému množství nezdařených pokusů o přihlášení. Zkuste se prosím přihlásit za {count} minut.'
ERRORNEWPASSWORD: 'Zadali jste nové heslo rozdílně, zkuste to znovu'
ERRORPASSWORDNOTMATCH: 'Váše současné heslo není správně, prosím zkuste to znovu'
ERRORWRONGCRED: 'Toto nevypadá jako správná emailová adresa nebo heslo. Prosím zkuste to znovu.'
@ -384,6 +390,7 @@ cs:
db_NumVisit: 'Počet návštěvníků'
db_Password: Heslo
db_PasswordExpiry: 'Datum vypršení hesla'
NoPassword: 'Neni zde heslo pro tohoto člena'
MemberAuthenticator:
TITLE: 'E-mail a Heslo'
MemberDatetimeOptionsetField:
@ -466,6 +473,7 @@ cs:
PermissionRole:
OnlyAdminCanApply: 'Pouze administrátor může použít'
PLURALNAME: Role
SINGULARNAME: Role
Title: Název
PermissionRoleCode:
PLURALNAME: 'Kódy role oprávnění'
@ -481,6 +489,7 @@ cs:
NOTFOUND: 'Žádné položky'
Security:
ALREADYLOGGEDIN: 'K této stránce nemáte přístup. Pokud máte jiný účet, který k ní může přistupovat, můžete se přihlásit níže'
LOSTPASSWORDHEADER: 'Ztracené heslo'
BUTTONSEND: 'Pošlete mi nulovací odkaz pro heslo'
CHANGEPASSWORDBELOW: 'Svoje heslo si můžete změnit níže.'
CHANGEPASSWORDHEADER: 'Změnit heslo'
@ -516,7 +525,9 @@ cs:
FileFieldLabel: 'Soubor CSV <small>(Povoleny přípony: *.csv)</small>'
SilverStripeNavigator:
Edit: Editovat
Auto: Auto
ChangeViewMode: 'Změnit mód zobrazení'
Desktop: Desktop
DualWindowView: 'Dualní okno'
EditView: 'Mód editace'
Mobile: Mobilní telefon
@ -524,6 +535,7 @@ cs:
PreviewView: 'Mód náhledu'
Responsive: Responzivní
SplitView: 'Mód rozdělení'
Tablet: Tablet
ViewDeviceWidth: 'Vyberte šířku náhledu'
Width: šířka
SimpleImageField:
@ -593,3 +605,11 @@ cs:
PREVIEW: 'Náhled webu'
GridFieldEditButton_ss:
EDIT: Editovat
ContentController:
NOTLOGGEDIN: 'Nepřihlášen'
GridFieldItemEditView:
Go_back: 'Jdi zpět'
PasswordValidator:
LOWCHARSTRENGTH: 'Prosím, posilněte heslo přidáním některých z následujících znaků: %s'
PREVPASSWORD: 'Již jste použil toto heslo v minulosti, vyberte nové heslo, prosím'
TOOSHORT: 'Heslo je příliš krátké, musí být %s nebo více znaků dlouhé'

View File

@ -390,6 +390,7 @@ de:
db_NumVisit: 'Anzahl der Besuche'
db_Password: Passwort
db_PasswordExpiry: 'Ablaufdatum des Passworts'
NoPassword: 'Dieser Benutzer hat kein Passwort.'
MemberAuthenticator:
TITLE: 'E-Mail &amp; Passwort'
MemberDatetimeOptionsetField:
@ -488,6 +489,7 @@ de:
NOTFOUND: 'Keine Elemente gefunden'
Security:
ALREADYLOGGEDIN: 'Sie haben keinen Zugriff auf diese Seite. Wenn Sie ein anderes Konto besitzen, mit dem Sie auf diese Seite zugreifen können, melden Sie sich bitte unten an.'
LOSTPASSWORDHEADER: 'Passwort vergessen'
BUTTONSEND: 'Senden Sie mir den Link zur Passwortrücksetzung'
CHANGEPASSWORDBELOW: 'Sie können Ihr Passwort unten ändern.'
CHANGEPASSWORDHEADER: 'Passwort ändern'
@ -529,6 +531,7 @@ de:
DualWindowView: 'Zwei Fenster'
EditView: 'Bearbeitungsmodus'
Mobile: Mobil
PreviewState: 'Vorschau Status'
PreviewView: 'Vorschaumodus'
Responsive: Responsive
SplitView: 'Geteilter Modus'
@ -571,23 +574,29 @@ de:
ATTACHFILES: 'Dateien anhängen'
AttachFile: 'Datei(en) anhängen'
DELETE: 'Aus Dateien löschen'
DELETEINFO: 'Diese Datei am Server löschen'
DOEDIT: Speichern
DROPFILE: 'Datei hier ablegen'
DROPFILES: 'Dateien hier ablegen'
Dimensions: Dimensionen
EDIT: Bearbeiten
EDITINFO: 'Diese Datei bearbeiten'
FIELDNOTSET: 'Dateiinformationen nicht gefunden'
FROMCOMPUTER: 'Von Ihrem Computer'
FROMCOMPUTERINFO: 'Aus Dateien auswählen'
FROMFILES: 'Aus Dateien'
HOTLINKINFO: 'Info: Dieses Bild wird verknüpft. Bitte vergewissere dich die Erlaubnis des Inhabers der Ursprungsseite zu haben.'
MAXNUMBEROFFILES: 'Maximale Anzahl an {count} Datei(en) überschritten'
MAXNUMBEROFFILESSHORT: 'SIe können maximal {count} Datei(en) hochladen'
MAXNUMBEROFFILESONE: 'SIe können maximal eine Datei hochladen'
REMOVE: Entfernen
REMOVEERROR: 'Fehler beim Entfernen der Datei'
REMOVEINFO: 'Diese Datei entfernen aber nicht am Server löschen'
STARTALL: 'Alle starten'
STARTALLINFO: 'Alle Uploads starten'
Saved: Gespeichert
CHOOSEANOTHERFILE: 'Andere Datei auswählen'
CHOOSEANOTHERINFO: 'Diese Datei mit einer Datei vom Server ersetzen'
OVERWRITEWARNING: 'Eine Datei mit dem selben Namen existiert bereits'
UPLOADSINTO: 'speichert nach /{path}'
Versioned:
@ -596,3 +605,11 @@ de:
PREVIEW: 'Vorschau der Webseite'
GridFieldEditButton_ss:
EDIT: Bearbeiten
ContentController:
NOTLOGGEDIN: 'Nicht eingeloggt'
GridFieldItemEditView:
Go_back: 'Zurück'
PasswordValidator:
LOWCHARSTRENGTH: 'Bitte erhöhen Sie die Sicherheit des Passworts, indem Sie auch einige der folgenden Zeichen verwenden: %s'
PREVPASSWORD: 'Sie haben dieses Passwort schon einmal verwendet. Bitte wählen Sie ein neues Passwort'
TOOSHORT: 'Das Passwort ist zu kurz, es muss mindestens %s Zeichen lang sein'

View File

@ -1,8 +1,11 @@
fr:
AssetAdmin:
ALLOWEDEXTS: 'Extensions autorisées'
NEWFOLDER: Nouveau dossier
SHOWALLOWEDEXTS: 'Montrer les extensions autorisées'
AssetTableField:
CREATED: 'Premier chargement'
DIM: Dimensions
FILENAME: Nom du fichier
FOLDER: Dossier
LASTEDIT: 'Dernière modification'
@ -10,6 +13,7 @@ fr:
SIZE: 'Taille'
TITLE: Titre
TYPE: 'Type'
URL: URL
AssetUploadField:
ChooseFiles: 'Choisissez les fichiers'
DRAGFILESHERE: 'Glissez des fichiers ici'
@ -20,6 +24,7 @@ fr:
FILES: Fichiers
FROMCOMPUTER: 'Choisissez des fichiers de votre ordinateur'
FROMCOMPUTERINFO: 'Télécharger depuis votre ordinateur'
TOTAL: Total
TOUPLOAD: 'Choisissez les fichiers à télécharger…'
UPLOADINPROGRESS: 'Patientez s''il vous plaît… téléchargement en cours '
UPLOADOR: OU
@ -35,6 +40,7 @@ fr:
COLOREDEXAMPLE: 'texte bleu'
EMAILLINK: 'Lien email'
EMAILLINKDESCRIPTION: 'Créer un lien vers une adresse email'
IMAGE: Image
IMAGEDESCRIPTION: 'Afficher une image dans votre message'
ITALIC: 'Texte en italique'
ITALICEXAMPLE: Italique
@ -68,6 +74,7 @@ fr:
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Vous avez modifié votre mot de passe pour'
CHANGEPASSWORDTEXT2: 'Vous pouvez maintenant utiliser les identifiants suivants pour vous connecter&nbsp;:'
EMAIL: Email
HELLO: Salut
PASSWORD: Mot de passe
ComplexTableField:
@ -90,12 +97,31 @@ fr:
CreditCardField:
FIRST: premier
FOURTH: quatrième
SECOND: second
THIRD: troisième
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Modèles de données'
SINGULARNAME: 'Modèle de donées'
Date:
DAY: jour
DAYS: jours
HOUR: heure
HOURS: heures
MIN: min
MINS: mins
MONTH: mois
MONTHS: mois
SEC: sec
SECS: secs
TIMEDIFFAGO: 'Il y a {difference}'
TIMEDIFFIN: 'Dans {difference}'
YEAR: année
YEARS: années
LessThanMinuteAgo: 'moins d''une minute'
DateField:
NOTSET: 'pas d''ensemble'
NOTSET: 'non renseigné'
TODAY: aujourd'hui
VALIDDATEFORMAT2: 'Saisissez la date au format valide ({format})'
VALIDDATEMAXDATE: 'La date doit être antérieure ou égale à celle qui a été autorisée ({date})'
@ -169,6 +195,7 @@ fr:
TEXT2: 'lien de réinitialisation de mot de passe'
TEXT3: pour
Form:
FIELDISREQUIRED: '{name} requis'
SubmitBtnLabel: Envoyer
VALIDATIONCREDITNUMBER: 'Vérifiez que vous avez bien saisi votre numéro de carte bleue {number}.'
VALIDATIONNOTUNIQUE: 'La valeur entrée n''est pas unique'
@ -177,8 +204,10 @@ fr:
VALIDATIONSTRONGPASSWORD: 'Le mot de passe doit comporter au moins un chiffre et un caractère alphanumérique'
VALIDATOR: Validateur
VALIDCURRENCY: 'Saisissez une monnaie valide'
CSRF_FAILED_MESSAGE: 'Il semble qu''il y ait eu un problème technique. Veuillez cliquez sur le bouton Retour, raffraîchir votre navigateur, et essayer à nouveau'
FormField:
NONE: aucun
Example: 'par exemple %s'
GridAction:
DELETE_DESCRIPTION: Supprime
Delete: Supprimer
@ -200,6 +229,7 @@ fr:
ResetFilter: Réinitialiser
GridFieldAction_Delete:
DeletePermissionsFailure: 'Vous navez pas les autorisations pour supprimer'
EditPermissionsFailure: 'Pas de permissions pour délier l''enregistrement'
GridFieldDetailForm:
CancelBtn: Annuler
Create: Créer
@ -207,12 +237,16 @@ fr:
DeletePermissionsFailure: 'Vous navez pas les autorisations pour supprimer'
Deleted: '%s %s supprimés'
Save: Enregistrer
Saved: '{name} {link} sauvegardé'
GridFieldItemEditView_ss:
Go_back: 'Revenir en arrière'
Group:
AddRole: 'Ajouter un rôle pour ce groupe'
Code: 'Code de groupe'
DefaultGroupTitleAdministrators: Administrateur
DefaultGroupTitleContentAuthors: 'Auteurs du contenu'
Description: Description
GroupReminder: 'Si vous choisissez un groupe parent, ce groupe prendra tous ses rôles'
Locked: 'Verrouillé?'
NoRoles: 'Vous navez pas la permission pour faire ça'
PLURALNAME: Groupes
@ -255,6 +289,7 @@ fr:
IMAGEALT: 'Texte alternatif (alt)'
IMAGEALTTEXT: 'Texte alternatif (alt) - s''affiche si l''image ne peut être affichée.'
IMAGEALTTEXTDESC: 'Proposé aux lecteurs décran ou si limage ne peut pas être affichée'
IMAGEDIMENSIONS: Dimensions
IMAGEHEIGHTPX: Hauteur
IMAGETITLE: 'Texte du titre (tooltip) - informations à propos de l''image'
IMAGETITLETEXT: 'Texte du titre (info-bulle)'
@ -270,11 +305,16 @@ fr:
LINKINTERNAL: 'Une page du site'
LINKOPENNEWWIN: 'Ouvrir le lien dans une nouvelle fenêtre ?'
LINKTO: 'Lier à'
PAGE: Page
URL: URL
URLNOTANOEMBEDRESOURCE: 'LURL {url} na pas pu être utilisée comme ressource média.'
UpdateMEDIA: 'Mettre à jour le support audiovisuel'
BUTTONADDURL: 'Ajouter une URL'
Image:
PLURALNAME: Fichiers
SINGULARNAME: Fichier
ImageField:
IMAGE: Image
Image_Cached:
PLURALNAME: Fichiers
SINGULARNAME: Fichier
@ -283,6 +323,7 @@ fr:
LeftAndMain:
CANT_REORGANISE: 'Vous navez pas lautorisation de modifier les pages de premier niveau. Vos modifications nont pas été enregistrées.'
DELETED: Supprimé.
DropdownBatchActionsDefault: Actions
HELP: Aide
PAGETYPE: 'Type de page&nbsp;:'
PERMAGAIN: 'Vous avez été déconnecté du CMS. Si vous voulez vous reconnecter, entrez un nom d''utilisateur et un mot de passe ci-dessous.'
@ -293,6 +334,9 @@ fr:
REORGANISATIONSUCCESSFUL: 'Larbre du site a été bien réorganisé.'
SAVEDUP: Enregistré.
VersionUnknown: inconnu
ShowAsList: 'lister'
TooManyPages: 'Trop de pages'
ValidationError: 'Erreur de validation'
LeftAndMain_Menu_ss:
Hello: Bonjour
LOGOUT: 'Déconnexion'
@ -314,10 +358,12 @@ fr:
DATEFORMAT: 'Format de la date'
DefaultAdminFirstname: 'Administrateur par défaut'
DefaultDateTime: par défaut
EMAIL: Email
EMPTYNEWPASSWORD: 'Le champs nouveau mot de passe ne peut être vide, essayez de nouveau'
ENTEREMAIL: 'Veuillez écrire une adresse email pour obtenir le lien de réinitialisation du mot de passe.'
ERRORLOCKEDOUT: 'Votre compte a été désactivé temporairement à cause de multiples tentatives de connexions. Veuillez réessayer dans 20 minutes.'
ERRORNEWPASSWORD: 'Vous avez entré votre nouveau mot de passe différemment, essayez encore'
ERRORLOCKEDOUT2: 'Votre compte a été temporairement désactivé à cause de trop nombreux échecs d''identification. Veuillez réessayer dans {count} minutes.'
ERRORNEWPASSWORD: 'Vous avez entré votre nouveau mot de passe différemment, réessayez'
ERRORPASSWORDNOTMATCH: 'Votre actuel mot de passe ne correspond pas, essayez encore s''il vous plaît'
ERRORWRONGCRED: 'Il semble que ce ne soit pas le bon email ou mot de passe. Essayez encore s''il vous plaît.'
FIRSTNAME: 'Prénom'
@ -344,10 +390,11 @@ fr:
db_NumVisit: 'Nombre de visite'
db_Password: Mot de passe
db_PasswordExpiry: 'Date d''expiration du mot de passe'
NoPassword: 'Ce membre n''a pas de mot de passe'
MemberAuthenticator:
TITLE: 'Email &amp; Mot de passe'
MemberDatetimeOptionsetField:
AMORPM: 'AM (Ante meridiem) ou PM (Post meridiem)'
AMORPM: 'AM (matin) ou PM (après-midi)'
Custom: Personnalisé
DATEFORMATBAD: 'Format de date incorrect'
DAYNOLEADING: 'Le jour du mois sans zéro initial'
@ -366,6 +413,7 @@ fr:
TWODIGITMONTH: 'Le mois sur deux chiffres (p. ex. 01=janvier)'
TWODIGITSECOND: 'La seconde sur deux chiffres (de 00 à 59)'
TWODIGITYEAR: 'Lannée sur deux chiffres'
Toggle: 'Afficher laide de mise en forme'
MemberImportForm:
Help1: '<p>Importer les membres au format<em>CSV format</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Afficher l''usage avancé.</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Utilisation avancée</h4>\n<ul>\n<li>Colonnes autorisées&nbsp;: <em>%s</em></li>\n<li>Les utilisateurs existants sont retrouvés avec leur <em>Code</em> unique et les registres sont mis à jour avec les nouvelles valeurs du fichier importé.</li>\n<li>Des groupes peuvent être assignés à laide de la colonne <em>Groups</em>. Les groupes sont identifiés par leur <em>Code</em>, plusieurs groupes peuvent être indiqués en les séparant par des virgules. Lappartenance actuelle aux groupes nest pas modifiée.</li>\n</ul>\n</div>"
@ -381,6 +429,7 @@ fr:
ModelAdmin:
DELETE: Supprime
DELETEDRECORDS: '{count} enregistrements supprimés.'
EMPTYBEFOREIMPORT: 'Remplacer les données'
IMPORT: 'Importer de CSV'
IMPORTEDRECORDS: '{count} enregistrements importés.'
NOCSVFILE: 'Veuillez choisir un fichier CSV à importer'
@ -391,6 +440,7 @@ fr:
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Colonnes de la base de données'
IMPORTSPECLINK: 'Afficher la spécification de %s'
IMPORTSPECRELATIONS: Relations
IMPORTSPECTITLE: 'Spécification de %s'
ModelAdmin_Tools_ss:
FILTER: Filtrer
@ -406,6 +456,7 @@ fr:
NumericField:
VALIDATION: '« {value} » nest pas un chiffre, seule donnée acceptée dans ce champ '
Pagination:
Page: Page
View: Afficher
Permission:
AdminGroup: Administrateur
@ -438,6 +489,7 @@ fr:
NOTFOUND: 'Aucun élément na été trouvé'
Security:
ALREADYLOGGEDIN: 'Vous n''avez pas accès à cette page. Si vous avez un autre identifiant pouvant accéder à cette page, vous pouvez l''utiliser ci-dessous.'
LOSTPASSWORDHEADER: 'Mot de passe oublié'
BUTTONSEND: 'Envoyer moi le lien pour modifier le mot de passe'
CHANGEPASSWORDBELOW: 'Vous pouvez modifier votre mot de passe ci-dessous.'
CHANGEPASSWORDHEADER: 'Modifier votre mot de passe'
@ -457,6 +509,8 @@ fr:
EDITPERMISSIONS: 'Gérer les autorisations des groupes'
EDITPERMISSIONS_HELP: 'Possibilité d''éditer les autorisations et les adresses IP pour un groupe. Nécessite lautorisation « Accès à la section “Securité” ».'
GROUPNAME: 'Nom du group'
IMPORTGROUPS: 'Importer des groupes'
IMPORTUSERS: 'Importer des utilisateurs'
MEMBERS: Membres
MENUTITLE: Sécurité
MemberListCaution: 'Attention&nbsp;: en supprimant des membres de cette liste vous les enlèverez de tous les groupes ainsi que de la base de données'
@ -471,6 +525,19 @@ fr:
FileFieldLabel: 'Fichier CSV <small>(extension autorisée&nbsp;: *.csv)</small>'
SilverStripeNavigator:
Edit: Tout modifier
Auto: Auto
ChangeViewMode: 'Changer de mode de vue'
Desktop: Bureau
DualWindowView: 'Double fenêtre'
EditView: 'Editer le mode'
Mobile: Mobile
PreviewState: 'Aperçu'
PreviewView: 'Aperçu'
Responsive: Responsive
SplitView: 'Mode partagé'
Tablet: Tablette
ViewDeviceWidth: 'Sélectionnez une largeur d''aperçu'
Width: largeur
SimpleImageField:
NOUPLOAD: 'Aucune image chargée'
SiteTree:
@ -510,17 +577,39 @@ fr:
DELETEINFO: 'Effacer définitivement ce fichier des archives'
DOEDIT: Enregistrer
DROPFILE: 'glissez-déposez un fichier'
DROPFILES: 'glissez-déposez des fichiers'
Dimensions: Dimensions
EDIT: Modifier
EDITINFO: 'Modifier ce fichier'
FIELDNOTSET: 'Informations concernant le fichiers non-trouvées'
FROMCOMPUTER: 'Depuis votre ordinateur'
FROMCOMPUTERINFO: 'Choisir parmi les fichiers'
FROMFILES: 'Depuis les fichiers'
HOTLINKINFO: 'Note : Cette image sera liée par un « hotlink », assurez-vous davoir lautorisation des ayant-droits du site web dorigine.'
MAXNUMBEROFFILES: 'Le nombre maximal de {count} fichiers a été dépassé.'
MAXNUMBEROFFILESSHORT: 'On ne peut pas télécharger plus de {count} fichiers'
MAXNUMBEROFFILESONE: 'Vous ne pouvez pas uploader plus d''un fichier'
REMOVE: Supprimer
REMOVEERROR: 'Le fichier na pas pu être supprimé'
REMOVEINFO: 'Supprimer ce fichier ici sans leffacer des archives'
STARTALL: 'Démarrer tout'
STARTALLINFO: 'Démarrer tous les téléchargements'
Saved: Enregistré
CHOOSEANOTHERFILE: 'Choisissez un autre fichier'
CHOOSEANOTHERINFO: 'Remplacer ce fichier par un autre depuis les archives'
OVERWRITEWARNING: 'Un fichier avec le même nom existe déjà'
UPLOADSINTO: 'sauvegarder dans /{path}'
Versioned:
has_many_Versions: Versions
CMSPageHistoryController_versions_ss:
PREVIEW: 'Aperçu du site'
GridFieldEditButton_ss:
EDIT: Editer
ContentController:
NOTLOGGEDIN: 'Hors-ligne'
GridFieldItemEditView:
Go_back: 'Retour'
PasswordValidator:
LOWCHARSTRENGTH: 'Veuillez augmenter la force de votre mot de passe en ajoutant certains caractères suivants : %s'
PREVPASSWORD: 'Vous avez déjà utilisé ce mot de passe par le passé, veuillez en choisir un autre'
TOOSHORT: 'Le mot de passe est trop court, il doit contenir au moins %s caractères'

View File

@ -1,6 +1,8 @@
hi:
AssetUploadField:
ChooseFiles: 'फाइलें चुनें'
ChooseFiles: 'अपलोड करने के लिए फ़ाइलों को चुनें'
DRAGFILESHERE: 'अपलोड करने के लिए फाइलें यहाँ लाये '
DROPAREA: 'अपलोड छेत्र '
EDITALL: 'सभी संपादित करें'
EDITANDORGANIZE: 'संपादित और व्यवस्थित करें '
EDITINFO: 'फ़ाइलों को संपादित करें'
@ -28,6 +30,27 @@ hi:
ITALIC: 'तिरछे अक्षर'
ITALICEXAMPLE: तिरछे अक्षर
LINK: 'वेबसाइट लिंक'
LINKDESCRIPTION: ' किसी दुसरे वेबसाइट या यूआरएल से जोड़े '
BackLink_Button_ss:
Back: वापस
BasicAuth:
ENTERINFO: 'कृपया उपयोगकर्ता का नाम और पासवर्ड दर्ज करें'
ERRORNOTADMIN: 'वह उपयोगकर्ता एक व्यवस्थापक नहीं है.'
CMSProfileController:
MENUTITLE: 'मेरी प्रोफाइल '
ComplexTableField_popup_ss:
NEXT: अगला
PREVIOUS: पिछला
ConfirmedPasswordField:
ATLEAST: 'पासवर्डों को कम से कम {min} अक्षर लंबा होना चाहिए.'
SHOWONCLICKTITLE: 'पासवर्ड बदलें'
DataObject:
PLURALNAME: 'डेटा ऑब्जेक्ट्स'
SINGULARNAME: 'डेटा ऑब्जेक्ट'
DateField:
NOTSET: 'सेट नहीं'
TODAY: आज
VALIDDATEFORMAT2: 'कृपया एक मान्य दिनांक स्वरूप दर्ज करें ({format})'
Group:
RolesAddEditLink: 'भूमिकाओं का प्रबंधन करे '
has_many_Permissions: अनुमतियाँ

View File

@ -24,6 +24,7 @@ nb:
FILES: Filer
FROMCOMPUTER: 'Velg filer fra din pc'
FROMCOMPUTERINFO: 'Last opp fra din pc'
TOTAL: Total
TOUPLOAD: 'Velg filer til opplasting ...'
UPLOADINPROGRESS: 'Vennligst vent... opplasting pågår'
UPLOADOR: ELLER
@ -98,6 +99,8 @@ nb:
FOURTH: fjerde
SECOND: andre
THIRD: tredje
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Dataobjekter'
SINGULARNAME: 'Dataobjekt'
@ -140,6 +143,7 @@ nb:
AviType: 'AVI videofil'
Content: Innhold
CssType: 'CSS-fil'
DmgType: 'Apple disk image'
DocType: 'Word-dokument'
Filename: Filnavn
GifType: 'GIF bilde - bra til diagrammer'
@ -162,6 +166,7 @@ nb:
SINGULARNAME: Fil
TOOLARGE: 'Filstørrelse for stor, maksimum {size} tillatt '
TOOLARGESHORT: 'Filstørrelse overstiger {size}'
TiffType: 'Tagged image format'
Title: Tittel
WavType: 'WAV lydfil'
XlsType: 'Excel regneark'
@ -188,6 +193,7 @@ nb:
HELLO: Hei
TEXT1: 'Her er din'
TEXT2: 'lenke for nullstilling av passord'
TEXT3: for
Form:
FIELDISREQUIRED: '{name} er påkrevet'
SubmitBtnLabel: Utfør
@ -196,6 +202,7 @@ nb:
VALIDATIONPASSWORDSDONTMATCH: 'Passordene stemmer ikke overens'
VALIDATIONPASSWORDSNOTEMPTY: 'Passord kan ikke være tomt'
VALIDATIONSTRONGPASSWORD: 'Passord må inneholde minst ett siffer og en bokstav'
VALIDATOR: Validator
VALIDCURRENCY: 'Vennligst skriv inn gyldig valuta'
CSRF_FAILED_MESSAGE: 'Det ser ut til å ha oppstått et teknisk problem. Vennligst trykk på tilbakeknappen, oppdater nettsiden og prøv på nytt.'
FormField:
@ -204,16 +211,21 @@ nb:
GridAction:
DELETE_DESCRIPTION: Slett
Delete: Slett
UnlinkRelation: Koble fra
GridField:
Add: 'Legg til {name}'
Filter: Filtrer
FilterBy: 'Filtrer på '
Find: Finn
LEVELUP: 'Opp ett nivå'
LinkExisting: 'Eksisterende lenke'
NewRecord: 'Ny %s'
NoItemsFound: 'Ingen elementer ble funnet'
PRINTEDAT: 'Skrevet ut ved'
PRINTEDBY: 'Skrevet ut av'
PlaceHolder: 'Finn {type}'
PlaceHolderWithLabels: 'Finn {type} ved {name}'
RelationSearch: 'Relasjonssøk'
ResetFilter: Tilbakestille
GridFieldAction_Delete:
DeletePermissionsFailure: 'Ikke tillatt å slette'
@ -232,6 +244,7 @@ nb:
AddRole: 'Legg til en rolle for denne gruppen'
Code: 'Gruppekode'
DefaultGroupTitleAdministrators: Administratorer
DefaultGroupTitleContentAuthors: 'Innholdsforfattere'
Description: Beskrivelse
GroupReminder: 'Hvis du velger en overordnet gruppe, vil denne gruppen arve alle rollene'
Locked: 'Låst?'
@ -244,6 +257,7 @@ nb:
has_many_Permissions: Tillatelser
many_many_Members: Medlemmer
GroupImportForm:
Help1: '<p>Importer en eller flere grupper i <em>CSV</em>-format (kommaseparerte verdier). <small><a href="#" class="toggle-advanced">Vis avanserte alternativer</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Avanserte alternativer</h4>\n<ul>\n<li>Tillatte kolonner: <em>%s</em></li>\n<li>Eksisterende grupper matches mot deres <em>Code</em>-verdi og oppdateres med nye verdier fra den importerte filen.</li>\n<li>Gruppehierarkier kan bli opprettet ved å benytte en <em>ParentCode</em>-kolonne.</li>\n<li>Tillatelseskoder kan bli angitt med <em>PermissionCode</em>-kolonnen. Eksisterende tillatelselskoder blir ikke fjernet.</li>\n</ul>\n</div>"
ResultCreated: 'Opprettet {count} grupper'
ResultDeleted: 'Slettet %d grupper'
@ -331,6 +345,7 @@ nb:
IP: 'IP-adresse'
PLURALNAME: 'Innloggingsforsøk'
SINGULARNAME: 'Innloggingsforsøk'
Status: Status
Member:
ADDGROUP: 'Legg til en gruppe'
BUTTONCHANGEPASSWORD: 'Bytt passord'
@ -341,10 +356,13 @@ nb:
CONFIRMNEWPASSWORD: 'Bekreft nytt passord'
CONFIRMPASSWORD: 'Bekreft passord'
DATEFORMAT: 'Datoformat'
DefaultAdminFirstname: 'Standard Admin'
DefaultDateTime: standard
EMAIL: Epost
EMPTYNEWPASSWORD: 'Det nye passordet kan ikke være tomt, vennligst prøv igjen'
ENTEREMAIL: 'Vennligst skriv en epostadresse så du kan bli tilsendt en lenke til å nullstille passord.'
ERRORLOCKEDOUT: 'Din konto har blitt sperret på grunn av for mange forsøk på å logge inn. Vennligst prøv igjen om 20 minutter.'
ERRORLOCKEDOUT2: 'Din konto har blitt midlertidig sperret på grunn av for mange mislykkede forsøk på å logge inn. Vennligst prøv igjen om {count} minutter.'
ERRORNEWPASSWORD: 'Du har tastet inn nye passord forskjellig, vennligst prøv igjen.'
ERRORPASSWORDNOTMATCH: 'Passordene stemmer ikke overens, vennligst prøv igjen.'
ERRORWRONGCRED: 'Det ser ikke ut til å være riktig epostadresse eller passord. Vennligst prøv igjen.'
@ -372,9 +390,12 @@ nb:
db_NumVisit: 'Antall besøk'
db_Password: Passord
db_PasswordExpiry: 'Utløpsdato for passord'
NoPassword: 'Det finnes ikke noe passord for dette medlemmet.'
MemberAuthenticator:
TITLE: 'E-post og passord'
MemberDatetimeOptionsetField:
AMORPM: 'AM (Ante meridiem) or PM (Post meridiem)'
Custom: Egendefinert
DATEFORMATBAD: 'Datoformat er ugyldig'
DAYNOLEADING: 'Dag i måneden uten ledende null'
DIGITSDECFRACTIONSECOND: 'Ett eller flere siffer som representerer en desimlbrøkdel av et sekund'
@ -411,6 +432,7 @@ nb:
EMPTYBEFOREIMPORT: 'Erstatt data'
IMPORT: 'Importer fra CSV'
IMPORTEDRECORDS: 'Importerte {count} oppføringer.'
NOCSVFILE: 'Vennligst finn en CSV-fil å importere'
NOIMPORT: 'Ingenting å importere'
RESET: Tilbakestill
Title: 'Datamodeller'
@ -437,6 +459,7 @@ nb:
Page: Side
View: Viser
Permission:
AdminGroup: Administratorer
CMS_ACCESS_CATEGORY: 'Tilgang til publiseringssystem'
FULLADMINRIGHTS: 'Fulle administrative rettigheter'
FULLADMINRIGHTS_HELP: 'Overstyrer alle andre tilordnede tillatelser.'
@ -466,6 +489,7 @@ nb:
NOTFOUND: 'Ingen elementer ble funnet'
Security:
ALREADYLOGGEDIN: 'Du har ikke adgang til denne siden. Hvis du har en annen konto som har adgang til denne siden, kan du logge inn med den under.'
LOSTPASSWORDHEADER: 'Mistet passord'
BUTTONSEND: 'Send meg en lenke for å nullstille passordet'
CHANGEPASSWORDBELOW: 'Du kan bytte passord under her.'
CHANGEPASSWORDHEADER: 'Bytt passord'
@ -485,15 +509,20 @@ nb:
EDITPERMISSIONS: 'Administrer tillatelser for grupper'
EDITPERMISSIONS_HELP: 'Mulighet for å endre tillatelser og IP-adresser for en gruppe. Brukere må ha adgang til sikkerhetsseksjon.'
GROUPNAME: 'Gruppenavn'
IMPORTGROUPS: 'Importer grupper'
IMPORTUSERS: 'Importer brukere'
MEMBERS: Medlemmer
MENUTITLE: Sikkerhet
MemberListCaution: 'Advarsel: Hvis du fjerner medlemmer fra denne listen vil det samtidig fjerne dem fra alle grupper og databasen'
NEWGROUP: 'Ny gruppe'
PERMISSIONS: Tilganger
ROLES: Roller
ROLESDESCRIPTION: 'Roller er forhåndsdefinerte lister av tillatelser og kan angis til grupper.<br />De blir arvet fra overerdnede grupper om nødvendig.'
TABROLES: Roller
Users: Brukere
SecurityAdmin_MemberImportForm:
BtnImport: 'Importer fra CSV'
FileFieldLabel: 'CSV-fil <small>(Tillatt filtype: *.csv)</small>'
SilverStripeNavigator:
Edit: Rediger
Auto: Automatisk
@ -541,6 +570,7 @@ nb:
LESS: mindre
MORE: mer
UploadField:
ATTACHFILE: 'Legg ved en fil'
ATTACHFILES: 'Legg ved filer'
AttachFile: 'Legg ved fil(er)'
DELETE: 'Slett fra filer'
@ -575,3 +605,11 @@ nb:
PREVIEW: 'Forhåndsvisning'
GridFieldEditButton_ss:
EDIT: Rediger
ContentController:
NOTLOGGEDIN: 'Ikke innlogget'
GridFieldItemEditView:
Go_back: 'Gå tilbake'
PasswordValidator:
LOWCHARSTRENGTH: 'Vennligst øk passordstyrken ved å legge til noen av følgende tegn: %s'
PREVPASSWORD: 'Du har brukt passordet tidligere, vennligst velg et nytt passord'
TOOSHORT: 'Passordet er for kort, det må være %s eller flere tegn langt'

View File

@ -13,8 +13,10 @@ sk:
SIZE: 'Veľkosť'
TITLE: Titulok
TYPE: 'Typ'
URL: URL
AssetUploadField:
DRAGFILESHERE: 'Tiahni súbory sem'
ChooseFiles: 'Vyberte súbory'
DRAGFILESHERE: 'Tiahni súbory tu'
DROPAREA: 'Oblasť upustenia'
EDITALL: 'Editovať všetko'
EDITANDORGANIZE: 'Editovať a organizovať'
@ -72,6 +74,7 @@ sk:
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Vaše heslo bolo zmenené pre'
CHANGEPASSWORDTEXT2: 'Teraz môžete použiť nasledujúce prihlasovacie údaje na prihlásenie:'
EMAIL: E-mail
HELLO: Dobrý deň
PASSWORD: Heslo
ComplexTableField:
@ -96,6 +99,8 @@ sk:
FOURTH: štvrtý
SECOND: druhý
THIRD: tretí
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Datové objekty'
SINGULARNAME: 'Dátový objekt'
@ -209,6 +214,7 @@ sk:
UnlinkRelation: Odpojiť
GridField:
Add: 'Pridať {name}'
Filter: Filter
FilterBy: 'Filtrovať podľa'
Find: Vyhľadať
LEVELUP: 'Úroveň vyššie'
@ -220,6 +226,7 @@ sk:
PlaceHolder: 'Vyhľadať {type}'
PlaceHolderWithLabels: 'Vyhľadať {type} podľa {name}'
RelationSearch: 'Vzťah hľadania'
ResetFilter: Reset
GridFieldAction_Delete:
DeletePermissionsFailure: 'Žiadne oprávnenia zmazať'
EditPermissionsFailure: 'Žiadne oprávnenie pre odpojenie záznamu'
@ -261,6 +268,7 @@ sk:
ADDURL: 'Pridať URL'
ADJUSTDETAILSDIMENSIONS: 'Detaily &amp; rozmery'
ANCHORVALUE: Odkaz
BUTTONINSERT: Vložiť
BUTTONINSERTLINK: 'Vložiť odkaz'
BUTTONREMOVELINK: 'Odstrániť odkaz'
BUTTONUpdate: Aktualizovať
@ -298,6 +306,7 @@ sk:
LINKOPENNEWWIN: 'Otvoriť odkaz v novom okne?'
LINKTO: 'Odkázať na'
PAGE: Stránku
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' nemôže byť vložené do zdroja médií.'
UpdateMEDIA: 'Aktualizovať média'
BUTTONADDURL: 'Pridať url'
@ -314,6 +323,7 @@ sk:
LeftAndMain:
CANT_REORGANISE: 'Nemáte oprávnenie meniť stránky najvyššej úrovne. Vaša zmena nebola uložená.'
DELETED: Zmazané.
DropdownBatchActionsDefault: Akcie
HELP: Pomoc
PAGETYPE: 'Typ stránky:'
PERMAGAIN: 'Boli ste odhlásený'
@ -323,6 +333,7 @@ sk:
PreviewButton: Náhľad
REORGANISATIONSUCCESSFUL: 'Strom webu bol reorganizovaný úspešne.'
SAVEDUP: Uložené.
VersionUnknown: Neznáme
ShowAsList: 'ukázať ako zoznam'
TooManyPages: 'Príliž veľa stránok'
ValidationError: 'Chyba platnosti'
@ -334,7 +345,9 @@ sk:
IP: 'IP adreasa'
PLURALNAME: 'Pokusy o prihlásenie'
SINGULARNAME: 'Pokus o prihlásenie'
Status: Stav
Member:
ADDGROUP: 'Pridať skupinu'
BUTTONCHANGEPASSWORD: 'Zmeniť heslo'
BUTTONLOGIN: 'Prihlásiť sa'
BUTTONLOGINOTHER: 'Prihlásiť sa ako niekto iný'
@ -349,6 +362,7 @@ sk:
EMPTYNEWPASSWORD: 'Nové heslo nesmie byť prázdne, skúste to prosím znova'
ENTEREMAIL: 'Prosím zadajte emailovú adresu pre zaslanie odkazu na resetovanie hesla.'
ERRORLOCKEDOUT: 'Váš účet bol dočasne zablokovaný, kvôli množstvu neúspešných pokusov o prihlásenie. Prosí skúste to znova za 20 minút.'
ERRORLOCKEDOUT2: 'Váš účet bol dočasne zablokovaný, kvôli množstvu neúspešných pokusov o prihlásenie. Prosím skúste to znova za {count} minút.'
ERRORNEWPASSWORD: 'Zadali ste rozdielne nové heslo, skúste to znovu'
ERRORPASSWORDNOTMATCH: 'Vaše súčasné heslo nie je správne, prosím skúste to znovu'
ERRORWRONGCRED: 'Toto nevyzerá ako správna e-mailová adresa alebo heslo. Prosím skúste to znovu.'
@ -367,6 +381,7 @@ sk:
TIMEFORMAT: 'Formát času'
VALIDATIONMEMBEREXISTS: 'Člen s takýmto e-mailom už existuje'
ValidationIdentifierFailed: 'Nemôžte prepísať existujúceho člena #{id} s identickým identifikátorm ({name} = {value}))'
WELCOMEBACK: 'Vitajte späť, {firstname}'
YOUROLDPASSWORD: 'Vaše staré heslo'
belongs_many_many_Groups: Skupiny
db_LastVisited: 'Dátum poslednej navštevy'
@ -375,10 +390,12 @@ sk:
db_NumVisit: 'Počet návštev'
db_Password: Heslo
db_PasswordExpiry: 'Dátum expirácie hesla'
NoPassword: 'Nie je tu heslo pre tohto člena.'
MemberAuthenticator:
TITLE: 'E-mail &amp; Heslo'
MemberDatetimeOptionsetField:
AMORPM: 'AM (pred poludním) alebo PM (popoludní)'
Custom: Vlastné
DATEFORMATBAD: 'Formát dátumu je neplatný'
DAYNOLEADING: 'Deň mesiaca bez úvodnej nuly'
DIGITSDECFRACTIONSECOND: 'Jedna alebo viac číslic zastupujúcich desatinný zlomok sekundy'
@ -387,6 +404,7 @@ sk:
HOURNOLEADING: 'Hodina bez úvodnej nuly'
MINUTENOLEADING: 'Minúta bez úvodnej nuly'
MONTHNOLEADING: 'Číslo mesiaca bez úvodnej nuly'
Preview: Náhľad
SHORTMONTH: 'Krátky názov mesiaca (napr. jún)'
TOGGLEHELP: 'Prepnúť nápovedu formátovania'
TWODIGITDAY: 'Dvojčíslie dňa mesiaca'
@ -416,6 +434,7 @@ sk:
IMPORTEDRECORDS: 'Importovaných {count} záznamov.'
NOCSVFILE: 'Prosím vyhľadajte CSV súbor pre importovanie'
NOIMPORT: 'Nie je čo importovať'
RESET: Reset
Title: 'Dátové modely'
UPDATEDRECORDS: 'Aktualizovaných {count} záznamov.'
ModelAdmin_ImportSpec_ss:
@ -432,6 +451,8 @@ sk:
MoneyField:
FIELDLABELAMOUNT: Množstvo
FIELDLABELCURRENCY: Mena
NullableField:
IsNullLabel: 'Je Null'
NumericField:
VALIDATION: '''{value}'' nie je číslo, iba čísla môžu byť akceptované pre toto pole'
Pagination:
@ -468,6 +489,7 @@ sk:
NOTFOUND: 'Žiadne položky'
Security:
ALREADYLOGGEDIN: 'K tejto stránke nemáte prístup. Ak máte iný účet, ktorý k nej má prístup, môžete sa <a href="%s">prihlásiť</a>.'
LOSTPASSWORDHEADER: 'Stratené heslo'
BUTTONSEND: 'Pošlite mi odkaz na resetovanie hesla'
CHANGEPASSWORDBELOW: 'Svoje heslo si môžete zmeniť nižšie.'
CHANGEPASSWORDHEADER: 'Zmeniť heslo'
@ -487,6 +509,7 @@ sk:
EDITPERMISSIONS: 'Spravovať právomoci pre skupiny'
EDITPERMISSIONS_HELP: 'Schopnosť upravovať právomoci a IP adresi pre skupinu. Vyžaduje prístup do sekcie "Bezpečnosť".'
GROUPNAME: 'Názov skupiny'
IMPORTGROUPS: 'Importovať skupiny'
IMPORTUSERS: 'Importovať požívateľov'
MEMBERS: Členovia
MENUTITLE: Bezpečnosť
@ -502,7 +525,9 @@ sk:
FileFieldLabel: 'CSV súbor <small>(Povoléné koncovki súborov: *.csv)</small>'
SilverStripeNavigator:
Edit: Editovať
Auto: Auto
ChangeViewMode: 'Zmeniť nód zobrazenia'
Desktop: Desktop
DualWindowView: 'Duálne okno'
EditView: 'Mód editácie'
Mobile: Mobilný telefón
@ -510,6 +535,7 @@ sk:
PreviewView: 'Mód náhľadu'
Responsive: Responzívny
SplitView: 'Mód rozdelenia'
Tablet: Tablet
ViewDeviceWidth: 'Vyberte šírku náhľadu'
Width: šírka
SimpleImageField:
@ -544,8 +570,12 @@ sk:
LESS: menej
MORE: viac
UploadField:
ATTACHFILE: 'Pripojiť súbor'
ATTACHFILES: 'Pripojiť súbory'
AttachFile: 'Pripojiť súbor(y)'
DELETE: 'Zmazať zo súborov'
DELETEINFO: 'Trvalo zmazať tento súbor z úložiska súborov'
DOEDIT: Uložiť
DROPFILE: 'pusť súbor'
DROPFILES: 'pusť súbory'
Dimensions: Rozmery
@ -554,6 +584,7 @@ sk:
FIELDNOTSET: 'Žiadna informácia o súbore'
FROMCOMPUTER: 'Z vášho počitača'
FROMCOMPUTERINFO: 'Vyberte zo súborov'
FROMFILES: 'Zo súborov'
HOTLINKINFO: 'Info: Tento obrázok bude "hotlinkovaný". Uistete sa prosím, že máte oprávnenie od pôvodneho tvorcu webu, aby sa tak stalo.'
MAXNUMBEROFFILES: 'Maximálny počet {count} súbor(ov) prekročený.'
MAXNUMBEROFFILESSHORT: 'Môžte nahrať iba {count} súborov'
@ -563,6 +594,7 @@ sk:
REMOVEINFO: 'Odstrániť tento súbor odtiaľ, ale nezmazať z úložiska súborov'
STARTALL: 'Začni všetko'
STARTALLINFO: 'Začni všetko nahrávať'
Saved: Uložené
CHOOSEANOTHERFILE: 'Vyberte iný súbor'
CHOOSEANOTHERINFO: 'Nahradiť tento súbor iným z úložiska súborov'
OVERWRITEWARNING: 'Súbor s rovnakým názvom už existuje'
@ -573,3 +605,11 @@ sk:
PREVIEW: 'Náhľad webu'
GridFieldEditButton_ss:
EDIT: Editovať
ContentController:
NOTLOGGEDIN: 'Neprihlásený'
GridFieldItemEditView:
Go_back: 'Choď späť'
PasswordValidator:
LOWCHARSTRENGTH: 'Prosím posilnite heslo pridaním z týchto niektorých znakov: %s'
PREVPASSWORD: 'Už ste použili toto heslo v minulosti, vyberte nové hoslo, prosím'
TOOSHORT: 'Heslo je príliš krátke, musí byť %s alebo viacej znakov dlhé'

View File

@ -141,6 +141,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/**
* @config
* @var boolean Should dataobjects be validated before they are written?
* Caution: Validation can contain safeguards against invalid/malicious data,
* and check permission levels (e.g. on {@link Group}). Therefore it is recommended
* to only disable validation for very specific use cases.
*/
private static $validation_enabled = true;

View File

@ -337,6 +337,31 @@ class Group extends DataObject {
public function setCode($val){
$this->setField("Code", Convert::raw2url($val));
}
public function validate() {
$result = parent::validate();
// Check if the new group hierarchy would add certain "privileged permissions",
// and require an admin to perform this change in case it does.
// This prevents "sub-admin" users with group editing permissions to increase their privileges.
if($this->Parent()->exists() && !Permission::check('ADMIN')) {
$inheritedCodes = Permission::get()
->filter('GroupID', $this->Parent()->collateAncestorIDs())
->column('Code');
$privilegedCodes = Config::inst()->get('Permission', 'privileged_permissions');
if(array_intersect($inheritedCodes, $privilegedCodes)) {
$result->error(sprintf(
_t(
'Group.HierarchyPermsError',
'Can\'t assign parent group "%s" with privileged permissions (requires ADMIN access)'
),
$this->Parent()->Title
));
}
}
return $result;
}
public function onBeforeWrite() {
parent::onBeforeWrite();

View File

@ -1169,7 +1169,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
$password->setCanBeEmpty(true);
if(!$this->ID) $password->showOnClick = false;
$mainFields->replaceField('Password', $password);
$mainFields->replaceField('Locale', new DropdownField(
"Locale",
_t('Member.INTERFACELANG', "Interface Language", 'Language of the CMS'),
@ -1231,7 +1231,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
$permissionsTab = $fields->fieldByName("Root")->fieldByName('Permissions');
if($permissionsTab) $permissionsTab->addExtraClass('readonly');
$defaultDateFormat = Zend_Locale_Format::getDateFormat($this->Locale);
$defaultDateFormat = Zend_Locale_Format::getDateFormat(new Zend_Locale($this->Locale));
$dateFormatMap = array(
'MMM d, yyyy' => Zend_Date::now()->toString('MMM d, yyyy'),
'yyyy/MM/dd' => Zend_Date::now()->toString('yyyy/MM/dd'),
@ -1249,7 +1249,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
);
$dateFormatField->setValue($this->DateFormat);
$defaultTimeFormat = Zend_Locale_Format::getTimeFormat($this->Locale);
$defaultTimeFormat = Zend_Locale_Format::getTimeFormat(new Zend_Locale($this->Locale));
$timeFormatMap = array(
'h:mm a' => Zend_Date::now()->toString('h:mm a'),
'H:mm' => Zend_Date::now()->toString('H:mm'),

View File

@ -86,6 +86,17 @@ class Permission extends DataObject implements TemplateGlobalProvider {
*/
private static $hidden_permissions = array();
/**
* @config These permissions can only be applied by ADMIN users, to prevent
* privilege escalation on group assignments and inheritance.
* @var array
*/
private static $privileged_permissions = array(
'ADMIN',
'APPLY_ROLES',
'EDIT_PERMISSIONS'
);
/**
* Check that the current member has the given permission.
*

View File

@ -162,6 +162,8 @@ class PermissionCheckboxSetField extends FormField {
$options = '';
$globalHidden = (array)Config::inst()->get('Permission', 'hidden_permissions');
if($this->source) {
$privilegedPermissions = Permission::config()->privileged_permissions;
// loop through all available categorized permissions and see if they're assigned for the given groups
foreach($this->source as $categoryName => $permissions) {
$options .= "<li><h5>$categoryName</h5></li>";
@ -194,6 +196,11 @@ class PermissionCheckboxSetField extends FormField {
$inheritMessage = ' (' . join(', ', $uninheritedCodes[$code]).')';
}
// Disallow modification of "privileged" permissions unless currently logged-in user is an admin
if(!Permission::check('ADMIN') && in_array($code, $privilegedPermissions)) {
$disabled = ' disabled="true"';
}
// If the field is readonly, always mark as "disabled"
if($this->readonly) $disabled = ' disabled="true"';
@ -246,6 +253,16 @@ class PermissionCheckboxSetField extends FormField {
$fieldname = $this->name;
$managedClass = $this->managedClass;
// Remove all "privileged" permissions if the currently logged-in user is not an admin
$privilegedPermissions = Permission::config()->privileged_permissions;
if(!Permission::check('ADMIN')) {
foreach($this->value as $id => $bool) {
if(in_array($id, $privilegedPermissions)) {
unset($this->value[$id]);
}
}
}
// remove all permissions and re-add them afterwards
$permissions = $record->$fieldname();
foreach ( $permissions as $permission ) {

View File

@ -76,4 +76,20 @@ class PermissionRole extends DataObject {
return $labels;
}
public function canView($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
public function canCreate($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
public function canEdit($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
public function canDelete($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
}

View File

@ -13,4 +13,38 @@ class PermissionRoleCode extends DataObject {
private static $has_one = array(
"Role" => "PermissionRole",
);
protected function validate() {
$result = parent::validate();
// Check that new code doesn't increase privileges, unless an admin is editing.
$privilegedCodes = Config::inst()->get('Permission', 'privileged_permissions');
if(
$this->Code
&& in_array($this->Code, $privilegedCodes)
&& !Permission::check('ADMIN')
) {
$result->error(sprintf(
_t(
'PermissionRoleCode.PermsError',
'Can\'t assign code "%s" with privileged permissions (requires ADMIN access)'
),
$this->Code
));
}
return $result;
}
public function canCreate($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
public function canEdit($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
public function canDelete($member = null) {
return Permission::check('APPLY_ROLES', 'any', $member);
}
}

View File

@ -97,9 +97,9 @@ class i18nTest extends SapphireTest {
public function testGetExistingTranslations() {
$translations = i18n::get_existing_translations();
$this->assertTrue(isset($translations['en']), 'Checking for en translation');
$this->assertEquals($translations['en'], 'English (United States)');
$this->assertTrue(isset($translations['de']), 'Checking for de_DE translation');
$this->assertTrue(isset($translations['en_US']), 'Checking for en translation');
$this->assertEquals($translations['en_US'], 'English (United States)');
$this->assertTrue(isset($translations['de_DE']), 'Checking for de_DE translation');
}
public function testDataObjectFieldLabels() {

View File

@ -109,6 +109,49 @@ class GroupTest extends FunctionalTest {
'Grandchild groups are removed');
}
public function testValidatesPrivilegeLevelOfParent() {
$nonAdminUser = $this->objFromFixture('GroupTest_Member', 'childgroupuser');
$adminUser = $this->objFromFixture('GroupTest_Member', 'admin');
$nonAdminGroup = $this->objFromFixture('Group', 'childgroup');
$adminGroup = $this->objFromFixture('Group', 'admingroup');
$nonAdminValidateMethod = new ReflectionMethod($nonAdminGroup, 'validate');
$nonAdminValidateMethod->setAccessible(true);
// Making admin group parent of a non-admin group, effectively expanding is privileges
$nonAdminGroup->ParentID = $adminGroup->ID;
$this->logInWithPermission('APPLY_ROLES');
$result = $nonAdminValidateMethod->invoke($nonAdminGroup);
$this->assertFalse(
$result->valid(),
'Members with only APPLY_ROLES can\'t assign parent groups with direct ADMIN permissions'
);
$this->logInWithPermission('ADMIN');
$result = $nonAdminValidateMethod->invoke($nonAdminGroup);
$this->assertTrue(
$result->valid(),
'Members with ADMIN can assign parent groups with direct ADMIN permissions'
);
$nonAdminGroup->write();
$newlyAdminGroup = $nonAdminGroup;
$this->logInWithPermission('ADMIN');
$inheritedAdminGroup = $this->objFromFixture('Group', 'group1');
$inheritedAdminMethod = new ReflectionMethod($inheritedAdminGroup, 'validate');
$inheritedAdminMethod->setAccessible(true);
$inheritedAdminGroup->ParentID = $adminGroup->ID;
$inheritedAdminGroup->write(); // only works with ADMIN login
$this->logInWithPermission('APPLY_ROLES');
$result = $inheritedAdminMethod->invoke($nonAdminGroup);
$this->assertFalse(
$result->valid(),
'Members with only APPLY_ROLES can\'t assign parent groups with inherited ADMIN permission'
);
}
}
class GroupTest_Member extends Member implements TestOnly {

View File

@ -16,4 +16,35 @@ class PermissionRoleTest extends FunctionalTest {
$this->assertEquals(0, DataObject::get('PermissionRoleCode',"\"RoleID\"={$role->ID}")->count(),
'Permissions removed along with the role');
}
public function testValidatesPrivilegedPermissions() {
$nonAdminCode = new PermissionRoleCode(array('Code' => 'CMS_ACCESS_CMSMain'));
$nonAdminValidateMethod = new ReflectionMethod($nonAdminCode, 'validate');
$nonAdminValidateMethod->setAccessible(true);
$adminCode = new PermissionRoleCode(array('Code' => 'ADMIN'));
$adminValidateMethod = new ReflectionMethod($adminCode, 'validate');
$adminValidateMethod->setAccessible(true);
$this->logInWithPermission('APPLY_ROLES');
$result = $nonAdminValidateMethod->invoke($nonAdminCode);
$this->assertTrue(
$result->valid(),
'Members with only APPLY_ROLES can create non-privileged permission role codes'
);
$this->logInWithPermission('APPLY_ROLES');
$result = $adminValidateMethod->invoke($adminCode);
$this->assertFalse(
$result->valid(),
'Members with only APPLY_ROLES can\'t create privileged permission role codes'
);
$this->logInWithPermission('ADMIN');
$result = $adminValidateMethod->invoke($adminCode);
$this->assertTrue(
$result->valid(),
'Members with ADMIN can create privileged permission role codes'
);
}
}