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

Conflicts:
	.scrutinizer.yml
	admin/javascript/LeftAndMain.Panel.js
	core/startup/ParameterConfirmationToken.php
	dev/Debug.php
	dev/FixtureBlueprint.php
	docs/en/00_Getting_Started/05_Coding_Conventions.md
	docs/en/00_Getting_Started/index.md
	docs/en/02_Developer_Guides/01_Templates/01_Syntax.md
	filesystem/File.php
	filesystem/Folder.php
	forms/FieldList.php
	forms/LabelField.php
	forms/MoneyField.php
	forms/TextField.php
	forms/TreeDropdownField.php
	forms/Validator.php
	forms/gridfield/GridField.php
	forms/gridfield/GridFieldExportButton.php
	lang/de.yml
	lang/fi.yml
	model/DataObject.php
	model/SQLQuery.php
	parsers/ShortcodeParser.php
	security/ChangePasswordForm.php
	security/Security.php
	tests/control/DirectorTest.php
	tests/core/startup/ParameterConfirmationTokenTest.php
	tests/dev/FixtureBlueprintTest.php
	tests/forms/FieldListTest.php
	tests/forms/MoneyFieldTest.php
	tests/model/SQLQueryTest.php
	tests/security/SecurityTest.php
This commit is contained in:
Damian Mooyman 2015-06-02 19:13:38 +12:00
commit 8331171f2c
104 changed files with 3738 additions and 1105 deletions

View File

@ -1,15 +1,7 @@
tools:
php_pdepend:
enabled: true
excluded_dirs:
- vendor
- thirdparty
- tests
- parsers/HTML/BBCodeParser
- docs
custom_commands:
-
scope: file
command: php tests/phpcs_runner.php %pathname%
filter:
excluded_paths: ["*/css/*", "css/*", "thirdparty/*", "*/jquery-changetracker/*", "parsers/HTML/BBCodeParser/*", "*/SSTemplateParser.php$", "docs/*", "*/images/*"]
inherit: true
filter:
excluded_paths:
- thirdparty/*
- parsers/*
- docs/*
- images/*

View File

@ -1,4 +1,4 @@
language: php
language: php
php:
- 5.4
@ -13,11 +13,14 @@ env:
matrix:
- DB=MYSQL
- DB=SQLITE
- DB=PGSQL
- DB=PGSQL
matrix:
allow_failures:
- php: hhvm-nightly
- php: hhvm
- php: 7.0
- php: nightly
include:
- php: 5.4
env: DB=MYSQL PDO=1
@ -29,7 +32,11 @@ matrix:
env: DB=MYSQL BEHAT_TEST=1
- php: 5.3
env: DB=MYSQL
- php: hhvm-nightly
- php: 7.0
env: DB=MYSQL
- php: nightly
env: DB=MYSQL
- php: hhvm
env: DB=MYSQL
before_install:
- sudo apt-get update -qq
@ -45,7 +52,7 @@ before_script:
- php ~/travis-support/travis_setup_selenium.php --if-env BEHAT_TEST
- php ~/travis-support/travis_setup_php54_webserver.php --if-env BEHAT_TEST
script:
script:
- "if [ \"$BEHAT_TEST\" = \"\" ]; then vendor/bin/phpunit framework/tests; fi"
- "if [ \"$BEHAT_TEST\" = \"1\" ]; then vendor/bin/behat @framework; fi"

View File

@ -8,6 +8,12 @@ Oembed:
'https://*.youtube.com/watch*':
http: 'http://www.youtube.com/oembed/',
https: 'https://www.youtube.com/oembed/?scheme=https'
'http://*.youtu.be/*':
http: 'http://www.youtube.com/oembed/',
https: 'https://www.youtube.com/oembed/?scheme=https'
'https://youtu.be/*':
http: 'http://www.youtube.com/oembed/',
https: 'https://www.youtube.com/oembed/?scheme=https'
'http://*.flickr.com/*':
'http://www.flickr.com/services/oembed/'
'http://*.viddler.com/*':

View File

@ -120,7 +120,7 @@ abstract class ModelAdmin extends LeftAndMain {
public function getEditForm($id = null, $fields = null) {
$list = $this->getList();
$exportButton = new GridFieldExportButton('before');
$exportButton = new GridFieldExportButton('buttons-before-left');
$exportButton->setExportColumns($this->getExportFields());
$listField = GridField::create(
$this->sanitiseClassName($this->modelClass),
@ -129,7 +129,7 @@ abstract class ModelAdmin extends LeftAndMain {
$fieldConfig = GridFieldConfig_RecordEditor::create($this->stat('page_length'))
->addComponent($exportButton)
->removeComponentsByType('GridFieldFilterHeader')
->addComponents(new GridFieldPrintButton('before'))
->addComponents(new GridFieldPrintButton('buttons-before-left'))
);
// Validation

View File

@ -141,6 +141,7 @@ body, html { font-size: 12px; line-height: 16px; font-family: Arial, sans-serif;
.ui-accordion .ui-accordion-content { border: 1px solid #c0c0c2; border-top: none; }
.ui-autocomplete { max-height: 240px; overflow-x: hidden; overflow-y: auto; }
.ui-autocomplete-loading { background-image: url(../images/throbber.gif) !important; background-position: 97% center !important; background-repeat: no-repeat !important; background-size: auto !important; }
/** This file defines common styles for form elements used throughout the CMS interface. It is an addition to the base styles defined in framework/css/Form.css. @package framework @subpackage admin */
/** ---------------------------------------------------- Basic form fields ---------------------------------------------------- */
@ -455,7 +456,7 @@ body.cms { overflow: hidden; }
.ss-loading-screen .loading-animation { display: none; position: absolute; left: 50%; margin-left: -21.5px; top: 80%; }
/** -------------------------------------------- Actions -------------------------------------------- */
.cms-content-actions, .cms-preview-controls { margin: 0; padding: 12px 12px; z-index: 0; border-top: 1px solid #cacacc; -moz-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; -webkit-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; height: 28px; background-color: #ECEFF1; }
.cms-content-actions, .cms-preview-controls { margin: 0; padding: 12px 12px; z-index: 999; border-top: 1px solid #cacacc; -moz-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; -webkit-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; height: 28px; background-color: #ECEFF1; }
/** -------------------------------------------- Messages -------------------------------------------- */
.message { display: block; clear: both; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #ccc solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -moz-border-radius: 3px 3px 3px 3px; -webkit-border-radius: 3px; border-radius: 3px 3px 3px 3px; }

View File

@ -46,9 +46,6 @@
// when JSTree auto-selects elements on first load.
if(!origEvent) {
return false;
}else if($(origEvent.target).hasClass('jstree-icon') || $(origEvent.target).hasClass('jstree-pageicon')){
// in case the click is not on the node title, ie on pageicon or dragicon,
return false;
}
// Don't allow checking disabled nodes

View File

@ -670,8 +670,11 @@ jQuery.noConflict();
// Support a full reload
if(xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) {
document.location.href = $('base').attr('href').replace(/\/*$/, '')
+ '/' + xhr.getResponseHeader('X-ControllerURL');
var baseUrl = $('base').attr('href'),
rawURL = xhr.getResponseHeader('X-ControllerURL'),
url = $.path.isAbsoluteUrl(rawURL) ? rawURL : $.path.makeUrlAbsolute(rawURL, baseUrl);
document.location.href = url;
return;
}

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/id.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('id', {
"LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",
"ModelAdmin.SAVED": "Tersimpan",
"ModelAdmin.REALLYDELETE": "Anda yakin ingin menghapus?",
"ModelAdmin.DELETED": "Terhapus",
"ModelAdmin.VALIDATIONERROR": "Kesalahan Validasi",
"LeftAndMain.PAGEWASDELETED": "Laman sudah terhapus. Untuk mengedit, pilih pada sisi kiri."
});
}

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/id_ID.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('id_ID', {
"LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",
"ModelAdmin.SAVED": "Tersimpan",
"ModelAdmin.REALLYDELETE": "Anda yakin ingin menghapus?",
"ModelAdmin.DELETED": "Terhapus",
"ModelAdmin.VALIDATIONERROR": "Kesalahan Validasi",
"LeftAndMain.PAGEWASDELETED": "Laman sudah terhapus. Untuk mengedit, pilih pada sisi kiri."
});
}

View File

@ -0,0 +1,10 @@
{
"LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",
"ModelAdmin.SAVED": "Tersimpan",
"ModelAdmin.REALLYDELETE": "Anda yakin ingin menghapus?",
"ModelAdmin.DELETED": "Terhapus",
"ModelAdmin.VALIDATIONERROR": "Kesalahan Validasi",
"LeftAndMain.PAGEWASDELETED": "Laman sudah terhapus. Untuk mengedit, pilih pada sisi kiri."
}

View File

@ -0,0 +1,10 @@
{
"LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",
"ModelAdmin.SAVED": "Tersimpan",
"ModelAdmin.REALLYDELETE": "Anda yakin ingin menghapus?",
"ModelAdmin.DELETED": "Terhapus",
"ModelAdmin.VALIDATIONERROR": "Kesalahan Validasi",
"LeftAndMain.PAGEWASDELETED": "Laman sudah terhapus. Untuk mengedit, pilih pada sisi kiri."
}

View File

@ -6,5 +6,5 @@
"ModelAdmin.REALLYDELETE": "Vill du verkligen radera?",
"ModelAdmin.DELETED": "Raderad",
"ModelAdmin.VALIDATIONERROR": "Valideringsfel",
"LeftAndMain.PAGEWASDELETED": "Sidan raderades. För att redigera en sida, välj den från menyn till vänster."
"LeftAndMain.PAGEWASDELETED": "Sidan raderades. För att redigera en sida, välj den i menyn till vänster."
}

View File

@ -11,6 +11,6 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
"ModelAdmin.REALLYDELETE": "Vill du verkligen radera?",
"ModelAdmin.DELETED": "Raderad",
"ModelAdmin.VALIDATIONERROR": "Valideringsfel",
"LeftAndMain.PAGEWASDELETED": "Sidan raderades. För att redigera en sida, välj den från menyn till vänster."
"LeftAndMain.PAGEWASDELETED": "Sidan raderades. För att redigera en sida, välj den i menyn till vänster."
});
}

View File

@ -429,7 +429,7 @@ body.cms {
.cms-content-actions, .cms-preview-controls {
margin: 0;
padding: $grid-y*1.5 $grid-y*1.5;
z-index: 0;
z-index: 999;
border-top: 1px solid lighten($color-separator, 4%);
@include box-shadow(
1px 0 0 $tab-panel-texture-color,

View File

@ -85,8 +85,16 @@
}
}
.ui-autocomplete{
.ui-autocomplete {
max-height: 240px;
overflow-x: hidden;
overflow-y: auto;
/** sorry about the !important but the specificity of other selectors mandates it over writing out very specific selectors **/
&-loading {
background-image: url(../images/throbber.gif) !important;
background-position: 97% center !important;
background-repeat: no-repeat !important;
background-size: auto !important;
}
}

View File

@ -501,6 +501,7 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
// absolute redirection URLs not located on this site may cause phishing
if(Director::is_site_url($url)) {
$url = Director::absoluteURL($url, true);
return $this->redirect($url);
} else {
return false;

View File

@ -510,14 +510,16 @@ class Director implements TemplateGlobalProvider {
if ($protocol = Config::inst()->get('Director', 'alternate_protocol')) {
$return = ($protocol == 'https');
} else if(
isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
) {
// Convention for (non-standard) proxy signaling a HTTPS forward,
// see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
$return = true;
} else if(
isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https'
) {
// Less conventional proxy header

View File

@ -655,10 +655,10 @@ class SS_HTTPRequest implements ArrayAccess {
* @return string
*/
public function getIP() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
if (TRUSTED_PROXY && !empty($_SERVER['HTTP_CLIENT_IP'])) {
//check ip from share internet
return $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
} elseif (TRUSTED_PROXY && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
//to check ip is pass from proxy
return $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif(isset($_SERVER['REMOTE_ADDR'])) {

View File

@ -286,7 +286,7 @@ EOT
* @return bool
*/
public function isFinished() {
return in_array($this->statusCode, array(301, 302, 401, 403));
return in_array($this->statusCode, array(301, 302, 303, 304, 305, 307, 401, 403));
}
}

View File

@ -23,6 +23,8 @@
* - FRAMEWORK_ADMIN_PATH: Absolute filepath, e.g. "/var/www/my-webroot/framework/admin"
* - THIRDPARTY_DIR: Path relative to webroot, e.g. "framework/thirdparty"
* - THIRDPARTY_PATH: Absolute filepath, e.g. "/var/www/my-webroot/framework/thirdparty"
* - TRUSTED_PROXY: true or false, depending on whether the X-Forwarded-* HTTP
* headers from the given client are trustworthy (e.g. from a reverse proxy).
*
* @package framework
* @subpackage core
@ -85,6 +87,35 @@ function stripslashes_recursively(&$array) {
}
}
/**
* Validate whether the request comes directly from a trusted server or not
* This is necessary to validate whether or not the values of X-Forwarded-
* or Client-IP HTTP headers can be trusted
*/
if(!defined('TRUSTED_PROXY')) {
$trusted = true; // will be false by default in a future release
if(getenv('BlockUntrustedProxyHeaders') // Legacy setting (reverted from documentation)
|| getenv('BlockUntrustedIPs') // Documented setting
|| defined('SS_TRUSTED_PROXY_IPS')
) {
$trusted = false;
if(defined('SS_TRUSTED_PROXY_IPS') && SS_TRUSTED_PROXY_IPS !== 'none') {
if(SS_TRUSTED_PROXY_IPS === '*') {
$trusted = true;
} elseif(isset($_SERVER['REMOTE_ADDR'])) {
$trusted = in_array($_SERVER['REMOTE_ADDR'], explode(',', SS_TRUSTED_PROXY_IPS));
}
}
}
/**
* Declare whether or not the connecting server is a trusted proxy
*/
define('TRUSTED_PROXY', $trusted);
}
/**
* A blank HTTP_HOST value is used to detect command-line execution.
* We update the $_SERVER variable to contain data consistent with the rest of the application.
@ -147,12 +178,21 @@ if(!isset($_SERVER['HTTP_HOST'])) {
/**
* Fix HTTP_HOST from reverse proxies
*/
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
if (TRUSTED_PROXY && isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
// Get the first host, in case there's multiple separated through commas
$_SERVER['HTTP_HOST'] = strtok($_SERVER['HTTP_X_FORWARDED_HOST'], ',');
}
}
if (defined('SS_ALLOWED_HOSTS')) {
$all_allowed_hosts = explode(',', SS_ALLOWED_HOSTS);
if (!in_array($_SERVER['HTTP_HOST'], $all_allowed_hosts)) {
header('HTTP/1.1 400 Invalid Host', true, 400);
die();
}
}
/**
* Define system paths
*/

View File

@ -11,14 +11,37 @@
* It will likely be heavily refactored before the release of 3.2
*/
class ParameterConfirmationToken {
/**
* The name of the parameter
*
* @var string
*/
protected $parameterName = null;
/**
* The parameter given
*
* @var string|null The string value, or null if not provided
*/
protected $parameter = 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_FOLDER.'/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)
require_once(dirname(dirname(dirname(__FILE__))).'/security/RandomGenerator.php');
@ -31,7 +54,17 @@ class ParameterConfirmationToken {
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;
@ -43,16 +76,23 @@ class ParameterConfirmationToken {
return $content == $token;
}
/**
* Create a new ParameterConfirmationToken
*
* @param string $parameterName Name of the querystring parameter to check
*/
public function __construct($parameterName) {
// Store the parameter name
$this->parameterName = $parameterName;
// Store the parameter value
$this->parameter = isset($_GET[$parameterName]) ? $_GET[$parameterName] : null;
// Store the token
$this->token = isset($_GET[$parameterName.'token']) ? $_GET[$parameterName.'token'] : null;
// If a token was provided, but isn't valid, ignore it
if ($this->token && (!$this->checkToken($this->token))) $this->token = null;
// If the token provided is valid, mark it as such
$token = isset($_GET[$parameterName.'token']) ? $_GET[$parameterName.'token'] : null;
if ($this->checkToken($token)) {
$this->token = $token;
}
}
/**
@ -66,7 +106,8 @@ class ParameterConfirmationToken {
/**
* Is the parameter requested?
*
* ?parameter and ?parameter=1 are both considered requested
*
* @return bool
*/
public function parameterProvided() {
@ -75,11 +116,12 @@ class ParameterConfirmationToken {
/**
* Is the necessary token provided for this parameter?
*
* A value must be provided for the token
*
* @return bool
*/
public function tokenProvided() {
return $this->token !== null;
return !empty($this->token);
}
/**
@ -98,6 +140,11 @@ class ParameterConfirmationToken {
unset($_GET[$this->parameterName]);
}
/**
* Determine the querystring parameters to include
*
* @return array List of querystring parameters with name and token parameters
*/
public function params() {
return array(
$this->parameterName => $this->parameter,
@ -114,14 +161,16 @@ class ParameterConfirmationToken {
// Are we http or https? Replicates Director::is_https() without its dependencies/
$proto = 'http';
if(
isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
) {
// Convention for (non-standard) proxy signaling a HTTPS forward,
// see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
$proto = 'https';
} else if(
isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
TRUSTED_PROXY
&& isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https'
) {
// Less conventional proxy header
@ -154,6 +203,10 @@ class ParameterConfirmationToken {
return "$proto://" . preg_replace('#/{2,}#', '/', implode('/', $parts));
}
/**
* Forces a reload of the request with the token included
* This method will terminate the script with `die`
*/
public function reloadWithToken() {
$location = $this->currentAbsoluteURL();
@ -179,7 +232,7 @@ You are being redirected. If you are not redirected soon, <a href='$location'>cl
* 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 type $keys List of token keys in ascending priority (low to high)
* @param array $keys List of token keys in ascending priority (low to high)
* @return ParameterConfirmationToken The token container for the unvalidated $key given with the highest priority
*/
public static function prepare_tokens($keys) {

View File

@ -68,11 +68,14 @@ Used in side panels and action tabs
.cms table.ss-gridfield-table tr.sortable-header { background: #dbe3e8; }
.cms table.ss-gridfield-table tr.sortable-header th { padding: 0; font-weight: normal; }
.cms table.ss-gridfield-table tr.sortable-header th .ss-ui-button { font-weight: normal; }
.cms table.ss-gridfield-table tr:hover { background: #FFFAD6 !important; }
.cms table.ss-gridfield-table tr:hover { background: #FFFAD6; }
.cms table.ss-gridfield-table tr:first-child { background: transparent; }
.cms table.ss-gridfield-table tr:first-child:hover { background: #FFFAD6; }
.cms table.ss-gridfield-table tr.ss-gridfield-even { background: #F0F4F7; }
.cms table.ss-gridfield-table tr.ss-gridfield-even.ss-gridfield-last { border-bottom: none; }
.cms table.ss-gridfield-table tr.ss-gridfield-even:hover { background: #FFFAD6; }
.cms table.ss-gridfield-table tr.even { background: #F0F4F7; }
.cms table.ss-gridfield-table tr.even:hover { background: #FFFAD6; }
.cms table.ss-gridfield-table tr th { font-weight: bold; font-size: 12px; color: #FFF; padding: 5px; border-right: 1px solid rgba(0, 0, 0, 0.1); }
.cms table.ss-gridfield-table tr th div.fieldgroup, .cms table.ss-gridfield-table tr th div.fieldgroup-field { width: 100%; position: relative; }
.cms table.ss-gridfield-table tr th div.fieldgroup { min-width: 200px; padding-right: 0; }

View File

@ -190,13 +190,7 @@ class FixtureBlueprint {
// If LastEdited was set in the fixture, set it here
if($data && array_key_exists('LastEdited', $data)) {
$edited = $this->parseValue($data['LastEdited'], $fixtures);
$update = new SQLUpdate(
$class,
array('"LastEdited"' => $edited),
array('"ID"' => $obj->id)
);
$update->execute();
$this->overrideField($obj, 'LastEdited', $data['LastEdited'], $fixtures);
}
} catch(Exception $e) {
Config::inst()->update('DataObject', 'validation_enabled', $validationenabled);
@ -297,4 +291,17 @@ class FixtureBlueprint {
$obj->$name = $this->parseValue($value, $fixtures);
}
}
protected function overrideField($obj, $fieldName, $value, $fixtures = null) {
$table = ClassInfo::table_for_object_field(get_class($obj), $fieldName);
$value = $this->parseValue($value, $fixtures);
DB::manipulate(array(
$table => array(
"command" => "update", "id" => $obj->ID,
"fields" => array($fieldName => is_string($value) ? "'$value'" : $value)
)
));
$obj->$fieldName = $value;
}
}

View File

@ -1,75 +1,49 @@
# Mac OSX
# Mac OSX with MAMP
This topic covers setting up your Mac as a Web Server and installing SilverStripe.
This topic covers setting up your Mac as a web server and installing SilverStripe.
While OSX Comes bundled with PHP and Apache (Thanks Apple!) Its not quite ideal for SilverStripe so for setting up a
webserver on OSX we suggest using [MAMP](http://www.mamp.info/en/index.php) or using [MacPorts](http://www.macports.org/)
to manage your packages.
OSX comes bundled with PHP and Apache, but you're stuck with the versions it ships with.
It is also a bit harder to install additional PHP modules required by SilverStripe.
[MAMP](http://www.mamp.info/en/) is a simple way to get a complete webserver
environment going on your OSX machine, without removing or altering any system-level configuration.
If you want to use the default OSX PHP version then you will need to recompile your own versions of PHP with GD. Providing instructions
for how to recompile PHP is beyond the scope of our documentation but try an online search.
Check out the [MAC OSX with Homebrew](other_installation_options/Mac_OSX_Homebrew)
for an alternative, more configurable installation process.
## Installing MAMP
## Requirements
If you have decided to install using MacPorts you can skip this section.
Please check the [system requirements](http://www.mamp.info/en/documentation/) for MAMP,
you'll need a fairly new version of OSX to run it.
Once you have downloaded and Installed MAMP start the Application and Make sure everything is running by clicking the
MAMP icon. Under `Preferences -> PHP` make sure Version 5 is Selected.
## MAMP Installation
Open up `/Applications/MAMP/conf/PHP5/php.ini` and make the following configuration changes:
* [Download MAMP](http://www.mamp.info/en/)
* Install and start MAMP
* Check out your new web server environment on `http://localhost:8888`
memory_limit = 64M
## SilverStripe Installation
Once you make that change open the MAMP App Again by clicking on the MAMP Icon and click Stop Servers then Start
Servers - this is so our changes to the php.ini take effect.
[Composer](http://getcomposer.org) is a dependancy manager for PHP, and the preferred way to
install SilverStripe. It ensures that you get the correct set of files for your project.
Composer uses your MAMP PHP executable to run and also requires [git](http://git-scm.com)
to automatically download the required files from GitHub and other repositories.
## Installing SilverStripe
In order to install Composer, we need to let the system know where to find the PHP executable.
Open or create the `~/.bash_profile` file in your home folder, then add the following line:
`export PATH=/Applications/MAMP/bin/php/php5.5.22/bin:$PATH`
You'll need to adjust the PHP version number (`php5.5.22`). The currently running PHP version is shown on `http://localhost:8888/MAMP/index.php?page=phpinfo`.
Run `source ~/.bash_profile` for the changes to take effect. You can verify that the correct executable
is used by running `which php`. It should show the path to MAMP from above.
### Composer
[Composer (a dependancy manager for PHP)](http://getcomposer.org) is the preferred way to install SilverStripe and ensure you get the correct set of files for your project.
Now you're ready to install Composer: Run `curl -sS https://getcomposer.org/installer | php`.
We recommend that you make the `composer` executable available globally,
which requires moving the file to a different folder. Run `mv composer.phar /usr/local/bin/composer`.
More detailed installation instructions are available on [getcomposer.org](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx).
You can verify the installation by typing the `composer` command, which should show you a command overview.
Composer uses your MAMP PHP executable to run and also requires [git](http://git-scm.com) (so it can automatically download the required files from GitHub).
Finally, we're ready to install SilverStripe through composer:
`composer create-project silverstripe/installer /Applications/MAMP/htdocs/silverstripe/`.
After finishing, the installation wizard should be available at `http://localhost:8888/silverstripe`.
The MAMP default database credentials are user `root` and password `root`.
#### Install composer using MAMP
1. First create an alias for our bash profile, using your preferred terminal text editor (nano, vim, etc) open `~/.bash_profile`.
2. Add the following line (adjusting the version number of PHP to your installation of MAMP): `alias phpmamp='/Applications/MAMP/bin/php/php5.4.10/bin/php'`.
3. The run `. ~/.bash_profile` to reload the bash profile and make it accessible.
4. This will create an alias, `phpmamp`, allowing you to use the MAMP installation of PHP. Please take note of the PHP version, in this case 5.4.10, as with different versions of MAMP this may be different. Check your installation and see what version you have, and replace the number accordingly (this was written with MAMP version 2.1.2).
5. With that setup, we are ready to install `composer`. This is a two step process if we would like this to be installed globally (only do the first step if you would like `composer` installed to the local working directory only).
- First, run the following command in the terminal: `curl -sS https://getcomposer.org/installer | phpmamp`
We are using `phpmamp` so that we correctly use the MAMP installation of PHP from above.
- Second, if you want to make composer available globally, we need to move the file to '/usr/local/bin/composer'. To do this, run the following command:
`sudo mv composer.phar /usr/local/bin/composer`
Terminal will ask you for your root password, after entering it and pressing the 'return' (or enter) key, you'll have a working global installation of composer on your mac that uses MAMP.
6. You can verify your installation worked by typing the following command:
`composer`
It'll show you the current version and a list of commands you can use.
7. Run the following command to get a fresh copy of SilverStripe via composer:
`composer create-project silverstripe/installer /Applications/MAMP/htdocs/silverstripe/`
8. You can now [use composer](http://doc.silverstripe.org/framework/en/getting_started/composer/) to manage future SilverStripe updates and adding modules with a few easy commands.
### Package Download
[Download](http://silverstripe.org/software/download/) the latest SilverStripe installer package. Copy the tar.gz or zip file to the 'Document Root' for MAMP - By Default its `/Applications/MAMP/htdocs`.
Don't know what your Document Root is? Open MAMP Click `Preferences -> Apache`.
Extract the tar.gz file to a folder, e.g. `silverstripe/` (you always move the tar.gz file first and not the other way
around as SilverStripe uses a '.htaccess' file which is hidden from OSX so if you move SilverStripe the .htaccess file
won't come along.
### Run the installation wizard
Once you have a copy of the required code (by either of the above methods), open your web browser and go to `http://localhost:8888/silverstripe/`. Enter your database details - by default with MAMP its user `root` and password `root` and select your account details. Click "Check Details".
Once everything is sorted hit "Install!" and Voila, you have SilverStripe installed
We have a separate in-depth tutorial for [Composer Installation and Usage](composer).

View File

@ -0,0 +1,118 @@
# Mac OSX with Homebrew
This topic covers setting up your Mac as a web server and installing SilverStripe.
OSX comes bundled with PHP, but you're stuck with the version and modules it ships with.
If you run projects on different PHP versions, or care about additional PHP module support
and other dependencies such as MariaDB, we recommend an installation through [Homebrew](http://brew.sh/).
Check out the [MAC OSX with MAMP](../Mac_OSX) for an alternative installation process
which packages up the whole environment into a convenient application.
## Requirements
Since we're compiling PHP, some build tooling is required.
Run the following command to install Xcode Command Line Tools.
xcode-select --install
Now you can install Homebrew itself:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
## Install PHP
First we're telling Homebrew about some new repositories to get the PHP installation from:
brew tap homebrew/dupes
brew tap homebrew/php
We're installing PHP 5.5 here, with the required `mcrypt` module:
brew install php55 php55-mcrypt
There's a [Homebrew Troubleshooting](https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Troubleshooting.md) guide if Homebrew doesn't work out as expected (run `brew update` and `brew doctor`).
Have a look at the [brew-php-switcher](https://github.com/philcook/brew-php-switcher)
project to install multiple PHP versions in parallel and switch between them easily.
## Install the Database (MariaDB/MySQL)
brew install mariadb
unset TMPDIR
mysql_install_db --user=`whoami` --basedir="$(brew --prefix mariadb)" --datadir=/usr/local/var/mysql --tmpdir=/tmp
mysql.server start
'/usr/local/opt/mariadb/bin/mysql_secure_installation'
To start the database server on boot, run the following:
ln -sfv /usr/local/opt/mariadb/*.plist ~/Library/LaunchAgents
You can also use `mysql.server start` and `mysql.server stop` on demand.
## Configure PHP and Apache
We're not installing Apache, since OSX already ships with a perfectly fine installation of it.
Edit the existing configuration at `/etc/apache2/httpd.conf`,
and uncomment/add the following lines to activate the required modules:
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
LoadModule php5_module /usr/local/opt/php55/libexec/apache2/libphp5.so
Change the `DocumentRoot` setting to your user folder (replacing `<user>` with your OSX user name):
DocumentRoot "/Users/<user>/Sites"
Now find the section starting with `<Directory "/Library/WebServer/Documents">` and change it as follows,
again replacing `<user>` with your OSX user name:
<Directory "/Users/<user>/Sites">
Options FollowSymLinks Multiviews
MultiviewsMatch Any
AllowOverride All
Require all granted
</Directory>
We also recommend running the web server process with your own user on a development environment,
since it makes permissions easier to handle when running commands both
from the command line and through the web server. Find and adjust the following options,
replacing the `<user>` placeholder:
User <user>
Group staff
Now start the web server:
sudo apachectl start
Every configuration change requires a restart:
sudo apachectl restart
You can also load this webserver on boot:
sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
After starting the webserver, you should see a simple "Forbidden" page generated by Apache
when accessing `http://localhost`.
## SilverStripe Installation
[Composer](http://getcomposer.org) is a dependancy manager for PHP, and the preferred way to
install SilverStripe. It ensures that you get the correct set of files for your project.
Composer uses the PHP executable we've just installed. It also needs [git](http://git-scm.com)
to automatically download the required files from GitHub and other repositories.
Run `curl -sS https://getcomposer.org/installer | php` to install the `composer` executable.
We recommend that you make the executable available globally,
which requires moving the file to a different folder. Run `mv composer.phar /usr/local/bin/composer`.
More detailed installation instructions are available on [getcomposer.org](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx).
You can verify the installation by typing the `composer` command, which should show you a command overview.
Finally, we're ready to install SilverStripe through composer:
`composer create-project silverstripe/installer /Users/<user>/Sites/silverstripe/`.
After finishing, the installation wizard should be available at `http://localhost/silverstripe`.
The Homebrew MariaDB default database credentials are user `root` and password `root`.
We have a separate in-depth tutorial for [Composer Installation and Usage](composer).

View File

@ -1,152 +0,0 @@
# Install SilverStripe manually on Windows using IIS 6
<div class="warning" markdown="1">Note: These instructions may not work, as they're no longer maintained.</div>
How to prepare Windows Server 2003 for SilverStripe using IIS 6 and FastCGI.
This guide will work for the following operating systems:
* Windows Server 2003
* Windows Server 2003 R2
Database install and configuration is not covered here, it is assumed you will do this yourself.
PHP comes with MySQL support out of the box, but you will need to install the [SQL Server Driver for PHP](http://www.microsoft.com/downloads/en/details.aspx?displaylang=en&FamilyID=80e44913-24b4-4113-8807-caae6cf2ca05)
from Microsoft if you want to use SQL Server.
## Preparation
Open **Windows Update** and make sure everything is updated, including optional updates. It is important that all .NET Framework updates including service packs are installed.
## Install IIS
- Open **Control Panel** > **Add/Remove Programs**
- Click **Add/Remove Windows Components** on the left hand bar
- Check **Application Server** and then click **Next** to install it
## Install FastCGI for IIS
- Download and install this package: http://www.iis.net/download/fastcgi
- Open **inetmgr.exe**
- Right click **Web Sites** and go to **Properties**
- Click the **Home Directory** tab
- Click **Configuration...** then **Add**
- In the **Add/Edit Extension Mapping** dialog, click **Browse...** and navigate to fcgiext.dll which is located in %windir%\system32\inetsrv
- In the **Extension** text box, enter **.php**
- Under **Verbs** in the **Limit to** text box, enter **GET,HEAD,POST**
- Ensure that the **Script engine** and **Verify that file exists** boxes are checked then click **OK**
- Open fcgiext.ini located in %windir%\system32\inetsrv. In the [Types] section of the file, add **php=PHP**
- Create a new section called **[PHP]** at the bottom of the file, like this:
[PHP]
ExePath=c:\php5\php-cgi.exe
Finally, run these commands in **Command Prompt**
cd %windir%\system32\inetsrv
cscript fcgiconfig.js -set -section:"PHP" -InstanceMaxRequests:10000
cscript fcgiconfig.js -set -section:"PHP" -EnvironmentVars:PHP_FCGI_MAX_REQUESTS:10000
cscript fcgiconfig.js -set -section:"PHP" -ActivityTimeout:300
## Install PHP
- [Download PHP](http://windows.php.net/download) (**Zip** link underneath the **VC9 x86 Non Thread Safe** section)
- [Download WinCache](http://www.iis.net/download/WinCacheForPHP) (**WinCache 1.1 for PHP 5.3**)
- Extract the PHP zip contents to **c:\php5**
- Run the WinCache self-extractor and extract to **c:\php5\ext**. A file called **php_wincache.dll** should now reside in **c:\php5\ext**
- Rename **php.ini-development** to **php.ini** in **c:\php5**
- Open **php.ini**, located in **c:\php5** with **Notepad** or another editor like **Notepad++**
- Search for **date.timezone**, uncomment it by removing the semicolon and set a timezone from here: http://php.net/manual/en/timezones.php
- Search for **fastcgi.impersonate**, uncomment it by removing the semicolon and set it like this: **fastcgi.impersonate = 1**
- Search for **cgi.fix_pathinfo**, uncomment it by removing the semicolon and set it like this: **cgi.fix_pathinfo = 1**
- Search for **cgi.force_redirect**, uncomment it by removing the semicolon and set it like this: **cgi.force_redirect = 0**
- Search for **fastcgi.logging**, uncomment it by removing the semicolon and set it like this: **fastcgi.logging = 0**
- Search for **extension_dir** and make sure it looks like this: **extension_dir = "ext"** (use proper double quotation characters here)
- Find the "Dynamic Extensions" part of the file, and replace all extension entries with the following:
;extension=php_bz2.dll
extension=php_curl.dll
;extension=php_enchant.dll
;extension=php_exif.dll
;extension=php_fileinfo.dll
extension=php_gd2.dll
;extension=php_gettext.dll
;extension=php_gmp.dll
;extension=php_imap.dll
;extension=php_intl.dll
;extension=php_ldap.dll
extension=php_mbstring.dll
extension=php_mysql.dll
extension=php_mysqli.dll
;extension=php_oci8.dll
;extension=php_oci8_11g.dll
;extension=php_openssl.dll
;extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll
;extension=php_pdo_odbc.dll
;extension=php_pdo_pgsql.dll
;extension=php_pdo_sqlite.dll
;extension=php_pgsql.dll
;extension=php_shmop.dll
;extension=php_snmp.dll
;extension=php_soap.dll
;extension=php_sockets.dll
;extension=php_sqlite3.dll
;extension=php_sqlite.dll
extension=php_tidy.dll
extension=php_wincache.dll
;extension=php_xmlrpc.dll
;extension=php_xsl.dll
This is a minimal set of loaded extensions which will get you started.
If want to use **SQL Server** as a database, you will need to install the [SQL Server Driver for PHP](http://www.microsoft.com/downloads/en/details.aspx?displaylang=en&FamilyID=80e44913-24b4-4113-8807-caae6cf2ca05) and add an extension entry for it to the list above.
## Test PHP
- Open **Command Prompt** and type the following:
c:\php5\php.exe -v
You should see some output showing the PHP version. If you get something else, or nothing at all, then there are missing updates for your copy of Windows Server 2003. Open **Windows Update** and make sure you've updated everything including optional updates.
## Install SilverStripe
- [Download SilverStripe](http://silverstripe.org/downloads)
- Extract the download contents to **C:\Inetpub\wwwroot\silverstripe**
- Open **inetmgr.exe**
- Right click **Web Sites** and go to **New** > **Web Site**
- Fill in all appropriate details. If you enter **(All Unassigned)** for the IP address field, make sure the port is something other than **80**, as this will conflict with "Default Web Site" in IIS. When asked for path, enter **C:\Inetpub\wwwroot\silverstripe**
- Browse to **http://localhost:8888** or to the IP address you just assigned in your browser.
An installation screen should appear. There may be some permission problems, which you should be able to correct by assigning the **Users** group write permissions by right clicking files / folders in Windows Explorer and going to **Properties** then the **Security** tab.
When ready, hit **Install SilverStripe**.
SilverStripe should now be installed and you should have a basic site with three pages.
However, URLs will not look "nice", like this: http://localhost/index.php/about-us. In order to fix this problem, we need to install a third-party URL rewriting tool, as IIS 6 does not support this natively.
Proceed to **Install IIRF** below to enable nice URLs.
## Install IIRF
At the moment, all URLs will have index.php in them. This is because IIS does not support URL rewriting. To make this work, we need to install IIRF which is a third-party plugin for IIS.
- [Download IIRF](http://iirf.codeplex.com/releases/view/36814) and install it
- Create a new file called iirf.ini in C:\inetpub\wwwroot\silverstripe with this content
RewriteEngine On
MaxMatchCount 10
IterationLimit 5
# URLs with query strings
# Don't catch successful file references
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)\?(.+)$ /framework/main.php?url=$1&$2
# URLs without query strings
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /framework/main.php?url=$1
Friendly URLs should now be working when you browse to your site.
Remember that IIRF works on a per-virtual host basis. This means for each site you want IIRF to work for, you need to add a new entry to **Web Sites** in **inetmgr.exe**.
Thanks to **kcd** for the rules: [http://www.silverstripe.org/installing-silverstripe/show/10488#post294415](http://www.silverstripe.org/installing-silverstripe/show/10488#post294415)

View File

@ -8,12 +8,14 @@ able to run PHP files via the FastCGI-wrapper from Nginx.
Now you need to set up a virtual host in Nginx with configuration settings
that are similar to those shown below.
<div class="notice" markdown='1'>
If you don't fully understand the configuration presented here, consult the
[nginx documentation](http://nginx.org/en/docs/).
Especially be aware of [accidental php-execution](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/ "Don't trust the tutorials") when extending the configuration.
</div>
But enough of the disclaimer, on to the actual configuration — typically in `nginx.conf`:
server {
@ -21,6 +23,11 @@ But enough of the disclaimer, on to the actual configuration — typically in `n
root /path/to/ss/folder;
server_name site.com www.site.com;
# Defend against SS-2015-013 -- http://www.silverstripe.org/software/download/security-releases/ss-2015-013
if ($http_x_forwarded_host) {
return 400;
}
location / {
try_files $uri /framework/main.php?url=$uri&$query_string;

View File

@ -1,19 +1,16 @@
# Installation
These instructions show you how to install SilverStripe on any web server.
The best way to install from the source code is to use [Composer](../composer).
These instructions show you how to install SilverStripe on any web server.
Check out our operating system specific guides for [Linux](linux_unix),
[Windows Server](windows) and [Mac OSX](mac_osx).
## Installation Steps
* [Download](http://silverstripe.org/download) the installer package
* Make sure the webserver has MySQL and PHP support. See [Server Requirements](../server_requirements) for more information.
* Unpack the installer somewhere into your web-root. Usually the www folder or similar. Most downloads from SilverStripe
are compressed tarballs. To extract these files you can either do them natively (Unix) or with 7-Zip (Windows)
* Visit your sites domain or IP address in your web browser.
* You will be presented with a form where you enter your MySQL login details and are asked to give your site a 'project
name' and the default login details. Follow the questions and select the *install* button at the bottom of the page.
* Make sure the webserver has MySQL and PHP support (check our [server requirements](../server_requirements)).
* Either [download the installer package](http://silverstripe.org/download), or [install through Composer](../composer).
* If using with the installer download, extract it into your webroot.
* Visit your domain or IP address in your web browser.
* You will be presented with an installation wizard asking for database and login credentials.
* After a couple of minutes, your site will be set up. Visit your site and enjoy!
## Issues?

View File

@ -9,7 +9,9 @@ We also have separate instructions for [installing modules with Composer](/devel
## Installing composer
To install Composer, run the following commands from your command-line.
Before installing Composer you should ensure your system has the version control system, [Git installed](http://git-scm.com/book/en/v2/Getting-Started-Installing-Git). Composer uses Git to check out the code dependancies you need to run your SilverStripe CMS website from the code repositories maintained on GitHub.
Next, to install Composer, run the following commands from your command-line.
# Download composer.phar
curl -s https://getcomposer.org/installer | php
@ -77,7 +79,7 @@ You can find other packages with the following command:
composer search silverstripe
This will return a list of package names of the forum `vendor/package`. If you prefer, you can search for pacakges on [packagist.org](https://packagist.org/search/?q=silverstripe).
This will return a list of package names of the forum `vendor/package`. If you prefer, you can search for packages on [packagist.org](https://packagist.org/search/?q=silverstripe).
The second part after the colon, `*`, is a version string. `*` is a good default: it will give you the latest version that works with the other modules you have installed. Alternatively, you can specificy a specific version, or a constraint such as `>=3.0`. For more information, read the [Composer documentation](http://getcomposer.org/doc/01-basic-usage.md#the-require-key).

View File

@ -431,7 +431,7 @@ Put code into the classes in the following order (where applicable).
### SQL Format
If you have to use raw SQL, make sure your code works across databases make sure you escape your queries like below,
If you have to use raw SQL, make sure your code works across databases. Make sure you escape your queries like below,
with the column or table name escaped with double quotes as below.
:::php

View File

@ -37,8 +37,27 @@ For more flexibility, you can set up either of the following web servers, and us
Mac OS X comes with a built-in webserver, but there are a number of other options:
* [Install using MAMP](mac-osx)
* Install using the built-in webserver (no docs yet)
* Install using MacPorts (no docs yet)
* [Install using Homebrew](installation/other_installation_options/mac_osx_homebrew)
### Virtual Machines through Vagrant
[Vagrant](https://www.vagrantup.com/) creates portable development environments
which can be hosted on Linux, Windows and Mac OS X. The virtual machine
usually runs a flavour of Linux. As a self-contained pre-configured environment,
getting up an running with Vagrant tends to be easier than creating a complete
development environment from scratch on your own machine.
* [silverstripe-australia/vagrant-environment](https://github.com/silverstripe-australia/vagrant-environment)
* [BetterBrief/vagrant-skeleton](https://github.com/BetterBrief/vagrant-skeleton)
Note: These instructions are supported by the community.
## Virtual Machines through Bitnami
[Bitnami](https://bitnami.com) is an online service that makes it easy to get
apps running on cloud providers like Amazon Web Services as well as local
virtualised environments. Bitnami has a [SilverStripe Virtual Machine](https://bitnami.com/stack/silverstripe/virtual-machine)
ready for download or installation on a cloud platform.
## Troubleshooting

View File

@ -10,16 +10,16 @@ These tutorials are deprecated, and have been replaced by the new [Lessons](http
These include video screencasts, written tutorials and code examples to get you started working with SilverStripe websites.
* [How to set up a local development environment in SilverStripe](https://vimeo.com/108861537)
* [Lesson 1: Creating your first theme](http://www.silverstripe.org/learn/lessons/lesson-1-creating-your-first-theme/)
* [Lesson 2: Migrating static templates into your theme](http://www.silverstripe.org/learn/lessons/lesson-2-migrating-static-templates-into-your-theme/)
* [Lesson 3: Adding dynamic content](http://www.silverstripe.org/learn/lessons/lesson-3-adding-dynamic-content/)
* [Lesson 4: Working with multiple templates](http://www.silverstripe.org/learn/lessons/lesson-4-working-with-multiple-templates/)
* [Lesson 5: The holder/page pattern](http://www.silverstripe.org/learn/lessons/lesson-5-the-holderpage-pattern/)
* [Lesson 6: Adding Custom Fields to a Page](http://www.silverstripe.org/learn/lessons/lesson-6-adding-custom-fields-to-a-page/)
* [Lesson 7: Working with Files and Images](http://www.silverstripe.org/learn/lessons/lesson-7-working-with-files-and-images/)
* [Lesson 8: Introduction to the ORM](http://www.silverstripe.org/learn/lessons/lesson-8-introduction-to-the-orm)
* [Lesson 9: Data Relationships - $has_many](http://www.silverstripe.org/learn/lessons/lesson-9-working-with-data-relationships-has-many)
* [Lesson 10: Introduction to the ORM](http://www.silverstripe.org/learn/lessons/lesson-10-working-with-data-relationships-many-many)
* [Lesson 1: Creating your first theme](http://www.silverstripe.org/learn/lessons/creating-your-first-theme)
* [Lesson 2: Migrating static templates into your theme]http://www.silverstripe.org/learn/lessons/migrating-static-templates-into-your-theme)
* [Lesson 3: Adding dynamic content](http://www.silverstripe.org/learn/lessons/adding-dynamic-content)
* [Lesson 4: Working with multiple templates](http://www.silverstripe.org/learn/lessons/working-with-multiple-templates)
* [Lesson 5: The holder/page pattern](http://www.silverstripe.org/learn/lessons/the-holderpage-pattern)
* [Lesson 6: Adding Custom Fields to a Page](http://www.silverstripe.org/learn/lessons/adding-custom-fields-to-a-page)
* [Lesson 7: Working with Files and Images](http://www.silverstripe.org/learn/lessons/working-with-files-and-images)
* [Lesson 8: Introduction to the ORM](http://www.silverstripe.org/learn/lessons/introduction-to-the-orm)
* [Lesson 9: Data Relationships - $has_many](http://www.silverstripe.org/learn/lessons/working-with-data-relationships-has-many)
* [Lesson 10: Introduction to the ORM](http://www.silverstripe.org/learn/lessons/working-with-data-relationships-many-many)
## Help: If you get stuck

View File

@ -70,7 +70,7 @@ These variables will call a method / field on the object and insert the returned
* `$Foo.Bar` will call `$obj->Foo()->Bar()`
If a variable returns a string, that string will be inserted into the template. If the variable returns an object, then
the system will attempt to render the object through its' `forTemplate()` method. If the `forTemplate()` method has not
the system will attempt to render the object through its `forTemplate()` method. If the `forTemplate()` method has not
been defined, the system will return an error.
<div class="note" markdown="1">
@ -96,7 +96,7 @@ Variables can come from your database fields, or custom methods you define on yo
Method names that begin with `get` will automatically be resolved when their prefix is excluded. For example, the above method call `$UsersIpAddress` would also invoke a method named `getUsersIpAddress()`.
</div>
The variable's that can be used in a template vary based on the object currently in [scope](#scope). Scope defines what
The variables that can be used in a template vary based on the object currently in [scope](#scope). Scope defines what
object the methods get called on. For the standard `Page.ss` template the scope is the current [api:Page_Controller]
class. This object gives you access to all the database fields on [api:Page_Controller], its corresponding [api:Page]
record and any subclasses of those two.
@ -243,7 +243,7 @@ object that is being looped over.
`<% loop %>` statements iterate over a [api:DataList] instance. As the template has access to the list object,
templates can call [api:DataList] methods.
Sort the list by a given field.
Sorting the list by a given field.
:::ss
<ul>
@ -270,7 +270,7 @@ Reversing the loop.
<% end_loop %>
</ul>
Filtering the loop
Filtering the loop.
:::ss
<ul>
@ -279,7 +279,7 @@ Filtering the loop
<% end_loop %>
</ul>
Methods can also be chained
Methods can also be chained.
:::ss
<ul>
@ -293,15 +293,15 @@ Methods can also be chained
Inside the loop scope, there are many variables at your disposal to determine the current position in the list and
iteration.
* `$Even`, `$Odd`: Returns boolean, handy for zebra striping
* `$Even`, `$Odd`: Returns boolean, handy for zebra striping.
* `$EvenOdd`: Returns a string, either 'even' or 'odd'. Useful for CSS classes.
* `$First`, `$Last`, `$Middle`: Booleans about the position in the list
* `$First`, `$Last`, `$Middle`: Booleans about the position in the list.
* `$FirstLast`: Returns a string, "first", "last", "first last" (if both), or "". Useful for CSS classes.
* `$Pos`: The current position in the list (integer).
Will start at 1, but can take a starting index as a parameter.
* `$FromEnd`: The position of the item from the end (integer).
Last item defaults to 1, but can be passed as a parameter.
* `$TotalItems`: Number of items in the list (integer)
* `$TotalItems`: Number of items in the list (integer).
:::ss
<ul>
@ -344,7 +344,7 @@ $Modulus and $MultipleOf can help to build column and grid layouts.
</div>
$MultipleOf(value, offset) can also be utilized to build column and grid layouts. In this case we want to add a `<br>`
after every 3th item.
after every 3rd item.
:::ss
<% loop $Children %>
@ -389,7 +389,7 @@ layout template is the [api:Page_Controller] that is currently being rendered.
When the scope is a `Page_Controller` it will automatically also look up any methods in the corresponding `Page` data
record. In the case of `$Title` the flow looks like
$Title --> [Looks up: Current Page_Controller and parent classes] --> [Looks up: Current Page and parent classes].
$Title --> [Looks up: Current Page_Controller and parent classes] --> [Looks up: Current Page and parent classes]
The list of variables you could use in your template is the total of all the methods in the current scope object, parent
classes of the current scope object, and any [api:Extension] instances you have.

View File

@ -186,7 +186,7 @@ If the Javascript files are preferred to be placed in the `<head>` tag rather th
`Requirements.write_js_to_body` should be set to false.
:::php
Requirements::set_force_js_to_bottom(true);
Requirements::set_write_js_to_body(false);
## API Documentation

View File

@ -76,7 +76,7 @@ does, such as `ArrayData` or `ArrayList`.
'Title' => 'First Job'
)));
return $this->customize(new ArrayData(array(
return $this->customise(new ArrayData(array(
'Name' => 'John',
'Role' => 'Head Coach',
'Experience' => $experience
@ -86,4 +86,4 @@ does, such as `ArrayData` or `ArrayList`.
}
}
}

View File

@ -100,7 +100,7 @@ Some common examples are [api:TextField] or [api:DropdownField].
TextField::create($name, $title, $value);
<div class="info" markdown='1'>
A list of the common FormField subclasses is available on the [Common Subclasses](fields/common_subclasses) page.
A list of the common FormField subclasses is available on the [Common Subclasses](field_types/common_subclasses/) page.
</div>
The fields are added to the [api:FieldList] `fields` property on the `Form` and can be modified at up to the point the

View File

@ -137,7 +137,7 @@ reusable and would not be possible within the `CMS` or other automated `UI` but
$form = new Form($controller, 'MyForm', $fields, $actions);
return $form
return $form;
}
public function doSubmitForm($data, $form) {

View File

@ -226,7 +226,7 @@ Example: Remove field for "image captions"
// File: mysite/code/MyToolbarExtension.php
class MyToolbarExtension extends Extension {
public function updateFieldsForImage(&$fields, $url, $file) {
$fields->removeByName('Caption');
$fields->removeByName('CaptionText');
}
}

View File

@ -71,7 +71,7 @@ the `getConfig()` method on `GridField`.
);
// GridField configuration
$config = $gridField->getConfig();
$config = $grid->getConfig();
//
// Modification of existing components can be done by fetching that component.
@ -373,7 +373,7 @@ Your new area can also be used by existing components, e.g. the [api:GridFieldPr
## Creating a Custom GridFieldComponent
Customizing a `GridField` is easy, applications and modules can provide their own `GridFieldComponent` instances to add
functionality. See [How to Create a GridFieldComponent](../how_tos/create_a_gridfield_component).
functionality. See [How to Create a GridFieldComponent](../how_tos/create_a_gridfieldcomponent).
## Creating a Custom GridField_ActionProvider

View File

@ -112,7 +112,7 @@ we added a `SayHi` method which is unique to our extension.
## Modifying Existing Methods
If the `Extension` needs to modify an existing method it's a little tricker. It requires that the method you want to
If the `Extension` needs to modify an existing method it's a little trickier. It requires that the method you want to
customize has provided an *Extension Hook* in the place where you want to modify the data. An *Extension Hook* is done
through the `[api:Object->extend]` method.

View File

@ -15,10 +15,10 @@ email was sent using this method.
$e->send();
}
To test that `MyMethod` sends the correct email, use the [api:Email::assertEmailSent] method.
To test that `MyMethod` sends the correct email, use the [api:SapphireTest::assertEmailSent] method.
:::php
$this->assertEmailSend($to, $from, $subject, $body);
$this->assertEmailSent($to, $from, $subject, $body);
// to assert that the email is sent to the correct person
$this->assertEmailSent("someone@example.com", null, "/th.*e$/");

View File

@ -19,22 +19,16 @@ If you are familiar with PHP coding but new to unit testing then check out Mark'
You should also read over [the PHPUnit manual](http://www.phpunit.de/manual/current/en/). It provides a lot of
fundamental concepts that we build on in this documentation.
Unit tests are not included in the normal SilverStripe downloads so you need to install them through git repositories
([installation instructions](/getting_started/composer)).
Unit tests are not included in the zip/tar.gz SilverStripe [downloads](http://www.silverstripe.org/software/download/) so to get them, install SilverStripe [with composer](/getting_started/composer).
## Install with Composer
## Invoking phpunit
Once you've got your project up and running, open a terminal and cd to your project root.
Once you have used composer to create your project, `cd` to your project root. Composer will have installed PHPUnit alongside the required PHP classes into the `vendor/bin/` directory.
composer require --dev "phpunit/phpunit:*@stable"
If you don't want to invoke PHPUnit through its full path (`vendor/bin/phpunit`), add `./vendor/bin` to your $PATH, or symlink phpunit into the root directory of your website:
This will install [PHPUnit](http://www.phpunit.de/) dependency, which is the framework we're
building our unit tests on. Composer installs it alongside the required PHP classes into the `vendor/bin/` directory.
### Symlinking the PHPUnit Binary
You can either use PHPUnit through its full path (`vendor/bin/phpunit`), or symlink it into the root directory of your website:
ln -s vendor/bin/phpunit phpunit
- `PATH=./vendor/bin:$PATH` in your shell's profile script; **or**
- `ln -s vendor/bin/phpunit phpunit` at the command prompt in your project root
## Configuration
@ -128,6 +122,6 @@ particularly around formatting test output.
### Via Web Browser
Executing tests from the command line is recommended, since it most closely reflects
test runs in any automated testing environments. However, you can also run tests through the browser (requires PHPUnit version 3.7.*@stable):
test runs in any automated testing environments. However, you can also run tests through the browser:
http://localhost/dev/tests

View File

@ -8,41 +8,28 @@ authentication system.
The main login system uses these controllers to handle the various security requests:
`[api:Security]` Which is the controller which handles most front-end security requests, including
Logging in, logging out, resetting password, or changing password. This class also provides an interface
to allow configured `[api:Authenticator]` classes to each display a custom login form.
`[api:CMSSecurity]` Which is the controller which handles security requests within the CMS, and allows
users to re-login without leaving the CMS.
`[api:Security]` - Which is the controller which handles most front-end security requests, including logging in, logging out, resetting password, or changing password. This class also provides an interface to allow configured `[api:Authenticator]` classes to each display a custom login form.
`[api:CMSSecurity]` - Which is the controller which handles security requests within the CMS, and allows users to re-login without leaving the CMS.
## Member Authentication
The default member authentication system is implemented in the following classes:
`[api:MemberAuthenticator]` Which is the default member authentication implementation. This uses the email
and password stored internally for each member to authenticate them.
`[api:MemberLoginForm]` Is the default form used by `MemberAuthenticator`, and is displayed on the public site
at the url `Security/login` by default.
`[api:CMSMemberLoginForm]` Is the secondary form used by `MemberAuthenticator`, and will be displayed to the
user within the CMS any time their session expires or they are logged out via an action. This form is
presented via a popup dialog, and can be used to re-authenticate that user automatically without them having
to lose their workspace. E.g. if editing a form, the user can login and continue to publish their content.
`[api:MemberAuthenticator]` - Which is the default member authentication implementation. This uses the email and password stored internally for each member to authenticate them.
`[api:MemberLoginForm]` - Is the default form used by `MemberAuthenticator`, and is displayed on the public site at the url `Security/login` by default.
`[api:CMSMemberLoginForm]` - Is the secondary form used by `MemberAuthenticator`, and will be displayed to the user within the CMS any time their session expires or they are logged out via an action. This form is presented via a popup dialog, and can be used to re-authenticate that user automatically without them having to lose their workspace. E.g. if editing a form, the user can login and continue to publish their content.
## Custom Authentication
Additional authentication methods (oauth, etc) can be implemented by creating custom implementations of each of the
following base classes:
`[api:Authenticator]` The base class for authentication systems. This class also acts as the factory
to generate various login forms for parts of the system. If an authenticator supports in-cms
reauthentication then it will be necessary to override the `supports_cms` and `get_cms_login_form` methods.
`[api:Authenticator]` - The base class for authentication systems. This class also acts as the factory to generate various login forms for parts of the system. If an authenticator supports in-cms reauthentication then it will be necessary to override the `supports_cms` and `get_cms_login_form` methods.
`[api:LoginForm]` which is the base class for a login form which links to a specific authenticator. At the very
least, it will be necessary to implement a form class which provides a default login interface. If in-cms
re-authentication is desired, then a specialised subclass of this method may be necessary. For example, this form
could be extended to require confirmation of username as well as password.
`[api:LoginForm]` - which is the base class for a login form which links to a specific authenticator. At the very least, it will be necessary to implement a form class which provides a default login interface. If in-cms re-authentication is desired, then a specialised subclass of this method may be necessary. For example, this form could be extended to require confirmation of username as well as password.
## Default Admin

View File

@ -552,6 +552,58 @@ This is a recommended option to secure any controller which displays
or submits sensitive user input, and is enabled by default in all CMS controllers,
as well as the login form.
## Request hostname forgery
To prevent a forged hostname appearing being used by the application, SilverStripe
allows the configure of a whitelist of hosts that are allowed to access the system. By defining
this whitelist in your _ss_environment.php file, any request presenting a `Host` header that is
_not_ in this list will be blocked with a HTTP 400 error:
:::php
define('SS_ALLOWED_HOSTS', 'www.mysite.com,mysite.com,subdomain.mysite.com');
Please note that if this configuration is defined, you _must_ include _all_ subdomains (eg www.)
that will be accessing the site.
When SilverStripe is run behind a reverse proxy, it's normally necessary for this proxy to
use the `X-Forwarded-Host` request header to tell the webserver which hostname was originally
requested. However, when SilverStripe is not run behind a proxy, this header can still be
used by attackers to fool the server into mistaking its own identity.
The risk of this kind of attack causing damage is especially high on sites which utilise caching
mechanisms, as rewritten urls could persist between requests in order to misdirect other users
into visiting external sites.
In order to prevent this kind of attack, it's necessary to whitelist trusted proxy
server IPs using the SS_TRUSTED_PROXY_IPS define in your _ss_environment.php.
:::php
define('SS_TRUSTED_PROXY_IPS', '127.0.0.1,192.168.0.1');
If there is no proxy server, 'none' can be used to distrust all clients.
If only trusted servers will make requests then you can use '*' to trust all clients.
Otherwise a comma separated list of individual IP addresses should be declared.
This behaviour is enabled whenever SS_TRUSTED_PROXY_IPS is defined, or if the
`BlockUntrustedIPs` environment variable is declared. It is advisable to include the
following in your .htaccess to ensure this behaviour is activated.
<IfModule mod_env.c>
# Ensure that X-Forwarded-Host is only allowed to determine the request
# hostname for servers ips defined by SS_TRUSTED_PROXY_IPS in your _ss_environment.php
# Note that in a future release this setting will be always on.
SetEnv BlockUntrustedIPs true
</IfModule>
In a future release this behaviour will be changed to be on by default, and this environment
variable will be no longer necessary, thus it will be necessary to always set
SS_TRUSTED_PROXY_IPS if using a proxy.
## Related
* [http://silverstripe.org/security-releases/](http://silverstripe.org/security-releases/)

View File

@ -13,7 +13,7 @@ feel familiar to you. This is just a quick run down to get you started
with some special conventions.
For a more practical-oriented approach to CMS customizations, refer to the
[Howto: Extend the CMS Interface](../how_tos/extend_cms_interface) which builds
[Howto: Extend the CMS Interface](how_tos/extend_cms_interface) which builds
## Markup and Style Conventions

View File

@ -1,6 +1,6 @@
# How to implement an alternating button #
# How to implement an alternating button
## Introduction ##
## Introduction
*Save* and *Save & publish* buttons alternate their appearance to reflect the state of the underlying `SiteTree` object.
This is based on a `ssui.button` extension available in `ssui.core.js`.
@ -16,7 +16,7 @@ This how-to will walk you through creation of a "Clean-up" button with two appea
The controller code that goes with this example is listed in [Extend CMS Interface](extend_cms_interface).
## Backend support ##
## Backend support
First create and configure the action button with alternate state on a page type. The button comes with the default
state already, so you just need to add the alternate state using two data additional attributes:
@ -60,7 +60,7 @@ Here we initialise the button based on the backend check, and assume that the bu
// ...
}
## Frontend support ##
## Frontend support
As with the *Save* and *Save & publish* buttons, you might want to add some scripted reactions to user actions on the
frontend. You can affect the state of the button through the jQuery UI calls.
@ -103,7 +103,7 @@ CMS core that tracks the changes to the input fields and reacts by enabling the
}
});
## Frontend hooks ##
## Frontend hooks
`ssui.button` defines several additional events so that you can extend the code with your own behaviours. For example
this is used in the CMS to style the buttons. Three events are available:
@ -153,7 +153,7 @@ cases.
}(jQuery));
## Summary ##
## Summary
The code presented gives you a fully functioning alternating button, similar to the defaults that come with the the CMS.
These alternating buttons can be used to give user the advantage of visual feedback upon their actions.

View File

@ -0,0 +1,39 @@
# 3.1.13
# Overview
This release includes several security fixes to prevent HTTP Hostname injection,
as well as a fix for flush or isDev querystring parameters
to be set via unauthenticated requests.
Users upgrading from 3.1.12 or below should read the [security documentation](/security/secure_coding)
on securing their site.
### Security
* 2015-05-22 [a978b89](https://github.com/silverstripe/sapphire/commit/a978b89) Fix handling of empty parameter token (Damian Mooyman) - See [ss-2015-014](http://www.silverstripe.org/software/download/security-releases/ss-2015-014)
* 2015-05-25 [75137db](https://github.com/silverstripe/sapphire/commit/75137db) Ensure only trusted proxy servers have control over certain HTTP headers (Damian Mooyman) - See [ss-2015-013](http://www.silverstripe.org/software/download/security-releases/ss-2015-013)
* 2015-05-25 [22a35e4](https://github.com/silverstripe/sapphire/commit/22a35e4) Fix malformed urls redirecting to external sites (Damian Mooyman) - See [ss-2015-012](http://www.silverstripe.org/software/download/security-releases/ss-2015-012)
* 2015-05-22 [79cfa2b](https://github.com/silverstripe/sapphire/commit/79cfa2b) Bug fix sqlquery select (Damian Mooyman) - See [ss-2015-011](http://www.silverstripe.org/software/download/security-releases/ss-2015-011)
### Bugfixes
* 2015-04-24 [242de4e](https://github.com/silverstripe/sapphire/commit/242de4e) Added Youtube's short URL. (Michael Strong)
* 2015-05-28 [9c8fa51](https://github.com/silverstripe/sapphire/commit/9c8fa51) Allow users to specify allowed hosts (Marcus Nyeholt)
* 2015-05-07 [828ad6e](https://github.com/silverstripe/sapphire/commit/828ad6e) Modifications to GridFieldExportButton to allow ArrayList use in SS_Report (Will Rossiter)
* 2015-04-30 [be10d90](https://github.com/silverstripe/sapphire/commit/be10d90) count breaks when having clause defined (Aram Balakjian)
* 2015-04-27 [120b983](https://github.com/silverstripe/sapphire/commit/120b983) X-Reload & X-ControllerURL didn't support absolute URLs (fixes #4119) (Loz Calver)
* 2015-04-25 [bfd8b66](https://github.com/silverstripe/sapphire/commit/bfd8b66) for #4104, minor revision of error messages in ListboxField (more intuitive). (Patrick Nelson)
* 2015-04-23 [5ae0ca1](https://github.com/silverstripe/sapphire/commit/5ae0ca1) #4100 Setup the ability to overload the ShortcodeParser class and ensuring its methods/properties are extensible via the "static" keyword. (Patrick Nelson)
* 2015-04-23 [c2fd18e](https://github.com/silverstripe/sapphire/commit/c2fd18e) use config for Security::$login_url (Daniel Hensby)
* 2015-04-23 [19423e9](https://github.com/silverstripe/sapphire/commit/19423e9) Fix tinymce errors crashing CMS When removing a tinymce field, internal third party errors should be caught and ignored gracefully rather than breaking the whole CMS. (Damian Mooyman)
* 2015-04-20 [8e24511](https://github.com/silverstripe/sapphire/commit/8e24511) Fix users with all cms section access not able to edit files Fixes #4078 (Damian Mooyman)
* 2015-04-14 [8caaae6](https://github.com/silverstripe/sapphire/commit/8caaae6) Fix accordion sometimes displaying scrollbars (Damian Mooyman)
* 2015-03-31 [a71f5f9](https://github.com/silverstripe/silverstripe-cms/commit/a71f5f9) Use SearchForm::create to instantiate SearchForm (Daniel Hensby)
* 2015-03-26 [636cddb](https://github.com/silverstripe/sapphire/commit/636cddb) export and print buttons outside button row (Naomi Guyer)
* 2015-03-26 [a7d3f89](https://github.com/silverstripe/sapphire/commit/a7d3f89) Check for existence of HTTP_USER_AGENT to avoid E_NOTICE error. (Sean Harvey)
* 2015-03-25 [8d6cd15](https://github.com/silverstripe/sapphire/commit/8d6cd15) Fix some database errors during dev/build where an auth token exists for the current user Fixes #3660 (Damian Mooyman)
* 2015-03-23 [aba0b70](https://github.com/silverstripe/sapphire/commit/aba0b70) GridFieldDetailForm::setItemEditFormCalback broke chaining (Daniel Hensby)
* 2015-03-23 [72bb9a2](https://github.com/silverstripe/sapphire/commit/72bb9a2) Debug::text no longer incorrecty returns "ViewableData_debugger" (Daniel Hensby)
* 2015-03-16 [f2b1fa9](https://github.com/silverstripe/sapphire/commit/f2b1fa9) broken link in docs to how_tos/extend_cms_interface (Jeremy Shipman)
* 2015-02-24 [6c92a86](https://github.com/silverstripe/silverstripe-cms/commit/6c92a86) Fix CMSMainTest attempting to render page on Security permission error (Damian Mooyman)

View File

@ -0,0 +1,22 @@
# 3.1.13-rc1
### Bugfixes
* 2015-04-24 [242de4e](https://github.com/silverstripe/sapphire/commit/242de4e) Added Youtube's short URL. (Michael Strong)
* 2015-05-07 [828ad6e](https://github.com/silverstripe/sapphire/commit/828ad6e) Modifications to GridFieldExportButton to allow ArrayList use in SS_Report (Will Rossiter)
* 2015-04-30 [be10d90](https://github.com/silverstripe/sapphire/commit/be10d90) count breaks when having clause defined (Aram Balakjian)
* 2015-04-27 [120b983](https://github.com/silverstripe/sapphire/commit/120b983) X-Reload & X-ControllerURL didn't support absolute URLs (fixes #4119) (Loz Calver)
* 2015-04-25 [bfd8b66](https://github.com/silverstripe/sapphire/commit/bfd8b66) for #4104, minor revision of error messages in ListboxField (more intuitive). (Patrick Nelson)
* 2015-04-23 [5ae0ca1](https://github.com/silverstripe/sapphire/commit/5ae0ca1) #4100 Setup the ability to overload the ShortcodeParser class and ensuring its methods/properties are extensible via the "static" keyword. (Patrick Nelson)
* 2015-04-23 [c2fd18e](https://github.com/silverstripe/sapphire/commit/c2fd18e) use config for Security::$login_url (Daniel Hensby)
* 2015-04-23 [19423e9](https://github.com/silverstripe/sapphire/commit/19423e9) Fix tinymce errors crashing CMS When removing a tinymce field, internal third party errors should be caught and ignored gracefully rather than breaking the whole CMS. (Damian Mooyman)
* 2015-04-20 [8e24511](https://github.com/silverstripe/sapphire/commit/8e24511) Fix users with all cms section access not able to edit files Fixes #4078 (Damian Mooyman)
* 2015-04-14 [8caaae6](https://github.com/silverstripe/sapphire/commit/8caaae6) Fix accordion sometimes displaying scrollbars (Damian Mooyman)
* 2015-03-31 [a71f5f9](https://github.com/silverstripe/silverstripe-cms/commit/a71f5f9) Use SearchForm::create to instantiate SearchForm (Daniel Hensby)
* 2015-03-26 [636cddb](https://github.com/silverstripe/sapphire/commit/636cddb) export and print buttons outside button row (Naomi Guyer)
* 2015-03-26 [a7d3f89](https://github.com/silverstripe/sapphire/commit/a7d3f89) Check for existence of HTTP_USER_AGENT to avoid E_NOTICE error. (Sean Harvey)
* 2015-03-25 [8d6cd15](https://github.com/silverstripe/sapphire/commit/8d6cd15) Fix some database errors during dev/build where an auth token exists for the current user Fixes #3660 (Damian Mooyman)
* 2015-03-23 [aba0b70](https://github.com/silverstripe/sapphire/commit/aba0b70) GridFieldDetailForm::setItemEditFormCalback broke chaining (Daniel Hensby)
* 2015-03-23 [72bb9a2](https://github.com/silverstripe/sapphire/commit/72bb9a2) Debug::text no longer incorrecty returns "ViewableData_debugger" (Daniel Hensby)
* 2015-03-16 [f2b1fa9](https://github.com/silverstripe/sapphire/commit/f2b1fa9) broken link in docs to how_tos/extend_cms_interface (Jeremy Shipman)
* 2015-02-24 [6c92a86](https://github.com/silverstripe/silverstripe-cms/commit/6c92a86) Fix CMSMainTest attempting to render page on Security permission error (Damian Mooyman)

View File

@ -326,7 +326,7 @@ class File extends DataObject {
$result = $this->extendedCan('canEdit', $member);
if($result !== null) return $result;
return Permission::checkMember($member, 'CMS_ACCESS_AssetAdmin');
return Permission::checkMember($member, array('CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain'));
}
/**
@ -411,7 +411,6 @@ class File extends DataObject {
//get a tree listing with only folder, no files
$folderTree = new TreeDropdownField("ParentID", _t('AssetTableField.FOLDER','Folder'), 'Folder');
$folderTree->setChildrenMethod('ChildFolders');
$fields = new FieldList(
new TabSet('Root',

View File

@ -474,6 +474,12 @@ class Folder extends File {
return Folder::get()->filter('ParentID', $this->ID);
}
/**
* Get the number of children of this folder that are also folders.
*/
public function numChildFolders() {
return $this->ChildFolders()->count();
}
/**
* @return String
*/
@ -486,7 +492,7 @@ class Folder extends File {
if(!$this->canEdit())
$classes .= " disabled";
$classes .= $this->markingClasses();
$classes .= $this->markingClasses('numChildFolders');
return $classes;
}

View File

@ -569,7 +569,7 @@ class FieldList extends ArrayList {
}
// Add the leftover fields to the end of the list.
$fields = $fields + array_values($fieldMap);
$fields = array_values($fields + $fieldMap);
// Update our internal $this->items parameter.
$this->items = $fields;

View File

@ -468,6 +468,7 @@ class Form extends RequestHandler {
if(Director::is_site_url($pageURL)) {
// Remove existing pragmas
$pageURL = preg_replace('/(#.*)/', '', $pageURL);
$pageURL = Director::absoluteURL($pageURL, true);
return $this->controller->redirect($pageURL . '#' . $this->FormName());
}
}

View File

@ -1,4 +1,5 @@
<?php
/**
* Hidden field.
*
@ -6,25 +7,42 @@
* @subpackage fields-dataless
*/
class HiddenField extends FormField {
/**
* @param array $properties
*
* @return string
*/
public function FieldHolder($properties = array()) {
return $this->Field($properties);
}
/**
* @return static
*/
public function performReadonlyTransformation() {
$clone = clone $this;
$clone->setReadonly(true);
return $clone;
}
/**
* @return bool
*/
public function IsHidden() {
return true;
}
/**
* {@inheritdoc}
*/
public function getAttributes() {
return array_merge(
parent::getAttributes(),
array('type' => 'hidden')
array(
'type' => 'hidden',
)
);
}

View File

@ -1,30 +1,34 @@
<?php
/**
* Simple label tag. This can be used to add extra text in your forms.
* Consider using a {@link ReadonlyField} if you need to display a label
* AND a value.
* Simple label, to add extra text in your forms.
*
* Use a {@link ReadonlyField} if you need to display a label and value.
*
* @package forms
* @subpackage fields-dataless
*/
class LabelField extends DatalessField {
/**
* @param string $name
* @param string $title
* @param Form $form
* @param null|string $title
*/
public function __construct($name, $title) {
// legacy handling for old parameters: $title, $heading, ...
// instead of new handling: $name, $title, $heading, ...
public function __construct($name, $title = null) {
// legacy handling:
// $title, $headingLevel...
$args = func_get_args();
if(!isset($args[1])) {
$title = (isset($args[0])) ? $args[0] : null;
$name = $title;
$form = (isset($args[3])) ? $args[3] : null;
if(isset($args[0])) {
$title = $args[0];
}
// Prefix name to avoid collisions.
$name = 'LabelField' . $title;
}
parent::__construct($name, $title);
}
}

View File

@ -215,13 +215,14 @@ class ListboxField extends DropdownField {
if($val) {
if(!$this->multiple && is_array($val)) {
throw new InvalidArgumentException('No array values allowed with multiple=false');
throw new InvalidArgumentException('Array values are not allowed (when multiple=false).');
}
if($this->multiple) {
$parts = (is_array($val)) ? $val : preg_split("/ *, */", trim($val));
if(ArrayLib::is_associative($parts)) {
throw new InvalidArgumentException('No associative arrays allowed multiple=true');
// This is due to the possibility of accidentally passing an array of values (as keys) and titles (as values) when only the keys were intended to be saved.
throw new InvalidArgumentException('Associative arrays are not allowed as values (when multiple=true), only indexed arrays.');
}
// Doesn't check against unknown values in order to allow for less rigid data handling.

View File

@ -106,12 +106,12 @@ class MoneyField extends FormField {
$fieldName = $this->name;
if($dataObject->hasMethod("set$fieldName")) {
$dataObject->$fieldName = DBField::create_field('Money', array(
"Currency" => $this->fieldCurrency->Value(),
"Amount" => $this->fieldAmount->Value()
"Currency" => $this->fieldCurrency->dataValue(),
"Amount" => $this->fieldAmount->dataValue()
));
} else {
$dataObject->$fieldName->setCurrency($this->fieldCurrency->Value());
$dataObject->$fieldName->setAmount($this->fieldAmount->Value());
$dataObject->$fieldName->setCurrency($this->fieldCurrency->dataValue());
$dataObject->$fieldName->setAmount($this->fieldAmount->dataValue());
}
}
@ -155,7 +155,7 @@ class MoneyField extends FormField {
$this->allowedCurrencies = $arr;
// @todo Has to be done twice in case allowed currencies changed since construction
$oldVal = $this->fieldCurrency->Value();
$oldVal = $this->fieldCurrency->dataValue();
$this->fieldCurrency = $this->FieldCurrency($this->name);
$this->fieldCurrency->setValue($oldVal);

View File

@ -1,4 +1,5 @@
<?php
/**
* Text input field.
*
@ -6,49 +7,77 @@
* @subpackage fields-basic
*/
class TextField extends FormField {
/**
* @var int
*/
protected $maxLength;
/**
* Returns an input field, class="text" and type="text" with an optional maxlength
* Returns an input field.
*
* @param string $name
* @param null|string $title
* @param string $value
* @param null|int $maxLength
* @param null|Form $form
*/
public function __construct($name, $title = null, $value = '', $maxLength = null, $form = null) {
$this->maxLength = $maxLength;
if($maxLength) {
$this->setMaxLength($maxLength);
}
parent::__construct($name, $title, $value, $form);
if($form) {
$this->setForm($form);
}
parent::__construct($name, $title, $value);
}
/**
* @param int $length
* @param int $maxLength
*
* @return static
*/
public function setMaxLength($length) {
$this->maxLength = $length;
public function setMaxLength($maxLength) {
$this->maxLength = $maxLength;
return $this;
}
/**
* @return int
* @return null|int
*/
public function getMaxLength() {
return $this->maxLength;
}
/**
* @return array
*/
public function getAttributes() {
$maxLength = $this->getMaxLength();
$attributes = array();
if($maxLength) {
$attributes['maxLength'] = $maxLength;
$attributes['size'] = min($maxLength, 30);
}
return array_merge(
parent::getAttributes(),
array(
'maxlength' => $this->getMaxLength(),
'size' => ($this->getMaxLength()) ? min($this->getMaxLength(), 30) : null
)
$attributes
);
}
/**
* @return string
*/
public function InternallyLabelledField() {
if(!$this->value) $this->value = $this->Title();
if(!$this->value) {
$this->value = $this->Title();
}
return $this->Field();
}

View File

@ -56,10 +56,15 @@ class TreeDropdownField extends FormField {
protected $sourceObject, $keyField, $labelField, $filterCallback,
$disableCallback, $searchCallback, $baseID = 0;
/**
* @var string default child method in Hierarcy->getChildrenAsUL
* @var string default child method in Hierarchy->getChildrenAsUL
*/
protected $childrenMethod = 'AllChildrenIncludingDeleted';
/**
* @var string default child counting method in Hierarchy->getChildrenAsUL
*/
protected $numChildrenMethod = 'numChildren';
/**
* Used by field search to leave only the relevant entries
*/
@ -96,6 +101,12 @@ class TreeDropdownField extends FormField {
$this->keyField = $keyField;
$this->labelField = $labelField;
$this->showSearch = $showSearch;
//Extra settings for Folders
if ($sourceObject == 'Folder') {
$this->childrenMethod = 'ChildFolders';
$this->numChildrenMethod = 'numChildFolders';
}
$this->addExtraClass('single');
@ -171,9 +182,9 @@ class TreeDropdownField extends FormField {
/**
* @param $method The parameter to ChildrenMethod to use when calling Hierarchy->getChildrenAsUL in
* {@link Hierarchy}. The method specified determined the structure of the returned list. Use "ChildFolders"
* {@link Hierarchy}. The method specified determines the structure of the returned list. Use "ChildFolders"
* in place of the default to get a drop-down listing with only folders, i.e. not including the child elements in
* the currently selected folder.
* the currently selected folder. setNumChildrenMethod() should be used as well for proper functioning.
*
* See {@link Hierarchy} for a complete list of possible methods.
*/
@ -181,6 +192,16 @@ class TreeDropdownField extends FormField {
$this->childrenMethod = $method;
return $this;
}
/**
* @param $method The parameter to numChildrenMethod to use when calling Hierarchy->getChildrenAsUL in
* {@link Hierarchy}. Should be used in conjunction with setChildrenMethod().
*
*/
public function setNumChildrenMethod($method) {
$this->numChildrenMethod = $method;
return $this;
}
/**
* @return string
@ -273,10 +294,11 @@ class TreeDropdownField extends FormField {
if ( $this->search != "" )
$this->populateIDs();
if ($this->filterCallback || $this->sourceObject == 'Folder' || $this->search != "" )
if ($this->filterCallback || $this->search != "" )
$obj->setMarkingFilterFunction(array($this, "filterMarking"));
$obj->markPartialTree();
$obj->markPartialTree($nodeCountThreshold = 30, $context = null,
$this->childrenMethod, $this->numChildrenMethod);
// allow to pass values to be selected within the ajax request
if( isset($_REQUEST['forceValue']) || $this->value ) {
@ -298,7 +320,7 @@ class TreeDropdownField extends FormField {
Convert::raw2xml($child->$keyField),
Convert::raw2xml($child->$keyField),
Convert::raw2xml($child->class),
Convert::raw2xml($child->markingClasses()),
Convert::raw2xml($child->markingClasses($self->numChildrenMethod)),
($self->nodeIsDisabled($child)) ? 'disabled' : '',
(int)$child->ID,
$child->obj($labelField)->forTemplate()
@ -330,7 +352,7 @@ class TreeDropdownField extends FormField {
null,
true,
$this->childrenMethod,
'numChildren',
$this->numChildrenMethod,
true, // root call
null,
$nodeCountCallback
@ -343,7 +365,7 @@ class TreeDropdownField extends FormField {
null,
true,
$this->childrenMethod,
'numChildren',
$this->numChildrenMethod,
true, // root call
null,
$nodeCountCallback
@ -353,15 +375,14 @@ class TreeDropdownField extends FormField {
}
/**
* Marking public function for the tree, which combines different filters sensibly. If a filter function has been
* set, that will be called. If the source is a folder, automatically filter folder. And if search text is set,
* Marking public function for the tree, which combines different filters sensibly.
* If a filter function has been set, that will be called. And if search text is set,
* filter on that too. Return true if all applicable conditions are true, false otherwise.
* @param $node
* @return unknown_type
*/
public function filterMarking($node) {
if ($this->filterCallback && !call_user_func($this->filterCallback, $node)) return false;
if ($this->sourceObject == "Folder" && $node->ClassName != 'Folder') return false;
if ($this->search != "") {
return isset($this->searchIds[$node->ID]) && $this->searchIds[$node->ID] ? true : false;
}

View File

@ -1,4 +1,5 @@
<?php
/**
* This validation class handles all form and custom form validation through
* the use of Required fields.
@ -24,18 +25,25 @@ abstract class Validator extends Object {
/**
* @param Form $form
*
* @return static
*/
public function setForm($form) {
$this->form = $form;
return $this;
}
/**
* @return array Errors (if any)
* Returns any errors there may be.
*
* @return null|array
*/
public function validate(){
public function validate() {
$this->errors = null;
$this->php($this->form->getData());
return $this->errors;
}
@ -45,9 +53,10 @@ abstract class Validator extends Object {
* @param string $fieldName name of the field
* @param string $message error message to display
* @param string $messageType optional parameter, gets loaded into the HTML class attribute in the rendered output.
*
* See {@link getErrors()} for details.
*/
public function validationError($fieldName, $message, $messageType='') {
public function validationError($fieldName, $message, $messageType = '') {
$this->errors[] = array(
'fieldName' => $fieldName,
'message' => $message,
@ -57,6 +66,7 @@ abstract class Validator extends Object {
/**
* Returns all errors found by a previous call to {@link validate()}.
*
* The array contains the following keys for each error:
* - 'fieldName': the name of the FormField instance
* - 'message': Validation message (optionally localized)
@ -70,28 +80,45 @@ abstract class Validator extends Object {
return $this->errors;
}
/**
* @param string $fieldName
* @param array $data
*/
public function requireField($fieldName, $data) {
if(is_array($data[$fieldName]) && count($data[$fieldName])) {
foreach($data[$fieldName] as $componentkey => $componentVal){
if(!strlen($componentVal)) {
$this->validationError($fieldName, "$fieldName $componentkey is required", "required");
foreach($data[$fieldName] as $componentKey => $componentValue) {
if(!strlen($componentValue)) {
$this->validationError(
$fieldName,
sprintf('%s %s is required', $fieldName, $componentKey),
'required'
);
}
}
} else if(!strlen($data[$fieldName])) {
$this->validationError($fieldName, "$fieldName is required", "required");
$this->validationError(
$fieldName,
sprintf('%s is required', $fieldName),
'required'
);
}
}
/**
* Returns true if the named field is "required".
*
* Used by FormField to return a value for FormField::Required(), to do things like show *s on the form template.
*
* By default, it always returns false.
*/
public function fieldIsRequired($fieldName) {
return false;
}
/**
* @param array $data
*
* @return mixed
*/
abstract public function php($data);
}

View File

@ -1,4 +1,5 @@
<?php
/**
* Displays a {@link SS_List} in a grid format.
*
@ -32,18 +33,21 @@ class GridField extends FormField {
/**
* The datasource
*
* @var SS_List
*/
protected $list = null;
/**
* The classname of the DataObject that the GridField will display. Defaults to the value of $this->list->dataClass
*
* @var string
*/
protected $modelClassName = '';
/**
* the current state of the GridField
*
* @var GridState
*/
protected $state = null;
@ -115,6 +119,7 @@ class GridField extends FormField {
* this modelclass $summary_fields
*
* @param string $modelClassName
*
* @see GridFieldDataColumns::getDisplayFields()
*/
public function setModelClass($modelClassName) {
@ -129,8 +134,8 @@ class GridField extends FormField {
* @return string
*/
public function getModelClass() {
if ($this->modelClassName) return $this->modelClassName;
if ($this->list && method_exists($this->list, 'dataClass')) {
if($this->modelClassName) return $this->modelClassName;
if($this->list && method_exists($this->list, 'dataClass')) {
$class = $this->list->dataClass();
if($class) return $class;
}
@ -150,6 +155,7 @@ class GridField extends FormField {
/**
* @param GridFieldConfig $config
*
* @return GridField
*/
public function setConfig(GridFieldConfig $config) {
@ -166,6 +172,7 @@ class GridField extends FormField {
*
* @param $value
* @param $castingDefinition
*
* @todo refactor this into GridFieldComponent
*/
public function getCastedValue($value, $castingDefinition) {
@ -177,16 +184,16 @@ class GridField extends FormField {
$castingParams = array();
}
if(strpos($castingDefinition,'->') === false) {
if(strpos($castingDefinition, '->') === false) {
$castingFieldType = $castingDefinition;
$castingField = DBField::create_field($castingFieldType, $value);
$value = call_user_func_array(array($castingField,'XML'),$castingParams);
$value = call_user_func_array(array($castingField, 'XML'), $castingParams);
} else {
$fieldTypeParts = explode('->', $castingDefinition);
$castingFieldType = $fieldTypeParts[0];
$castingMethod = $fieldTypeParts[1];
$castingField = DBField::create_field($castingFieldType, $value);
$value = call_user_func_array(array($castingField,$castingMethod),$castingParams);
$value = call_user_func_array(array($castingField, $castingMethod), $castingParams);
}
return $value;
@ -294,7 +301,7 @@ class GridField extends FormField {
$fragmentDefined = array('header' => true, 'footer' => true, 'before' => true, 'after' => true);
reset($content);
while(list($k,$v) = each($content)) {
while(list($k, $v) = each($content)) {
if(preg_match_all('/\$DefineFragment\(([a-z0-9\-_]+)\)/i', $v, $matches)) {
foreach($matches[1] as $match) {
$fragmentName = strtolower($match);
@ -332,7 +339,7 @@ class GridField extends FormField {
foreach($content as $k => $v) {
if(empty($fragmentDefined[$k])) {
throw new LogicException("GridField HTML fragment '$k' was given content,"
. " but not defined. Perhaps there is a supporting GridField component you need to add?");
. " but not defined. Perhaps there is a supporting GridField component you need to add?");
}
}
@ -343,29 +350,25 @@ class GridField extends FormField {
if($record->hasMethod('canView') && !$record->canView()) {
continue;
}
$rowContent = '';
foreach($this->getColumns() as $column) {
$colContent = $this->getColumnContent($record, $column);
// A return value of null means this columns should be skipped altogether.
if($colContent === null) continue;
if($colContent === null) {
continue;
}
$colAttributes = $this->getColumnAttributes($record, $column);
$rowContent .= FormField::create_tag('td', $colAttributes, $colContent);
$rowContent .= $this->newCell($total, $idx, $record, $colAttributes, $colContent);
}
$classes = array('ss-gridfield-item');
if ($idx == 0) $classes[] = 'first';
if ($idx == $total-1) $classes[] = 'last';
$classes[] = ($idx % 2) ? 'even' : 'odd';
$row = FormField::create_tag(
'tr',
array(
"class" => implode(' ', $classes),
'data-id' => $record->ID,
// TODO Allow per-row customization similar to GridFieldDataColumns
'data-class' => $record->ClassName,
),
$rowContent
);
$rows[] = $row;
$rowAttributes = $this->getRowAttributes($total, $idx, $record);
$rows[] = $this->newRow($total, $idx, $record, $rowAttributes, $rowContent);
}
$content['body'] = implode("\n", $rows);
}
@ -418,11 +421,85 @@ class GridField extends FormField {
return
FormField::create_tag('fieldset', $attrs,
$content['before'] .
FormField::create_tag('table', $tableAttrs, $head."\n".$foot."\n".$body) .
FormField::create_tag('table', $tableAttrs, $head . "\n" . $foot . "\n" . $body) .
$content['after']
);
}
/**
* @param int $total
* @param int $index
* @param DataObject $record
* @param array $attributes
* @param string $content
*
* @return string
*/
protected function newCell($total, $index, $record, $attributes, $content) {
return FormField::create_tag(
'td',
$attributes,
$content
);
}
/**
* @param int $total
* @param int $index
* @param DataObject $record
* @param array $attributes
* @param string $content
*
* @return string
*/
protected function newRow($total, $index, $record, $attributes, $content) {
return FormField::create_tag(
'tr',
$attributes,
$content
);
}
/**
* @param int $total
* @param int $index
* @param DataObject $record
*
* @return array
*/
protected function getRowAttributes($total, $index, $record) {
$rowClasses = $this->newRowClasses($total, $index, $record);
return array(
'class' => implode(' ', $rowClasses),
'data-id' => $record->ID,
'data-class' => $record->ClassName,
);
}
/**
* @param int $total
* @param int $index
* @param DataObject $record
*
* @return array
*/
protected function newRowClasses($total, $index, $record) {
$classes = array('ss-gridfield-item');
if($index == 0) {
$classes[] = 'first';
}
if($index == $total - 1) {
$classes[] = 'last';
}
$classes[] = ($index % 2) ? 'even' : 'odd';
return $classes;
}
public function Field($properties = array()) {
return $this->FieldHolder($properties);
}
@ -453,6 +530,7 @@ class GridField extends FormField {
*
* @param DataObject $record
* @param string $column
*
* @return string
* @throws InvalidArgumentException
*/
@ -477,7 +555,7 @@ class GridField extends FormField {
* Add additional calculated data fields to be used on this GridField
*
* @param array $fields a map of fieldname to callback. The callback will
* be passed the record as an argument.
* be passed the record as an argument.
*/
public function addDataFields($fields) {
if($this->customDataFields) {
@ -513,6 +591,7 @@ class GridField extends FormField {
*
* @param DataObject $record
* @param string $column
*
* @return array
* @throws LogicException
* @throws InvalidArgumentException
@ -547,6 +626,7 @@ class GridField extends FormField {
* Get metadata for a column, example array('Title'=>'Email address')
*
* @param string $column
*
* @return array
* @throws LogicException
* @throws InvalidArgumentException
@ -611,6 +691,7 @@ class GridField extends FormField {
* This is the action that gets executed when a GridField_AlterAction gets clicked.
*
* @param array $data
*
* @return string
*/
public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request) {
@ -661,7 +742,8 @@ class GridField extends FormField {
*
* @param string $actionName
* @param mixed $args
* @param arrray $data - send data from a form
* @param array $data - send data from a form
*
* @return mixed
* @throws InvalidArgumentException
*/
@ -672,7 +754,7 @@ class GridField extends FormField {
continue;
}
if(in_array($actionName, array_map('strtolower', (array)$component->getActions($this)))) {
if(in_array($actionName, array_map('strtolower', (array) $component->getActions($this)))) {
return $component->handleAction($this, $actionName, $args, $data);
}
}
@ -705,7 +787,7 @@ class GridField extends FormField {
if($urlHandlers) foreach($urlHandlers as $rule => $action) {
if($params = $request->match($rule, true)) {
// Actions can reference URL parameters, eg, '$Action/$ID/$OtherID' => '$Action',
if($action[0] == '$') $action = $params[substr($action,1)];
if($action[0] == '$') $action = $params[substr($action, 1)];
if(!method_exists($component, 'checkAccessAction') || $component->checkAccessAction($action)) {
if(!$action) {
$action = "index";
@ -724,7 +806,8 @@ class GridField extends FormField {
}
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result)
&& $result instanceof RequestHandler) {
&& $result instanceof RequestHandler
) {
$returnValue = $result->handleRequest($request, $model);
@ -734,11 +817,11 @@ class GridField extends FormField {
return $returnValue;
// If we return some other data, and all the URL is parsed, then return that
// If we return some other data, and all the URL is parsed, then return that
} else if($request->allParsed()) {
return $result;
// But if we have more content on the URL and we don't know what to do with it, return an error
// But if we have more content on the URL and we don't know what to do with it, return an error
} else {
return $this->httpError(404,
"I can't handle sub-URLs of a " . get_class($result) . " object.");
@ -766,7 +849,7 @@ class GridField extends FormField {
* This class is the base class when you want to have an action that alters
* the state of the {@link GridField}, rendered as a button element.
*
* @package forms
* @package forms
* @subpackage fields-gridfield
*/
class GridField_FormAction extends FormAction {
@ -827,7 +910,7 @@ class GridField_FormAction extends FormAction {
* @param string $val
*/
public function _nameEncode($match) {
return '%'.dechex(ord($match[0]));
return '%' . dechex(ord($match[0]));
}
/**
@ -842,7 +925,7 @@ class GridField_FormAction extends FormAction {
);
// Ensure $id doesn't contain only numeric characters
$id = 'gf_'.substr(md5(serialize($state)), 0, 8);
$id = 'gf_' . substr(md5(serialize($state)), 0, 8);
Session::set($id, $state);
$actionData['StateID'] = $id;
@ -851,7 +934,7 @@ class GridField_FormAction extends FormAction {
array(
// Note: This field needs to be less than 65 chars, otherwise Suhosin security patch
// will strip it from the requests
'name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData),
'name' => 'action_gridFieldAlterAction' . '?' . http_build_query($actionData),
'data-url' => $this->gridField->Link(),
)
);
@ -861,6 +944,7 @@ class GridField_FormAction extends FormAction {
* Calculate the name of the gridfield relative to the Form
*
* @param GridField $base
*
* @return string
*/
protected function getNameFromParent() {
@ -870,7 +954,7 @@ class GridField_FormAction extends FormAction {
do {
array_unshift($name, $base->getName());
$base = $base->getForm();
} while ($base && !($base instanceof Form));
} while($base && !($base instanceof Form));
return implode('.', $name);
}

View File

@ -132,7 +132,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
}
foreach($items->limit(null) as $item) {
if($item->hasMethod('canView') && $item->canView()) {
if(!$item->hasMethod('canView') || $item->canView()) {
$columnData = array();
foreach($csvColumns as $columnSource => $columnHeader) {
@ -146,16 +146,21 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
$value = $columnHeader($relObj);
} else {
$value = $gridField->getDataFieldValue($item, $columnSource);
if(!$value) {
$value = $gridField->getDataFieldValue($item, $columnHeader);
}
}
$value = str_replace(array("\r", "\n"), "\n", $value);
$columnData[] = '"' . str_replace('"', '""', $value) . '"';
}
$fileData .= implode($separator, $columnData);
$fileData .= "\n";
}
if ($item->hasMethod('destroy')) {
if($item->hasMethod('destroy')) {
$item->destroy();
}
}

View File

@ -204,8 +204,9 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr
$itemRows->push(new ArrayData(array(
"ItemRow" => $itemRow
)));
$item->destroy();
if ($item->hasMethod('destroy')) {
$item->destroy();
}
}
$ret = new ArrayData(array(

View File

@ -515,7 +515,7 @@ class i18nTextCollector_Writer_Php implements i18nTextCollector_Writer {
// Create folder for lang files
$langFolder = $path . '/lang';
if(!file_exists($langFolder)) {
Filesystem::makeFolder($langFolder, Config::inst()->get('Filesystem', 'folder_create_mask'));
Filesystem::makeFolder($langFolder);
touch($langFolder . '/_manifest_exclude');
}
@ -595,7 +595,7 @@ class i18nTextCollector_Writer_RailsYaml implements i18nTextCollector_Writer {
// Create folder for lang files
$langFolder = $path . '/lang';
if(!file_exists($langFolder)) {
Filesystem::makeFolder($langFolder, Config::inst()->get('Filesystem', 'folder_create_mask'));
Filesystem::makeFolder($langFolder);
touch($langFolder . '/_manifest_exclude');
}

View File

@ -293,8 +293,15 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
onremove: function() {
var ed = tinyMCE.get(this.attr('id'));
if (ed) {
ed.remove();
ed.destroy();
try {
ed.remove();
} catch(ex) {}
try {
ed.destroy();
} catch(ex) {}
// Remove any residual tinyMCE editor element
this.next('.mceEditor').remove();
// TinyMCE leaves behind events. We should really fix TinyMCE, but lets brute force it for now
$.each(jQuery.cache, function(){

View File

@ -5,6 +5,7 @@
this._super();
this.accordion({
heightStyle: "content",
collapsible: true,
active: (this.hasClass("ss-toggle-start-closed")) ? false : 0
});

47
javascript/lang/id.js Normal file
View File

@ -0,0 +1,47 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/id.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('id', {
"VALIDATOR.FIELDREQUIRED": "Anda wajib mengisi \"%s\".",
"HASMANYFILEFIELD.UPLOADING": "Mengunggah... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Anda ingin menghapus data ini?",
"LOADING": "memuat...",
"UNIQUEFIELD.SUGGESTED": "Nilai diubah ke '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Anda perlu mengisi nilai baru pada isian ini",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Isian ini tidak boleh kosong",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Karakter '%s' tidak diijinkan pada isian ini",
"UPDATEURL.CONFIRM": "Anda ingin mengubah URL ke:\n\n%s/\n\nKlik OK untuk lanjut, atau klik Batal untuk tetap pada:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL sudah diubah ke\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Hapus Berkas",
"FILEIFRAMEFIELD.UNATTACHFILE": "Lepaskan Berkas",
"FILEIFRAMEFIELD.DELETEIMAGE": "Hapus Gambar",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Anda ingin menghapus berkas ini?",
"LeftAndMain.IncompatBrowserWarning": "Browser Anda tidak mendukung antarmuka CMS. Mohon gunakan Internet Explorer 7+, Google Chrome 10+ atau Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Terjadi kesalahan dalam menarik data dari server\n Mohon coba lagi nanti.",
"HtmlEditorField.SelectAnchor": "Pilih jangkar",
"UploadField.ConfirmDelete": "Anda ingin menghapus berkas ini dari sistem berkas server?",
"UploadField.PHP_MAXFILESIZE": "Ukuran berkas melebihi upload_max_filesize (direktif php.ini)",
"UploadField.HTML_MAXFILESIZE": "Ukuran berkas melebihi MAX_FILE_SIZE (direktif HTML form)",
"UploadField.ONLYPARTIALUPLOADED": "Berkas hanya sebagian terunggah",
"UploadField.NOFILEUPLOADED": "Tidak ada berkas terunggah",
"UploadField.NOTMPFOLDER": "Folder sementara tidak ditemukan",
"UploadField.WRITEFAILED": "Gagal menyimpan berkas",
"UploadField.STOPEDBYEXTENSION": "Ekstensi berkas yang diunggah tidak diijinkan",
"UploadField.TOOLARGE": "Ukuran berkas terlalu besar",
"UploadField.TOOSMALL": "Ukuran berkas terlalu kecil",
"UploadField.INVALIDEXTENSION": "Ekstensi berkas tidak diijinkan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Jumlah maksimal berkas sudah terlampaui",
"UploadField.UPLOADEDBYTES": "Berkas yang diunggah melebihi batas ukuran berkas",
"UploadField.EMPTYRESULT": "Pengunggahan menghasilkan berkas kosong",
"UploadField.LOADING": "Memuat...",
"UploadField.Editing": "Mengedit...",
"UploadField.Uploaded": "Terunggah",
"UploadField.OVERWRITEWARNING": "Berkas dengan nama sama sudah ada",
"TreeDropdownField.ENTERTOSEARCH": "Tekan Enter untuk pencarian",
"TreeDropdownField.OpenLink": "Buka",
"TreeDropdownField.FieldTitle": "Pilih",
"TreeDropdownField.SearchFieldTitle": "Pilih atau Cari"
});
}

47
javascript/lang/id_ID.js Normal file
View File

@ -0,0 +1,47 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/id_ID.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('id_ID', {
"VALIDATOR.FIELDREQUIRED": "Anda wajib mengisi \"%s\".",
"HASMANYFILEFIELD.UPLOADING": "Mengunggah... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Anda ingin menghapus data ini?",
"LOADING": "memuat...",
"UNIQUEFIELD.SUGGESTED": "Nilai diubah ke '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Anda perlu mengisi nilai baru pada isian ini",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Isian ini tidak boleh kosong",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Karakter '%s' tidak diijinkan pada isian ini",
"UPDATEURL.CONFIRM": "Anda ingin mengubah URL ke:\n\n%s/\n\nKlik OK untuk lanjut, atau klik Batal untuk tetap pada:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL sudah diubah ke\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Hapus Berkas",
"FILEIFRAMEFIELD.UNATTACHFILE": "Lepaskan Berkas",
"FILEIFRAMEFIELD.DELETEIMAGE": "Hapus Gambar",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Anda ingin menghapus berkas ini?",
"LeftAndMain.IncompatBrowserWarning": "Browser Anda tidak mendukung antarmuka CMS. Mohon gunakan Internet Explorer 7+, Google Chrome 10+ atau Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Terjadi kesalahan dalam menarik data dari server\n Mohon coba lagi nanti.",
"HtmlEditorField.SelectAnchor": "Pilih jangkar",
"UploadField.ConfirmDelete": "Anda ingin menghapus berkas ini dari sistem berkas server?",
"UploadField.PHP_MAXFILESIZE": "Ukuran berkas melebihi upload_max_filesize (direktif php.ini)",
"UploadField.HTML_MAXFILESIZE": "Ukuran berkas melebihi MAX_FILE_SIZE (direktif HTML form)",
"UploadField.ONLYPARTIALUPLOADED": "Berkas hanya sebagian terunggah",
"UploadField.NOFILEUPLOADED": "Tidak ada berkas terunggah",
"UploadField.NOTMPFOLDER": "Folder sementara tidak ditemukan",
"UploadField.WRITEFAILED": "Gagal menyimpan berkas",
"UploadField.STOPEDBYEXTENSION": "Ekstensi berkas yang diunggah tidak diijinkan",
"UploadField.TOOLARGE": "Ukuran berkas terlalu besar",
"UploadField.TOOSMALL": "Ukuran berkas terlalu kecil",
"UploadField.INVALIDEXTENSION": "Ekstensi berkas tidak diijinkan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Jumlah maksimal berkas sudah terlampaui",
"UploadField.UPLOADEDBYTES": "Berkas yang diunggah melebihi batas ukuran berkas",
"UploadField.EMPTYRESULT": "Pengunggahan menghasilkan berkas kosong",
"UploadField.LOADING": "Memuat...",
"UploadField.Editing": "Mengedit...",
"UploadField.Uploaded": "Terunggah",
"UploadField.OVERWRITEWARNING": "Berkas dengan nama sama sudah ada",
"TreeDropdownField.ENTERTOSEARCH": "Tekan Enter untuk pencarian",
"TreeDropdownField.OpenLink": "Buka",
"TreeDropdownField.FieldTitle": "Pilih",
"TreeDropdownField.SearchFieldTitle": "Pilih atau Cari"
});
}

41
javascript/lang/src/id.js Normal file
View File

@ -0,0 +1,41 @@
{
"VALIDATOR.FIELDREQUIRED": "Anda wajib mengisi \"%s\".",
"HASMANYFILEFIELD.UPLOADING": "Mengunggah... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Anda ingin menghapus data ini?",
"LOADING": "memuat...",
"UNIQUEFIELD.SUGGESTED": "Nilai diubah ke '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Anda perlu mengisi nilai baru pada isian ini",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Isian ini tidak boleh kosong",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Karakter '%s' tidak diijinkan pada isian ini",
"UPDATEURL.CONFIRM": "Anda ingin mengubah URL ke:\n\n%s/\n\nKlik OK untuk lanjut, atau klik Batal untuk tetap pada:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL sudah diubah ke\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Hapus Berkas",
"FILEIFRAMEFIELD.UNATTACHFILE": "Lepaskan Berkas",
"FILEIFRAMEFIELD.DELETEIMAGE": "Hapus Gambar",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Anda ingin menghapus berkas ini?",
"LeftAndMain.IncompatBrowserWarning": "Browser Anda tidak mendukung antarmuka CMS. Mohon gunakan Internet Explorer 7+, Google Chrome 10+ atau Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Terjadi kesalahan dalam menarik data dari server\n Mohon coba lagi nanti.",
"HtmlEditorField.SelectAnchor": "Pilih jangkar",
"UploadField.ConfirmDelete": "Anda ingin menghapus berkas ini dari sistem berkas server?",
"UploadField.PHP_MAXFILESIZE": "Ukuran berkas melebihi upload_max_filesize (direktif php.ini)",
"UploadField.HTML_MAXFILESIZE": "Ukuran berkas melebihi MAX_FILE_SIZE (direktif HTML form)",
"UploadField.ONLYPARTIALUPLOADED": "Berkas hanya sebagian terunggah",
"UploadField.NOFILEUPLOADED": "Tidak ada berkas terunggah",
"UploadField.NOTMPFOLDER": "Folder sementara tidak ditemukan",
"UploadField.WRITEFAILED": "Gagal menyimpan berkas",
"UploadField.STOPEDBYEXTENSION": "Ekstensi berkas yang diunggah tidak diijinkan",
"UploadField.TOOLARGE": "Ukuran berkas terlalu besar",
"UploadField.TOOSMALL": "Ukuran berkas terlalu kecil",
"UploadField.INVALIDEXTENSION": "Ekstensi berkas tidak diijinkan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Jumlah maksimal berkas sudah terlampaui",
"UploadField.UPLOADEDBYTES": "Berkas yang diunggah melebihi batas ukuran berkas",
"UploadField.EMPTYRESULT": "Pengunggahan menghasilkan berkas kosong",
"UploadField.LOADING": "Memuat...",
"UploadField.Editing": "Mengedit...",
"UploadField.Uploaded": "Terunggah",
"UploadField.OVERWRITEWARNING": "Berkas dengan nama sama sudah ada",
"TreeDropdownField.ENTERTOSEARCH": "Tekan Enter untuk pencarian",
"TreeDropdownField.OpenLink": "Buka",
"TreeDropdownField.FieldTitle": "Pilih",
"TreeDropdownField.SearchFieldTitle": "Pilih atau Cari"
}

View File

@ -0,0 +1,41 @@
{
"VALIDATOR.FIELDREQUIRED": "Anda wajib mengisi \"%s\".",
"HASMANYFILEFIELD.UPLOADING": "Mengunggah... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Anda ingin menghapus data ini?",
"LOADING": "memuat...",
"UNIQUEFIELD.SUGGESTED": "Nilai diubah ke '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Anda perlu mengisi nilai baru pada isian ini",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Isian ini tidak boleh kosong",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Karakter '%s' tidak diijinkan pada isian ini",
"UPDATEURL.CONFIRM": "Anda ingin mengubah URL ke:\n\n%s/\n\nKlik OK untuk lanjut, atau klik Batal untuk tetap pada:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL sudah diubah ke\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Hapus Berkas",
"FILEIFRAMEFIELD.UNATTACHFILE": "Lepaskan Berkas",
"FILEIFRAMEFIELD.DELETEIMAGE": "Hapus Gambar",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Anda ingin menghapus berkas ini?",
"LeftAndMain.IncompatBrowserWarning": "Browser Anda tidak mendukung antarmuka CMS. Mohon gunakan Internet Explorer 7+, Google Chrome 10+ atau Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Terjadi kesalahan dalam menarik data dari server\n Mohon coba lagi nanti.",
"HtmlEditorField.SelectAnchor": "Pilih jangkar",
"UploadField.ConfirmDelete": "Anda ingin menghapus berkas ini dari sistem berkas server?",
"UploadField.PHP_MAXFILESIZE": "Ukuran berkas melebihi upload_max_filesize (direktif php.ini)",
"UploadField.HTML_MAXFILESIZE": "Ukuran berkas melebihi MAX_FILE_SIZE (direktif HTML form)",
"UploadField.ONLYPARTIALUPLOADED": "Berkas hanya sebagian terunggah",
"UploadField.NOFILEUPLOADED": "Tidak ada berkas terunggah",
"UploadField.NOTMPFOLDER": "Folder sementara tidak ditemukan",
"UploadField.WRITEFAILED": "Gagal menyimpan berkas",
"UploadField.STOPEDBYEXTENSION": "Ekstensi berkas yang diunggah tidak diijinkan",
"UploadField.TOOLARGE": "Ukuran berkas terlalu besar",
"UploadField.TOOSMALL": "Ukuran berkas terlalu kecil",
"UploadField.INVALIDEXTENSION": "Ekstensi berkas tidak diijinkan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Jumlah maksimal berkas sudah terlampaui",
"UploadField.UPLOADEDBYTES": "Berkas yang diunggah melebihi batas ukuran berkas",
"UploadField.EMPTYRESULT": "Pengunggahan menghasilkan berkas kosong",
"UploadField.LOADING": "Memuat...",
"UploadField.Editing": "Mengedit...",
"UploadField.Uploaded": "Terunggah",
"UploadField.OVERWRITEWARNING": "Berkas dengan nama sama sudah ada",
"TreeDropdownField.ENTERTOSEARCH": "Tekan Enter untuk pencarian",
"TreeDropdownField.OpenLink": "Buka",
"TreeDropdownField.FieldTitle": "Pilih",
"TreeDropdownField.SearchFieldTitle": "Pilih atau Cari"
}

View File

@ -19,7 +19,7 @@
"UploadField.ConfirmDelete": "Är du säker på att du vill radera denna fil från servern?",
"UploadField.PHP_MAXFILESIZE": "Filen överskrider upload_max_filesize (php-ini-direktiv)",
"UploadField.HTML_MAXFILESIZE": "Filen överskrider MAX_FILE_SIZE (HTML form-direktiv)",
"UploadField.ONLYPARTIALUPLOADED": "Filen laddas bara upp delvis",
"UploadField.ONLYPARTIALUPLOADED": "Filen laddades bara upp delvis",
"UploadField.NOFILEUPLOADED": "Ingen fil laddades upp",
"UploadField.NOTMPFOLDER": "Tillfällig mapp saknas",
"UploadField.WRITEFAILED": "Kunde inte skriva filen",

View File

@ -24,7 +24,7 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
"UploadField.ConfirmDelete": "Är du säker på att du vill radera denna fil från servern?",
"UploadField.PHP_MAXFILESIZE": "Filen överskrider upload_max_filesize (php-ini-direktiv)",
"UploadField.HTML_MAXFILESIZE": "Filen överskrider MAX_FILE_SIZE (HTML form-direktiv)",
"UploadField.ONLYPARTIALUPLOADED": "Filen laddas bara upp delvis",
"UploadField.ONLYPARTIALUPLOADED": "Filen laddades bara upp delvis",
"UploadField.NOFILEUPLOADED": "Ingen fil laddades upp",
"UploadField.NOTMPFOLDER": "Tillfällig mapp saknas",
"UploadField.WRITEFAILED": "Kunde inte skriva filen",

View File

@ -60,6 +60,8 @@ cs:
ERRORNOTREC: 'Toto uživatelské jméno / heslo nebylo rozpoznáno'
Boolean:
ANY: Jakkýkoliv
NOANSWER: 'Ne'
YESANSWER: 'Ano'
CMSLoadingScreen_ss:
LOADING: Nahrávání...
REQUIREJS: 'CMS vyžaduje, aby jste měli JavaScript zapnut.'
@ -78,6 +80,23 @@ cs:
EMAIL: E-mail
HELLO: Dobrý den
PASSWORD: Heslo
CheckboxField:
NOANSWER: 'Ne'
YESANSWER: 'Ano'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v seznamu. {value} není platná volba'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Zapomenuté heslo?'
BUTTONLOGIN: 'Přihlásit se zpět'
BUTTONLOGOUT: 'Odhlásit se'
PASSWORDEXPIRED: '<p>Vaše heslo expirovalo. <a target="_top" href="{link}">Prosím zvolte nové heslo.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Neplatný uživatel. <a target="_top" href="{link}">Prosím oveřte se zde</a> pro pokračování.</p>'
LoginMessage: '<p>Máte-li jakékoli neuložené práce, můžete se vrátit na místo, kde jste přestali, po přihlášení se zpět níže.</p>'
SUCCESS: Úspěch
SUCCESSCONTENT: '<p>Úspěšné přihlášení. Pokud nebudete automaticky přesměrován <a target="_top" href="{link}">klikněte sem</a></p>'
TimedOutTitleAnonymous: 'Čas Vašeho sezení vypršel.'
TimedOutTitleMember: 'Ahoj {name}!<br />Čas Vašeho sezení vypršel.'
ConfirmedPasswordField:
ATLEAST: 'Hesla musí být nejméně {min} znaků dlouhé.'
BETWEEN: 'Hesla musí být {min} až {max} znaků dlouhé.'
@ -124,6 +143,7 @@ cs:
DropdownField:
CHOOSE: (Vyberte)
CHOOSESEARCH: '(Vybrat nebo vyhledat)'
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v seznamu. {value} není platná volba'
EmailField:
VALIDATION: 'Prosím zadejte e-mailovou adresu'
Enum:
@ -171,7 +191,7 @@ cs:
TEXT2: 'odkaz na reset hesla'
TEXT3: pro
Form:
CSRF_FAILED_MESSAGE: "Zdá se, že nastal technický problém. Prosím, klikněte na tlačítko zpět, \n\t\t\t\t\taktualizujte prohlížeč a zkuste to znovu."
CSRF_FAILED_MESSAGE: 'Vypadá to, že to musí být technický problém. Kliněte prosím na tlačítko zpět, obnovte váš prohlížeč a zkuste opět.'
FIELDISREQUIRED: '{name} je požadováno'
SubmitBtnLabel: Jdi
VALIDATIONCREDITNUMBER: 'Prosím ujistěte se, že jste zadal/a {number} číslo kreditní karty správně'
@ -238,7 +258,7 @@ cs:
many_many_Members: Členové
GroupImportForm:
Help1: '<p>Import jedné nebo více skupin v <em>CSV</em> formátu (čárkou-oddělené hodnoty). <small><a href="#" class="toggle-advanced">Zobrazit rozšířené použití</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Pokročilé použití</h4>\n<ul>\n<li>Povolené sloupce: <em>%s</em></li>\n<li>Existující skupiny jsou porovnány jejich unikátní vlastností <em>Code</em>, a aktualizovány s novými hodnotami z\nimportovaného souboru.</li>\n<li>Hierarchie skupin může být tvořena použitím sloupce <em>ParentCode</em>.</li>\n<li>Kódy oprávnění mohou být přiřazeny sloupcem <em>PermissionCode</em>. Existující oprávnění nejsou smazána.</li>\n</ul>\n</div>"
Help2: "<div class=\"advanced\">\n\t<h4>Pokročilé použití</h4>\n\t<ul>\n\t<li>Povolené sloupce: <em>%s</em></li>\n\t<li>Existující skupiny jsou porovnány jejich unikátním <em>Code</em> hodnotou, a aktualizovány s novými hodnotami z\\nimportovaného souboru</li>\n\t<li>Hierarchie skupin může být tvořena použitím <em>ParentCode</em> sloupce.</li>\n\t<li>Kódy oprávnění mohou být přiřazeny <em>PermissionCode</em> sloupcem. Existující oprávnění nejsou smazána.</li>\n\t</ul>\n</div>"
ResultCreated: 'Vytvořeno {count} skupin'
ResultDeleted: 'Smazáno %d skupin'
ResultUpdated: 'Aktualizováno %d skupin'
@ -247,6 +267,8 @@ cs:
HtmlEditorField:
ADDURL: 'Přidat URL'
ADJUSTDETAILSDIMENSIONS: 'Detaily &amp; rozměry'
ANCHORSCANNOTACCESSPAGE: 'Nemáte povolen přístup k obsahu cílové stránky.'
ANCHORSPAGENOTFOUND: 'Cílová stránka nenelazena.'
ANCHORVALUE: Záložka (kotva)
BUTTONADDURL: 'Přidat url'
BUTTONINSERT: Vložit
@ -290,6 +312,7 @@ cs:
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' nemůže být vloženo do zdroje médií.'
UpdateMEDIA: 'Aktualizovat média'
SUBJECT: 'Předmět emailu'
Image:
PLURALNAME: Soubory
SINGULARNAME: Soubor
@ -318,6 +341,8 @@ cs:
LeftAndMain_Menu_ss:
Hello: Ahoj
LOGOUT: 'Odhlásit se'
ListboxField:
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v seznamu. {value} není platná volba'
LoginAttempt:
Email: 'Emailové adresy'
IP: 'IP adresy'
@ -350,6 +375,7 @@ cs:
NEWPASSWORD: 'Nové heslo'
NoPassword: 'Neni zde heslo pro tohoto člena'
PASSWORD: Heslo
PASSWORDEXPIRED: 'Vaše heslo expirovalo. Prosím, zvolte nové heslo.'
PLURALNAME: Členové
REMEMBERME: 'Zapamatovat si mě pro příště?'
SINGULARNAME: Člen
@ -456,8 +482,8 @@ cs:
SINGULARNAME: Role
Title: Název
PermissionRoleCode:
PLURALNAME: 'Kódy role oprávnění'
PermsError: 'Nelze připojit kód "%s" s privilegovanými právy (vyžaduje ADMIN přístup)'
PLURALNAME: 'Kódy role oprávnění'
SINGULARNAME: 'Kód role oprávnění'
Permissions:
PERMISSIONS_CATEGORY: 'Role a přístupová práva'
@ -557,3 +583,5 @@ cs:
UPLOADSINTO: 'uloží do /{path}'
Versioned:
has_many_Versions: Verze
CheckboxSetField:
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v seznamu. ''{value}'' není platná volba'

View File

@ -57,11 +57,11 @@ de:
BasicAuth:
ENTERINFO: 'Bitte geben Sie einen Nutzernamen und ein Passwort ein'
ERRORNOTADMIN: 'Dieser Nutzer ist kein Administrator'
ERRORNOTREC: 'Dieser/s Nutzername/Passwort wurde nicht erkannt'
ERRORNOTREC: 'Der Nutzername oder das Passwort wurden nicht erkannt'
Boolean:
ANY: alle
NOANSWER: "Nein"
YESANSWER: "Ja"
NOANSWER: 'Nein'
YESANSWER: 'Ja'
CMSLoadingScreen_ss:
LOADING: Lade Daten ...
REQUIREJS: 'Für die Benutzung des CMS wird JavaScript benötigt.'
@ -76,21 +76,38 @@ de:
MENUTITLE: 'Mein Profil'
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Sie haben Ihr Passwort geändert für'
CHANGEPASSWORDTEXT2: 'Sie können nun folgende Angaben benutzen um sich einzuloggen'
CHANGEPASSWORDTEXT2: 'Sie können nun folgende Angaben benutzen um sich einzuloggen:'
EMAIL: E-Mail
HELLO: Hallo
PASSWORD: Passwort
CheckboxField:
NOANSWER: 'Nein'
YESANSWER: 'Ja'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Bitte wählen Sie aus der Liste. {value} ist kein gültiger Wert'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Passwort vergessen?'
BUTTONLOGIN: 'Wieder einloggen'
BUTTONLOGOUT: 'Abmelden'
PASSWORDEXPIRED: '<p>Ihr Passwort ist abgelaufen. <a target="_top" href="{link}">Bitte wählen Sie ein neues Passwort.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Ungültiger Benutzer. <a target="_top" href="{link}">Bitte melden Sie sich hier an</a> um fortzufahren.</p>'
LoginMessage: '<p>Wenn Sie ungespeicherte Arbeiten haben, können Sie wieder weiterarbeiten indem Sie sich unterhalb einloggen.</p>'
SUCCESS: Erfolg
SUCCESSCONTENT: '<p>Login erfolgreich. Falls Sie nicht automatisch weitergeleitet werden, bitte <a target="_top" href="{link}">hier klicken</a></p>'
TimedOutTitleAnonymous: 'Ihre Sitzung ist abgelaufen.'
TimedOutTitleMember: 'Hallo {name}!<br />Ihre Sitzung ist abgelaufen.'
ConfirmedPasswordField:
ATLEAST: 'Passwörter müssen mindestens {min} Zeichen lang sein.'
BETWEEN: 'Passwörter müssen mindestens {min} bis maximal {max} Zeichen lang sein.'
BETWEEN: 'Passwörter müssen zwischen {min} und {max} Zeichen lang sein.'
MAXIMUM: 'Passwörter dürfen maxinal {max} Zeichen lang sein.'
SHOWONCLICKTITLE: 'Passwort ändern'
ContentController:
NOTLOGGEDIN: 'Nicht eingeloggt'
CreditCardField:
FIRST: Zuerst
FIRST: erste
FOURTH: vierte
SECOND: erste
SECOND: zweite
THIRD: dritte
CurrencyField:
CURRENCYSYMBOL:
@ -117,8 +134,8 @@ de:
NOTSET: 'nicht gesetzt'
TODAY: heute
VALIDDATEFORMAT2: 'Bitte geben sie das Datum im korrekten Format ein ({format})'
VALIDDATEMAXDATE: 'Ihr Datum muss nach dem erlaubtem Datum ({date}) liegen oder gleich sein'
VALIDDATEMINDATE: 'Ihr Datum muss vor dem erlaubtem Datum ({date}) liegen oder gleich sein'
VALIDDATEMAXDATE: 'Ihr Datum muss vor dem erlaubtem Datum ({date}) liegen oder gleich sein'
VALIDDATEMINDATE: 'Ihr Datum muss nach dem erlaubtem Datum ({date}) liegen oder gleich sein'
DatetimeField:
NOTSET: 'nicht gesetzt'
Director:
@ -126,6 +143,7 @@ de:
DropdownField:
CHOOSE: (Auswahl)
CHOOSESEARCH: '(Auswählen oder Suchen)'
SOURCE_VALIDATION: 'Bitte wählen Sie aus der Liste. {value} ist kein gültiger Wert'
EmailField:
VALIDATION: 'Bitte geben Sie eine E-Mail-Adresse ein'
Enum:
@ -148,7 +166,7 @@ de:
JsType: 'Javascript Datei'
Mp3Type: 'MP3 Audiodatei'
MpgType: 'MPEG Videodatei'
NOFILESIZE: 'Dateigröße ist 0 bytes'
NOFILESIZE: 'Dateigröße ist 0 Bytes'
NOVALIDUPLOAD: 'Datei ist kein gültiger Upload'
Name: Name
PLURALNAME: Dateien
@ -160,7 +178,7 @@ de:
TiffType: 'TIFF Bild - ideal für hohe Auflösungen'
Title: Titel
WavType: 'WAV Audiodatei'
XlsType: 'Exceltabelle'
XlsType: 'Excel Arbeitsmappe'
ZipType: 'ZIP komprimierte Datei'
Filesystem:
SYNCRESULTS: 'Synchronisation beendet: {createdcount} Objekte erstellt, {deletedcount} Objekte gelöscht'
@ -173,12 +191,12 @@ de:
TEXT2: 'Link zum Zurücksetzen des Passworts'
TEXT3: für
Form:
CSRF_FAILED_MESSAGE: "Es gab ein technisches Problem. Bitte versuchen Sie es erneut, nachdem sie die vorherige Seite neu geladen haben."
CSRF_FAILED_MESSAGE: 'Es gab ein technisches Problem. Bitte versuchen Sie es erneut, nachdem sie die vorherige Seite neu geladen haben.'
FIELDISREQUIRED: '{name} muss ausgefüllt werden'
SubmitBtnLabel: Los
VALIDATIONCREDITNUMBER: 'Bitte stellen Sie sicher, dass Sie die Kreditkartennummer ({number}) korrekt eingegeben haben'
VALIDATIONNOTUNIQUE: 'Der eingegebene Wert ist nicht einzigartig'
VALIDATIONPASSWORDSDONTMATCH: 'Passwörter stimmen nicht überein'
VALIDATIONPASSWORDSDONTMATCH: 'Die Passwörter stimmen nicht überein'
VALIDATIONPASSWORDSNOTEMPTY: 'Passwortfelder dürfen nicht leer sein'
VALIDATIONSTRONGPASSWORD: 'Passwörter müssen mindestens eine Zahl und ein alphanumerisches Zeichen enthalten'
VALIDATOR: Prüfer
@ -194,7 +212,7 @@ de:
GridField:
Add: '{name} hinzufügen'
Filter: Filter
FilterBy: 'Filter nach'
FilterBy: 'Filtern nach'
Find: Suchen
LEVELUP: 'Eine Ebene hoch'
LinkExisting: 'Bestehenden Datensatz verknüpfen'
@ -202,7 +220,7 @@ de:
NoItemsFound: 'Keine Elemente gefunden'
PRINTEDAT: 'Gedruckt am'
PRINTEDBY: 'Gedruckt von'
PlaceHolder: '{type} Suchen'
PlaceHolder: '{type} suchen'
PlaceHolderWithLabels: 'Suche {type} über {name}'
RelationSearch: 'Relationssuche'
ResetFilter: Zurücksetzen
@ -228,7 +246,7 @@ de:
DefaultGroupTitleContentAuthors: 'Inhaltsautoren'
Description: Beschreibung
GroupReminder: 'Diese Gruppe übernimmt automatisch die Rollen der Elterngruppe'
HierarchyPermsError: 'Kann Berechtigungen der Eltern-Gruppe "%s" nicht hinzufügen (erfordert ADMIN Rechte)'
HierarchyPermsError: 'Kann Berechtigungen der Eltern-Gruppe "%s" nicht hinzufügen (erfordert Administratorrechte)'
Locked: 'Gesperrt?'
NoRoles: 'Keine Rollen gefunden'
PLURALNAME: Gruppen
@ -240,15 +258,17 @@ de:
many_many_Members: Mitglieder
GroupImportForm:
Help1: '<p>Eine oder mehrere Gruppen im <em>CSV</em>-Format (kommaseparierte Werte) importieren. <small><a href="#" class="toggle-advanced">Erweiterte Nutzung</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Erweiterte Benutzung</h4>\n<ul>\n<li>Gültige Spalten: <em>%s</em></li>\n<li>Bereits existierende Gruppen werden anhand ihres eindeutigen <em>Code</em> identifiziert und um neue Einträge aus der Importdatei erweitert.</li>\n<li>Berechtigungen können in der Spalte <em>PermissioinCode</em> hinzugefügt werden. Schon zugewiesene Berechtigungen werden nicht entfernt.</li>\n</ul>\n</div>"
Help2: "<div class=\"advanced\">\\n<h4>Erweiterte Benutzung</h4>\\n<ul>\\n<li>Gültige Spalten: <em>%s</em></li>\\n<li>Bereits existierende Gruppen werden anhand ihres eindeutigen <em>Code</em> identifiziert und um neue Einträge aus der Importdatei erweitert.</li>\\n\n<li>Hierarchien von Gruppen können über die Spalte <em>ParentCode</em> definiert werden.</li>\n<li>Berechtigungen können in der Spalte <em>PermissionCode</em> hinzugefügt werden. Schon zugewiesene Berechtigungen werden nicht entfernt.</li>\\n</ul>\\n</div>"
ResultCreated: '{count} Gruppe(n) wurden erstellt'
ResultDeleted: '%d Gruppen gelöscht'
ResultUpdated: '%d Gruppen aktualisiert'
ResultDeleted: '%d Gruppe(n) gelöscht'
ResultUpdated: '%d Gruppe(n) aktualisiert'
Hierarchy:
InfiniteLoopNotAllowed: 'Es wurde eine Endlosschleife innerhalb der "{type}"-Hierarchie gefunden. Bitte ändern Sie die übergeordnete Seite, um den Fehler zu beheben'
HtmlEditorField:
ADDURL: 'URL hinzufügen'
ADJUSTDETAILSDIMENSIONS: 'Details &amp; Dimensionen'
ANCHORSCANNOTACCESSPAGE: 'Sie haben keine Berechtigungen, den Inhalt dieser Seite zu sehen.'
ANCHORSPAGENOTFOUND: 'Zielseite nicht gefunden'
ANCHORVALUE: Anker
BUTTONADDURL: 'URL hinzufügen'
BUTTONINSERT: Einfügen
@ -256,7 +276,7 @@ de:
BUTTONREMOVELINK: 'Verweise entfernen'
BUTTONUpdate: Aktualisieren
CAPTIONTEXT: 'Beschriftungstext'
CSSCLASS: 'Ausrichtung/Stil'
CSSCLASS: 'Ausrichtung / Stil'
CSSCLASSCENTER: 'Zentriert, selbstständig'
CSSCLASSLEFT: 'Links, mit umfließendem Text'
CSSCLASSLEFTALONE: 'Links, alleinstehend'
@ -279,7 +299,7 @@ de:
IMAGETITLETEXTDESC: 'Weiterführende Informationen über das Bild'
IMAGEWIDTHPX: Breite (px)
INSERTMEDIA: 'Medienobjekt einfügen'
LINK: 'Verweis'
LINK: 'Verweis einfügen'
LINKANCHOR: 'Anker auf dieser Seite'
LINKDESCR: 'Beschreibung des Verweises'
LINKEMAIL: 'E-Mail-Adresse'
@ -292,6 +312,7 @@ de:
URL: URL
URLNOTANOEMBEDRESOURCE: 'Die URL ''{url}'' konnte nicht in eine Medienquelle umgewandelt werden'
UpdateMEDIA: 'Medienobjekt aktualisieren'
SUBJECT: 'E-Mail-Betreff'
Image:
PLURALNAME: Dateien
SINGULARNAME: Datei
@ -320,6 +341,8 @@ de:
LeftAndMain_Menu_ss:
Hello: Hallo
LOGOUT: 'Abmelden'
ListboxField:
SOURCE_VALIDATION: 'Bitte wählen Sie aus der Liste. {value} ist kein gültiger Wert'
LoginAttempt:
Email: 'E-Mail-Adresse'
IP: 'IP-Adresse'
@ -330,12 +353,12 @@ de:
ADDGROUP: 'Gruppe hinzufügen'
BUTTONCHANGEPASSWORD: 'Passwort ändern'
BUTTONLOGIN: 'Einloggen'
BUTTONLOGINOTHER: 'Als jemand anders einloggen'
BUTTONLOGINOTHER: 'Als jemand anderes einloggen'
BUTTONLOSTPASSWORD: 'Ich habe mein Passwort vergessen'
CANTEDIT: 'Sie haben keine Rechte, dies zu tun'
CONFIRMNEWPASSWORD: 'Neues Passwort bestätigen'
CONFIRMPASSWORD: 'Passwort bestätigen'
DATEFORMAT: 'Bitte geben sie das Datum im korrekten Format ein ({format})'
DATEFORMAT: 'Datumsformat'
DefaultAdminFirstname: 'Standardadmin'
DefaultDateTime: Standard
EMAIL: E-Mail
@ -352,14 +375,15 @@ de:
NEWPASSWORD: 'Neues Passwort'
NoPassword: 'Dieser Benutzer hat kein Passwort.'
PASSWORD: Passwort
PASSWORDEXPIRED: 'Ihr Passwort ist abgelaufen. Bitte wählen Sie ein neues Passwort.'
PLURALNAME: Benutzer
REMEMBERME: 'Für das nächste Mal merken?'
SINGULARNAME: Benutzer
SUBJECTPASSWORDCHANGED: 'Ihr Passwort wurde geändert'
SUBJECTPASSWORDRESET: 'Ihr Link zur Passwortrücksetzung'
SURNAME: Nachname
TIMEFORMAT: 'Bitte geben Sie die Uhrzeit im korrekten Format ein ({format})'
VALIDATIONMEMBEREXISTS: 'Es gibt bereits ein Mitglied mit dieser E-Mail-Adresse'
TIMEFORMAT: 'Uhrzeitformat'
VALIDATIONMEMBEREXISTS: 'Es gibt bereits ein Mitglied mit dem/der selben %s'
ValidationIdentifierFailed: 'Das vorhandene Mitglied #{id} mit identischer Bezeichnung kann nicht überschrieben werden ({name} = {value}))'
WELCOMEBACK: 'Hallo {firstname}. Schön, dass du wieder da bist'
YOUROLDPASSWORD: 'Ihr altes Passwort'
@ -373,30 +397,30 @@ de:
MemberAuthenticator:
TITLE: 'E-Mail &amp; Passwort'
MemberDatetimeOptionsetField:
AMORPM: 'AM (vormittag) oder PM (nachmittag)'
AMORPM: 'AM (vormittags) oder PM (nachmittags)'
Custom: Benutzerdefiniert
DATEFORMATBAD: 'Das Datumsformat ist ungültig'
DAYNOLEADING: 'Tag ohne führende Null'
DAYNOLEADING: 'Tag, ohne führende Null'
DIGITSDECFRACTIONSECOND: 'Eine oder mehrere Ziffern, die einen Dezimalbruch einer Sekunde darstellen'
FOURDIGITYEAR: 'Vierstellige Jahreszahl'
FULLNAMEMONTH: 'Volle Monatsbezeichnung (z.B. Juni)'
HOURNOLEADING: 'Stunde ohne führende Null'
MINUTENOLEADING: 'Minute'
MONTHNOLEADING: 'Monat ohne führende Null'
HOURNOLEADING: 'Stunde, ohne führende Null'
MINUTENOLEADING: 'Minute, ohne führende Null'
MONTHNOLEADING: 'Monat, ohne führende Null'
Preview: Vorschau
SHORTMONTH: 'Kurzname des Monats (z.B. Jun)'
TWODIGITDAY: 'Tag des Monats mit führender Null'
TWODIGITHOUR: 'Stunde mit führenden Nullen'
TWODIGITMINUTE: 'Minute mit führenden Nullen'
TWODIGITMONTH: 'Monat mit führender Null (z.B. 01 = Januar, usw.)'
TWODIGITSECOND: 'Sekunde'
TWODIGITDAY: 'Tag des Monats, mit führender Null'
TWODIGITHOUR: 'Stunde, mit führender Null (00 bis 23)'
TWODIGITMINUTE: 'Minute, mit führender Null (00 bis 59)'
TWODIGITMONTH: 'Monat, mit führender Null (z.B. 01 = Januar, usw.)'
TWODIGITSECOND: 'Sekunde, mit führender Null (00 bis 59)'
TWODIGITYEAR: 'Zweistellige Jahreszahl'
Toggle: 'Hilfe zur Formatierung anzeigen'
MemberImportForm:
Help1: '<p>Mitglieder im <em>CSV</em>-Format (kommaseparierte Werte) importieren. <small><a href="#" class="toggle-advanced">Erweiterte Nutzung</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Erweiterte Benutzung</h4>\n<ul>\n<li>Gültige Spalten: <em>%s</em></li>\n<li>Bereits existierende Benutzer werden anhand ihres eindeutigen <em>Code</em> identifiziert und um neue Einträge aus der Importdatei erweitert.</li>\n<li>Gruppen können in der Spalte <em>Gruppen</em> hinzugefügt werden. Gruppen werden anhand ihres <em>Code</em> erkannt. Mehrere Gruppen werden Komma-separiert eingetragen. Schon zugewiesene Gruppen werden nicht entfernt.</li>\n</ul>\n</div>"
ResultCreated: '{count} Mitglied(er) wurden erstellt'
ResultDeleted: '%d Mitglieder gelöscht'
ResultCreated: '{count} Mitglied(er) wurde(n) erstellt'
ResultDeleted: '%d Mitglied(er) gelöscht'
ResultNone: 'Keine Änderungen'
ResultUpdated: '{count} Mitglied(er) wurde(n) aktualisiert'
MemberPassword:
@ -411,7 +435,7 @@ de:
IMPORT: 'CSV Import'
IMPORTEDRECORDS: '{count} Einträge wurden importiert'
NOCSVFILE: 'Wählen Sie eine CSV-Datei zum Importieren'
NOIMPORT: 'Kein Import notwendig.'
NOIMPORT: 'Kein Import notwendig'
RESET: Zurücksetzen
Title: 'Datenmodelle'
UPDATEDRECORDS: '{count} Einträge wurden aktualisiert'
@ -458,8 +482,8 @@ de:
SINGULARNAME: Rolle
Title: Titel
PermissionRoleCode:
PermsError: 'Kann Berechtigungen dem Code "%s" nicht hinzufügen (erfordert Administratorrechte)'
PLURALNAME: 'Berechtigungsrollencodes'
PermsError: 'Kann Berechtigungen dem Code "%s" nicht hinzufügen (erfordert ADMIN Rechte)'
SINGULARNAME: 'Berechtigungsrollencode'
Permissions:
PERMISSIONS_CATEGORY: 'Rollen und Zugriffsberechtigungen'
@ -473,14 +497,14 @@ de:
CHANGEPASSWORDHEADER: 'Passwort ändern'
ENTERNEWPASSWORD: 'Bitte geben Sie ein neues Passwort ein'
ERRORPASSWORDPERMISSION: 'Sie müssen eingeloggt sein, um Ihr Passwort ändern zu können!'
LOGGEDOUT: 'Sie wurden ausgeloggt. Wenn Sie sich wieder einloggen möchten, geben Sie bitte unten Ihre Zugangsdaten ein.'
LOGGEDOUT: 'Sie wurden ausgeloggt. Wenn Sie sich wieder einloggen möchten, geben Sie bitte unterhalb Ihre Zugangsdaten ein.'
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.'
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.'
SecurityAdmin:
ACCESS_HELP: 'Benutzer hinzufügen, anzeigen und editieren sowie diesen Berechtigungen und Rollen zuweisen.'
APPLY_ROLES: 'Rollen zu Gruppen zuweisen'
@ -500,7 +524,7 @@ de:
TABROLES: Rollen
Users: Benutzer
SecurityAdmin_MemberImportForm:
BtnImport: 'Import'
BtnImport: 'Importieren'
FileFieldLabel: 'CSV Datei <small>(Erlaubte Dateierweiterung: *.csv)</small>'
SilverStripeNavigator:
Auto: Automatisch
@ -549,13 +573,15 @@ de:
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'
MAXNUMBEROFFILESONE: 'SIe können maximal eine Datei hochladen'
MAXNUMBEROFFILESSHORT: 'SIe können maximal {count} Datei(en) hochladen'
MAXNUMBEROFFILESONE: 'Sie können maximal eine Datei hochladen'
MAXNUMBEROFFILESSHORT: 'Sie können maximal {count} Datei(en) hochladen'
OVERWRITEWARNING: 'Eine Datei mit dem selben Namen existiert bereits'
REMOVE: Entfernen
REMOVEINFO: 'Diese Datei entfernen aber nicht am Server löschen'
REMOVEINFO: 'Diese Datei entfernen, aber nicht am Server löschen'
STARTALL: 'Alle starten'
Saved: Gespeichert
UPLOADSINTO: 'speichert nach /{path}'
Versioned:
has_many_Versions: Versionen
CheckboxSetField:
SOURCE_VALIDATION: 'Bitte wählen Sie aus der Liste. {value} ist kein gültiger Wert'

View File

@ -60,6 +60,8 @@ eo:
ERRORNOTREC: 'Kiuj salutnomo / pasvorto ne estas rekonebla'
Boolean:
ANY: Ajna
NOANSWER: 'Ne'
YESANSWER: 'Jes'
CMSLoadingScreen_ss:
LOADING: Ŝargas...
REQUIREJS: 'La CMS bezonas ke vi enŝaltis Ĝavaskripton.'
@ -78,6 +80,23 @@ eo:
EMAIL: Retpoŝto
HELLO: Saluton
PASSWORD: Pasvorto
CheckboxField:
NOANSWER: 'Ne'
YESANSWER: 'Jes'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Bonvolu elekti valoron el la listo donita. {value} ne estas valida agordo'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Ĉu forgesis pasvorton?'
BUTTONLOGIN: 'Ree ensaluti'
BUTTONLOGOUT: 'Adiaŭi'
PASSWORDEXPIRED: '<p>Via pasvorto finiĝis. <a target="_top" href="{link}">Bonvolu elekti novan.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Nevalida uzanto. <a target="_top" href="{link}">Bonvolu aŭtentigi ĉi tie</a> por daŭrigi.</p>'
LoginMessage: '<p>Se vi havas nekonservitan laboraĵon vi povos reveni al kie vi paŭzis reensalutante sube.</p>'
SUCCESS: Sukseso
SUCCESSCONTENT: '<p>Ensaluto suksesis. Se vi ne aŭtomate alidirektiĝos, <a target="_top" href="{link}">alklaku ĉi tie</a></p>'
TimedOutTitleAnonymous: 'Via seanco eltempiĝis.'
TimedOutTitleMember: 'He, {name}!<br />Via seanco eltempiĝis.'
ConfirmedPasswordField:
ATLEAST: 'Pasvorto devas esti almenaŭ {min} signojn longa.'
BETWEEN: 'Pasvorto devas esti inter {min} kaj {max} signojn longa.'
@ -124,6 +143,7 @@ eo:
DropdownField:
CHOOSE: (Elekti)
CHOOSESEARCH: '(Elekti aŭ serĉi)'
SOURCE_VALIDATION: 'Bonvolu elekti valoron el la listo donita. {value} ne estas valida agordo'
EmailField:
VALIDATION: 'Bonvolu enigi readreson'
Enum:
@ -171,7 +191,7 @@ eo:
TEXT2: 'pasvorta reagorda ligilo'
TEXT3: por
Form:
CSRF_FAILED_MESSAGE: "Ŝajne okazis teknika problemo. Bonvole alklaku la retrobutonon, \n\t\t\t\t\taktualigu vian foliumilon, kaj reprovu."
CSRF_FAILED_MESSAGE: 'Ŝajne okazis teknika problemo. Bonvolu alklaki la retrobutonon, refreŝigi vian foliumilon, kaj reprovi.'
FIELDISREQUIRED: '{name} estas bezonata'
SubmitBtnLabel: Iri
VALIDATIONCREDITNUMBER: 'Bonvole certigu ke vi ĝuste enigis la kreditkarton {number}'
@ -238,7 +258,7 @@ eo:
many_many_Members: Membroj
GroupImportForm:
Help1: '<p>Importi unu aŭ pliaj grupojn en formato <em>CSV</em> (perkome disigitaj valoroj values). <small><a href="#" class="toggle-advanced">Vidigi spertulan uzadon</a></small></p>'
Help2: "<div class=\"advanced\">\n⇥<h4>Spertula uzado</h4>\n⇥<ul>\n⇥<li>Permesitaj kolumnoj: <em>%s</em></li>\n⇥<li>Ekzistantaj grupoj pariĝas per ilia unika atributo <em>Kodo</em>, kaj aktualiĝas per eventualaj valoroj el \n⇥la importita dosiero</li>\n⇥<li>Grupaj hierarkioj kreiĝas per kolumno <em>PraKodo</em>.</li>\n⇥<li>Permeskodoj estas agordeblaj per la kolumno <em>PermesKodo</em>. Ekzistantaj permeskodoj \n⇥ne nuliĝas.</li>\n⇥</ul>\n</div>"
Help2: "<div class=\"advanced\">\n\t<h4>Speciala uzado</h4>\n\t<ul>\n\t<li>Permesitaj kolumnoj: <em>%s</em></li>\n\t<li>Ekzistantaj grupoj kongruiĝas laŭ ilia unika valoro <em>Kodo</em>, kaj aktualiĝas per eventualaj novaj valoroj el la importita dosiero</li>\n\t<li>Grupaj hierarkioj estas kreeblaj uzante kolumnon <em>PraKodo</em>.</li>\n\t<li>Permesaj kodoj estas agordeblaj de la kolumno <em>PermesKodo</em>. Ekzistantaj permesaj kodoj ne vakiĝos.</li>\n\t</ul>\n</div>"
ResultCreated: 'Kreiĝis {count} grupoj'
ResultDeleted: 'Forigis %d grupojn'
ResultUpdated: 'Aktualigis %d grupojn'
@ -247,6 +267,8 @@ eo:
HtmlEditorField:
ADDURL: 'Aldoni je URL'
ADJUSTDETAILSDIMENSIONS: 'Detaloj kaj dimensioj'
ANCHORSCANNOTACCESSPAGE: 'Vi ne rajtas aliri la enhavon de la cela paĝo.'
ANCHORSPAGENOTFOUND: 'Ne trovis la celan paĝon.'
ANCHORVALUE: Ankri
BUTTONADDURL: 'Aldoni je url'
BUTTONINSERT: Enmeti
@ -290,6 +312,7 @@ eo:
URL: URL
URLNOTANOEMBEDRESOURCE: 'La URL ''{url}'' ne estas konvertebla al memorilo.'
UpdateMEDIA: 'Ĝisdatigi memorilon'
SUBJECT: 'Temo de retpoŝto'
Image:
PLURALNAME: Dosieroj
SINGULARNAME: Dosiero
@ -307,7 +330,7 @@ eo:
PERMAGAIN: 'Vin adiaŭis la CMS. Se vi volas denove saluti, enigu salutnomon kaj pasvorton malsupre.'
PERMALREADY: 'Bedaŭrinde vi ne povas aliri tiun parton de la CMS. Se vi volas saluti kiel iu alia, tiel faru sube'
PERMDEFAULT: 'Enigi vian retadreson kaj pasvorton por aliri al la CMS.'
PLEASESAVE: 'Bonvolu konservi paĝon: Ne eblis ĝisdatigi ĉi tiun paĝon ĉar ĝi ankoraŭ ne estas konservita.'
PLEASESAVE: 'Bonvolu konservi paĝon: ne povis ĝisdatigi ĉi tiun paĝon ĉar ĝi ankoraŭ estas nekonservita.'
PreviewButton: Antaŭvido
REORGANISATIONSUCCESSFUL: 'Sukcese reorganizis la retejan arbon.'
SAVEDUP: Konservita.
@ -318,6 +341,8 @@ eo:
LeftAndMain_Menu_ss:
Hello: Saluton
LOGOUT: 'Elsaluti'
ListboxField:
SOURCE_VALIDATION: 'Bonvolu elekti valoron el la listo donita. {value} ne estas valida agordo'
LoginAttempt:
Email: 'Retadreso'
IP: 'IP-Adreso'
@ -350,6 +375,7 @@ eo:
NEWPASSWORD: 'Novan pasvorton'
NoPassword: 'Mankas pasvorto por ĉi tiu membro.'
PASSWORD: Pasvorto
PASSWORDEXPIRED: 'Via pasvorto finiĝis. Bonvolu elekti novan.'
PLURALNAME: Membroj
REMEMBERME: 'Memoru min je la sekva fojo?'
SINGULARNAME: Membro
@ -456,8 +482,8 @@ eo:
SINGULARNAME: Rolo
Title: Titolo
PermissionRoleCode:
PLURALNAME: 'Permesrolaj kodoj'
PermsError: 'Ne povas agordi kodon "%s" kun privilegiaj permesoj (bezonas ADMIN-aliron)'
PLURALNAME: 'Permesrolaj kodoj'
SINGULARNAME: 'Permesrola kodo'
Permissions:
PERMISSIONS_CATEGORY: 'Roloj kaj aliraj permesoj'
@ -557,3 +583,5 @@ eo:
UPLOADSINTO: 'konservas en /{path}'
Versioned:
has_many_Versions: Versioj
CheckboxSetField:
SOURCE_VALIDATION: 'Bonvolu elekti valoron el la listo donita. {value} ne estas valida agordo'

View File

@ -60,8 +60,8 @@ fi:
ERRORNOTREC: 'Tätä käyttäjänimeä/salasanaa ei tunnistettu.'
Boolean:
ANY: Yhtään
NOANSWER: "Ei"
YESANSWER: "Kyllä"
NOANSWER: 'Ei'
YESANSWER: 'Kyllä'
CMSLoadingScreen_ss:
LOADING: Ladataan...
REQUIREJS: 'CMS-järjestelmä vaatii, että selaimessasi on JavaSkriptit päällä.'
@ -80,6 +80,23 @@ fi:
EMAIL: Sähköposti
HELLO: Hei
PASSWORD: Salasana
CheckboxField:
NOANSWER: 'Ei'
YESANSWER: 'Kyllä'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Valitse arvo annetuista vaihtoehdoista. {value} ei kelpaa'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Unohditko salasanasi?'
BUTTONLOGIN: 'Kirjaudu takaisin sisään'
BUTTONLOGOUT: 'Kirjaudu ulos'
PASSWORDEXPIRED: '<p>Salasanasi on vanhentunut. <a target="_top" href="{link}">Valitse uusi.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Virheellinen käyttäjä. <a target="_top" href="{link}">Ole hyvä ja tunnistaudu uudelleen</a> jatkaaksesi.</p>'
LoginMessage: '<p>Mikäli tallennus jäi tekemättä, voit kirjautua uudelleen ja jatkaa muokkausta.</p>'
SUCCESS: Onnistui
SUCCESSCONTENT: '<p>Kirjautuminen onnistui. Mikäli automaattinen edelleenohjaus ei toimi <a target="_top" href="{link}">klikkaa tästä</a></p>'
TimedOutTitleAnonymous: 'Istuntosi on vanhentunut.'
TimedOutTitleMember: 'Hei {name}!<br />Istuntosi on vanhentunut.'
ConfirmedPasswordField:
ATLEAST: 'Salasanan on oltava vähintään {min} merkkiä pitkä.'
BETWEEN: 'Salasanan on oltava väh. {min} ja enintään {max} merkkiä pitkä.'
@ -126,6 +143,7 @@ fi:
DropdownField:
CHOOSE: (Valitse)
CHOOSESEARCH: '(Valitse tai Hae)'
SOURCE_VALIDATION: 'Valitse arvo pudotusvalikosta. {value} ei kelpaa'
EmailField:
VALIDATION: 'Anna sähköpostiosoite, ole hyvä.'
Enum:
@ -173,7 +191,7 @@ fi:
TEXT2: 'salasanan vaihtolinkki'
TEXT3: henkilölle
Form:
CSRF_FAILED_MESSAGE: "On ilmeisesti tapahtunut tekninen virhe. Klikkaa selaimesi Takaisin-nappia, päivitä sivu painamalla F5-näppäintä ja yritä uudelleen."
CSRF_FAILED_MESSAGE: 'On ilmeisesti tapahtunut tekninen virhe. Klikkaa selaimesi Takaisin-nappia, päivitä sivu ja yritä uudelleen.'
FIELDISREQUIRED: '{name} on pakollinen'
SubmitBtnLabel: Siirry
VALIDATIONCREDITNUMBER: 'Tarkista, ovatko antamasi luottokortin numerot ({number}) oikein'
@ -240,7 +258,8 @@ fi:
many_many_Members: Jäsenet
GroupImportForm:
Help1: '<p>Tuo yksi tai useampi ryhmä <em>CSV</em>-muotoisena (arvot pilkulla erotettuina). <small><a href="#" class="toggle-advanced">Näytä edistyksellinen käyttö</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Edistynyt käyttö</h4>\n<ul>\n<li>Sallitut palstat: <em>%s</em></li>\n<li>Olemassa olevat ryhmät kohdistetaan niiden uniikin <em>Code</em>-arvolla, ja päivitetään uudet arvot tuodusta tiedostosta.</li>\n<li>Ryhmien hierarkiat voidaan luoda <em>ParentCode</em>-palstalla.</li>\n<li>Oikeustasot voidaan kohdistaa <em>PermissionCode</em>-palstalla. Olemassa olevia oikeustasoja ei poisteta.</li>\n</ul>\n</div>"
Help2: "<div class=\"advanced\">\n\t<h4>Edistynyt käyttö</h4>\n\t<ul>\n\t<li>Sallitut palstat: <em>%s</em></li>\n\t<li>Olemassa olevat ryhmät kohdistetaan niiden uniikin <em>Code</em> arvolla, ja päivitetään uudet arvot tuodusta tiedostosta</li>\n\t<li>Oikeustasot voidaan luoda käyttämällä <em>ParentCode</em> palstaa.</li>\n\t<li>Oikeustasokoodit voidaan kohdistaa <em>PermissionCode</em> palstassa. Olemassaolevia oikeusia ei tyhjennetä.</li>\n\t</ul>\n\
</div>"
ResultCreated: 'Luotiin {count} ryhmä(ä)'
ResultDeleted: 'Poistettu %d ryhmää'
ResultUpdated: 'Päivitetty %d ryhmää'
@ -249,6 +268,8 @@ fi:
HtmlEditorField:
ADDURL: 'Lisää URL-osoite'
ADJUSTDETAILSDIMENSIONS: 'Tarkat tiedot &amp; mitat'
ANCHORSCANNOTACCESSPAGE: 'Sinulla ei ole oikeuksia tarkastella tämän sivun sisältöä.'
ANCHORSPAGENOTFOUND: 'Kohdesivua ei löytynyt.'
ANCHORVALUE: Ankkuri
BUTTONADDURL: 'Lisää URL-osoite'
BUTTONINSERT: Liitä
@ -292,6 +313,7 @@ fi:
URL: URL-osoite
URLNOTANOEMBEDRESOURCE: 'URL-osoitetteesta ''{url}'' ei voitu liittää mediaa'
UpdateMEDIA: 'Päivitä media'
SUBJECT: 'Sähköpostin aihe'
Image:
PLURALNAME: Tiedostot
SINGULARNAME: Tiedosto
@ -309,7 +331,7 @@ fi:
PERMAGAIN: 'Olet kirjautunut ulos CMS:stä. Jos haluat kirjautua uudelleen sisään, syötä käyttäjätunnuksesi ja salasanasi alla.'
PERMALREADY: 'Paihoittelut, mutta et pääse tähän osaan CMS:ää. Jos haluat kirjautua jonain muuna, voit tehdä sen alla.'
PERMDEFAULT: 'Valitse tunnistustapa ja syötä tunnistetietosi CMS:ään.'
PLEASESAVE: 'Ole hyvä ja tallenna sivu: tätä sivua ei voitu päivittää, koska sitä ei ole vielä tallennettu.'
PLEASESAVE: 'Tallenna sivu: tätä sivua ei voida päivittää, koska sitä ei ole vielä tallennettu.'
PreviewButton: Esikatselu
REORGANISATIONSUCCESSFUL: 'Hakemistopuu järjestettiin uudelleen onnistuneesti.'
SAVEDUP: Tallennettu.
@ -320,6 +342,8 @@ fi:
LeftAndMain_Menu_ss:
Hello: Hei
LOGOUT: 'Kirjaudu ulos'
ListboxField:
SOURCE_VALIDATION: 'Valitse arvo annetuista vaihtoehdoista. {value} ei kelpaa.'
LoginAttempt:
Email: 'Sähköpostiosoite'
IP: 'IP-osoite'
@ -352,6 +376,7 @@ fi:
NEWPASSWORD: 'Uusi salasana'
NoPassword: 'Tällä käyttäjällä ei ole salasanaa'
PASSWORD: Salasana
PASSWORDEXPIRED: 'Salasanasi on vanhentunut. Ole hyvä ja valitse uusi.'
PLURALNAME: Jäsenet
REMEMBERME: 'Muista seuraavalla kerralla?'
SINGULARNAME: Jäsen
@ -458,8 +483,8 @@ fi:
SINGULARNAME: Rooli
Title: Roolin nimi
PermissionRoleCode:
PLURALNAME: 'Käyttöoikeiden roolin koodit'
PermsError: 'Ei voida asettaa koodia "%s" annetuilla oikeuksilla (vaaditaan JÄRJESTELMÄNVALVOJAN oikeudet)'
PLURALNAME: 'Käyttöoikeuden roolin koodit'
SINGULARNAME: 'Käyttöoikeiden roolin koodi'
Permissions:
PERMISSIONS_CATEGORY: 'Roolit ja käyttöoikeudet'
@ -559,3 +584,5 @@ fi:
UPLOADSINTO: 'tallentuu polkuun: /{path}'
Versioned:
has_many_Versions: Versiot
CheckboxSetField:
SOURCE_VALIDATION: 'Valitse arvo annetuista vaihtoehdoista. ''{value}'' ei kelpaa'

View File

@ -1,211 +1,450 @@
id:
AssetAdmin:
NEWFOLDER: Berkas baru
NEWFOLDER: FolderBaru
SHOWALLOWEDEXTS: 'Tampilkan ekstensi yang dibolehkan'
AssetTableField:
CREATED: 'Pertama diunggah'
DIM: Dimensi
FILENAME: Nama berkas
FOLDER: Map
FOLDER: Folder
LASTEDIT: 'Terakhir diubah'
OWNER: Pemilik
SIZE: 'Ukuran berkas'
TITLE: Judul
TYPE: 'Jenis berkas'
URL: URL
AssetUploadField:
EDITALL: 'Ubah seluruhnya'
EDITANDORGANIZE: 'Ubah dan atur'
EDITINFO: 'Ubah berkas'
ChooseFiles: 'Pilih berkas'
DRAGFILESHERE: 'Tarik berkas ke sini'
DROPAREA: 'Area Taruh'
EDITALL: 'Edit semua'
EDITANDORGANIZE: 'Edit & kelola'
EDITINFO: 'Edit berkas'
FILES: Berkas
FROMCOMPUTER: 'Pilih berkas dari komputer Anda'
FROMCOMPUTERINFO: 'Unggah dari komputer Anda'
TOTAL: Total
TOUPLOAD: 'Pilih berkas untuk diunggah...'
UPLOADINPROGRESS: 'Mohon tunggu... sedang mengunggah'
UPLOADOR: ATAU
BBCodeParser:
ALIGNEMENT: Penjajaran barisan
ALIGNEMENTEXAMPLE: 'jajar kanan'
ALIGNEMENT: Perataan
ALIGNEMENTEXAMPLE: 'rata kanan'
BOLD: 'Teks Tebal'
BOLDEXAMPLE: Tebal
CODE: 'Blok Kode'
CODEDESCRIPTION: 'Kode blok yang tidak diformat'
CODEDESCRIPTION: 'Blok kode tanpa format'
CODEEXAMPLE: 'Blok kode'
COLORED: 'Teks berwarna'
COLOREDEXAMPLE: 'teks biru'
EMAILLINK: 'Link email'
EMAILLINKDESCRIPTION: 'Membuat link ke alamat email'
EMAILLINK: 'Tautan email'
EMAILLINKDESCRIPTION: 'Buat tautan ke alamat email'
IMAGE: Gambar
IMAGEDESCRIPTION: 'Tampilkan gambar di pos anda'
IMAGEDESCRIPTION: 'Tampilkan gambar pada entri'
ITALIC: 'Teks Miring'
ITALICEXAMPLE: Miring
LINK: 'Link situs web'
LINKDESCRIPTION: 'Link ke situs web atau URL lain'
STRUCK: 'Text distrip'
STRUCKEXAMPLE: Distrip
UNDERLINE: 'Teks Dengan Garis Bawah'
UNDERLINEEXAMPLE: Digaris bawahi
UNORDERED: 'Daftar tak berurut'
UNORDEREDDESCRIPTION: 'Daftar tak berurut'
UNORDEREDEXAMPLE1: 'barang 1'
LINK: 'Tautan situs'
LINKDESCRIPTION: 'Tautan ke situs atau URL lain'
STRUCK: 'Teks Coret'
STRUCKEXAMPLE: Coret
UNDERLINE: 'Teks Garis Bawah'
UNDERLINEEXAMPLE: Garis Bawah
UNORDERED: 'daftar acak'
UNORDEREDDESCRIPTION: 'Daftar acak'
UNORDEREDEXAMPLE1: 'Item acak 1'
BackLink_Button_ss:
Back: Kembali
BasicAuth:
ENTERINFO: 'Harap masukkan username dan password.'
ERRORNOTADMIN: 'User tersebut bukan administrator.'
ERRORNOTREC: 'Username/password tidak dikenali'
ENTERINFO: 'Mohon isikan nama pengguna dan kata kunci.'
ERRORNOTADMIN: 'Pengguna tersebut bukan pengelola.'
ERRORNOTREC: 'Nama pengguna dan kata kunci tidak dikenal'
Boolean:
ANY: Semua
NOANSWER: 'Tidak'
YESANSWER: 'Ya'
CMSLoadingScreen_ss:
LOADING: Memuat...
REQUIREJS: 'CMS memerlukan pengaktifan JavaScript.'
CMSMain:
ACCESS: 'Akses ke bagian ''{title}'''
ACCESSALLINTERFACES: 'Akses ke semua bagian CMS'
ACCESSALLINTERFACESHELP: 'Kesampingkan pengaturan akses yang spesifik.'
SAVE: Simpan
CMSPageHistoryController_versions_ss:
PREVIEW: 'Pratinjau situs'
CMSProfileController:
MENUTITLE: 'Profil Saya'
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Anda merubah password anda untuk'
CHANGEPASSWORDTEXT2: 'Anda dapat menggunakan surat kepercayaan berikut untuk masuk:'
CHANGEPASSWORDTEXT1: 'Anda mengganti kata kunci menjadi'
CHANGEPASSWORDTEXT2: 'Anda sekarang dapat menggunakannya untuk masuk:'
EMAIL: Email
HELLO: Hai
PASSWORD: Kata kunci
CheckboxField:
NOANSWER: 'Tidak'
YESANSWER: 'Ya'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Lupa kata kunci?'
BUTTONLOGIN: 'Masuk kembali'
BUTTONLOGOUT: 'Keluar'
PASSWORDEXPIRED: '<p>Kata kunci Anda telah kadaluarsa. <a target="_top" href="{link}">Mohon buat yang baru.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Pengguna tidak dikenal. <a target="_top" href="{link}">Mohon otentikasi ulang di sini</a> untuk melanjutkan.</p>'
LoginMessage: '<p>Jika ada pekerjaan yang belum tersimpan, Anda dapat kembali dengan masuk di sini.</p>'
SUCCESS: Berhasil
SUCCESSCONTENT: '<p>Berhasil masuk. Jika tidak secara otomatis diarahkan, klik <a target="_top" href="{link}">di sini</a></p>'
TimedOutTitleAnonymous: 'Sesi Anda sudah habis.'
TimedOutTitleMember: 'Hai {name}!<br />Sesi Anda sudah habis.'
ConfirmedPasswordField:
SHOWONCLICKTITLE: 'Ganti Kata Sandi'
ATLEAST: 'Kata kunci harus setidaknya terdiri dari {min} karakter.'
BETWEEN: 'Kata kunci harus terdiri dari minimal {min} sampai {max} karakter.'
MAXIMUM: 'Kata kunci tidak boleh lebih dari {max} karakter.'
SHOWONCLICKTITLE: 'Ganti Kata Kunci'
ContentController:
NOTLOGGEDIN: 'Belum masuk'
CreditCardField:
FIRST: pertama
FOURTH: keempat
SECOND: kedua
THIRD: ketiga
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Objek-objek Data'
SINGULARNAME: 'Objek Data'
PLURALNAME: 'Obyek Data'
SINGULARNAME: 'Obyek Data'
Date:
DAY: hari
DAYS: hari
HOUR: jam
HOURS: jam
LessThanMinuteAgo: 'kurang dari semenit'
MIN: menit
MINS: menit
MIN: mnt
MINS: mnt
MONTH: bulan
MONTHS: bulan
SEC: detik
SECS: detik
TIMEDIFFAGO: '{difference} lalu'
SEC: dtk
SECS: dtk
TIMEDIFFAGO: '{difference} yang lalu'
TIMEDIFFIN: 'pada {difference}'
YEAR: tahun
YEARS: tahun
DateField:
NOTSET: 'tidak diset'
NOTSET: 'tidak diatur'
TODAY: hari ini
VALIDDATEFORMAT2: 'Mohon masukkan format tanggal yang valid ({format})'
VALIDDATEFORMAT2: 'Mohon isikan format tanggal yang valid ({format})'
VALIDDATEMAXDATE: 'Tanggal Anda harus lebih lama atau sama dengan tanggal maksimum ({date})'
VALIDDATEMINDATE: 'Tanggal Anda harus lebih baru atau sama dengan tanggal minimum ({date})'
DatetimeField:
NOTSET: 'Tidak diset'
NOTSET: 'Tidak diatur'
Director:
INVALID_REQUEST: 'Permintaan tidak valid'
DropdownField:
CHOOSE: (Pilih)
CHOOSESEARCH: '(Pilih or Cari)'
CHOOSESEARCH: '(Pilih atau Cari)'
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'
EmailField:
VALIDATION: 'Mohon masukkan alamat surel'
VALIDATION: 'Mohon isikan alamat email'
Enum:
ANY: Lain
ANY: Semua
File:
Content: Isi
AviType: 'Berkas video AVI'
Content: Konten
CssType: 'Berkas CSS'
DmgType: 'Imej cakram Apple'
DocType: 'Dokumen Word'
Filename: Nama File
Filename: Nama berkas
GifType: 'Gambar GIF - bagus untuk diagram'
GzType: 'Berkas kompresi GZIP'
HtlType: 'Berkas HTML'
HtmlType: 'Berkas HTML'
IcoType: 'Ikon gambar'
NOFILESIZE: 'Ukuran file adalah nol bytes.'
INVALIDEXTENSION: 'Ekstensi tidak dibolehkan (valid: {extensions})'
INVALIDEXTENSIONSHORT: 'Ekstensi tidak dibolehkan'
IcoType: 'Gambar ikon'
JpgType: 'Gambar JPEG - bagus untuk foto'
JsType: 'Berkas Javascript'
Mp3Type: 'Berkas audio MP3'
MpgType: 'Berkas video MPEG'
NOFILESIZE: 'Ukuran berkas nol byte.'
NOVALIDUPLOAD: 'Berkas tidak diunggah dengan benar'
Name: Nama
PLURALNAME: File-file
PLURALNAME: Berkas
PdfType: 'Berkas PDF Adobe Acrobat'
PngType: 'Gambar PNG - bagus untuk format serba-bisa'
SINGULARNAME: Berkas
TOOLARGE: 'Ukuran berkas terlalu besar, maksimal {size} dibolehkan'
TOOLARGESHORT: 'Ukuran berkas melebihi {size}'
TiffType: 'Format gambar tertanda'
Title: Judul
WavType: 'Berkas audio WAV'
XlsType: 'Dokumen Excel'
ZipType: 'Berkas kompresi ZIP'
Filesystem:
SYNCRESULTS: 'Penyelarasan selesai: {createdcount} item dibuat, {deletedcount} item dihapus'
Folder:
PLURALNAME: Map
SINGULARNAME: Map
PLURALNAME: Folder
SINGULARNAME: Folder
ForgotPasswordEmail_ss:
HELLO: Hai
TEXT1: 'Inilah'
TEXT2: 'link untuk mereset kata sandi'
TEXT1: 'Berikut ini '
TEXT2: 'tautan ganti kata kunci'
TEXT3: untuk
Form:
VALIDATIONNOTUNIQUE: 'Harga yang dimasukkan tidak unik'
VALIDATIONPASSWORDSDONTMATCH: 'Password tidak cocok'
VALIDATIONPASSWORDSNOTEMPTY: 'Password tidak boleh kosong'
VALIDATOR: Pengesah
CSRF_FAILED_MESSAGE: 'Kemungkinan ada masalah teknis. Mohon klik tombol kembali, muat ulang browser, dan coba lagi.'
FIELDISREQUIRED: '{name} wajib diisi'
SubmitBtnLabel: Lanjut
VALIDATIONCREDITNUMBER: 'Mohon pastikan Anda sudah mengisi nomer kartu kredit {number} dengan benar'
VALIDATIONNOTUNIQUE: 'Nilai yang diisikan tidak unik'
VALIDATIONPASSWORDSDONTMATCH: 'Kata kunci tidak sesuai'
VALIDATIONPASSWORDSNOTEMPTY: 'Kata kunci tidak boleh kosong'
VALIDATIONSTRONGPASSWORD: 'Kata kunci harus setidaknya terdiri dari satu angka dan satu karakter alfanumerik'
VALIDATOR: Validasi
VALIDCURRENCY: 'Mohon isikan mata uang yang benar'
CSRF_EXPIRED_MESSAGE: 'Sesi Anda sudah habis. Mohon kirim ulang formulir.'
FormField:
Example: 'misalnya %s'
NONE: tidak ada
GridAction:
DELETE_DESCRIPTION: Hapus
Delete: Hapus
UnlinkRelation: Unlink
GridField:
Add: 'Tambah {name}'
Filter: Saring
FilterBy: 'Saring dengan'
Find: Temukan
LinkExisting: 'Tautan tersedia'
NewRecord: 'Baru %s'
FilterBy: 'Saring menurut '
Find: Cari
LEVELUP: 'Ke atas'
LinkExisting: 'Tautan yang Ada'
NewRecord: '%s baru'
NoItemsFound: 'Tidak ada data'
PRINTEDAT: 'Dicetak pada'
PRINTEDBY: 'Dicetak oleh'
PlaceHolder: 'Cari {type}'
PlaceHolderWithLabels: 'Cari {type} menurut {name}'
RelationSearch: 'Cari yang terkait'
ResetFilter: Reset
GridFieldAction_Delete:
DeletePermissionsFailure: 'Tidak ada ijin menghapus'
EditPermissionsFailure: 'Tidak ada ijin membuka tautan'
GridFieldDetailForm:
CancelBtn: Batal
Create: Buat
Delete: Hapus
DeletePermissionsFailure: 'Tidak ada ijin menghapus'
Deleted: '%s %s dihapus'
Save: Simpan
Saved: 'Simpan {name} {link}'
GridFieldEditButton_ss:
EDIT: Edit
GridFieldItemEditView:
Go_back: 'Kembali'
Group:
Code: 'Kode Grup'
DefaultGroupTitleAdministrators: Pengurus
DefaultGroupTitleContentAuthors: 'Pencipta isi'
AddRole: 'Tambahkan peran untuk kelompok ini'
Code: 'Kode Kelompok'
DefaultGroupTitleAdministrators: Pengelola
DefaultGroupTitleContentAuthors: 'Penulis Konten'
Description: Deskripsi
GroupReminder: 'Jika Anda memilih kelompok induk, kelompok ini akan mengambil perannya'
HierarchyPermsError: 'Tidak dapat menghubungkan kelompok induk "%s" dengan perijinan khusus (memerlukan akses PENGELOLA)'
Locked: 'Terkunci?'
Parent: 'Grup induk'
Sort: 'Urutan Sortir'
has_many_Permissions: Ijin
many_many_Members: Anggota-anggota
NoRoles: 'Tidak ada peran'
PLURALNAME: Kelompok
Parent: 'Kelompok Induk'
RolesAddEditLink: 'Kelola peran'
SINGULARNAME: Kelompok
Sort: 'Urutkan'
has_many_Permissions: Perijinan
many_many_Members: Pengguna
GroupImportForm:
Help1: '<p>Impor satu atau lebih kelompok di format <em>CSV</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Tampilkan penggunaan mahir</a></small></p>'
Help2: "<div class=\"advanced\">\n\t<h4>Penggunaan mahir</h4>\n\t<ul>\n\t<li>Kolom yang dibolehkan: <em>%s</em></li>\n\t<li>Kelompok yang sudah terdata dihubungkan dengan nilai <em>Kode</em> uniknya, dan diperbarui dengan nilai apapun dari berkas yang diimpor</li>\n\t<li>Hirarki kelompok dapat dibuat dengan kolom <em>ParentCode</em>.</li>\n\t<li>Kode perijinan dapat dihubungkan dengan kolom <em>PermissionCode</em>. Perijinan yang sudah ada tidak akan terpengaruh.</li>\n\t</ul>\n</div>"
ResultCreated: '{count} kelompok dibuat'
ResultDeleted: '%d kelompok dihapus'
ResultUpdated: '%d kelompok diperbarui'
Hierarchy:
InfiniteLoopNotAllowed: 'Putaran berulang ditemukan pada hirarki "{type}". Mohon ganti induk untuk mengatasi masalah ini'
HtmlEditorField:
BUTTONINSERTLINK: 'Beri link'
BUTTONREMOVELINK: 'Pindahkan link'
CSSCLASS: 'Aligmen/gaya'
CSSCLASSCENTER: 'Di tengah, sendiri'
CSSCLASSLEFT: 'Pada sebelah kiri, dengan teks disekitarnya'
CSSCLASSLEFTALONE: 'Di sebelah kiri, sendirian.'
CSSCLASSRIGHT: 'Pada sebelah kanan, dengan teks disekitarnya'
ADDURL: 'Tambah URL'
ADJUSTDETAILSDIMENSIONS: 'Rincian &amp; dimensi'
ANCHORSCANNOTACCESSPAGE: 'Anda tidak dijinkan mengakses konten laman yang diminta.'
ANCHORSPAGENOTFOUND: 'Laman yang diminta tidak ditemukan.'
ANCHORVALUE: Jangkar
BUTTONADDURL: 'Tambah URL'
BUTTONINSERT: Sisip
BUTTONINSERTLINK: 'Sisip tautan'
BUTTONREMOVELINK: 'Hapus tautan'
BUTTONUpdate: Perbarui
CAPTIONTEXT: 'Keterangan'
CSSCLASS: 'Perataan / gaya teks'
CSSCLASSCENTER: 'Rata tengah, mandiri'
CSSCLASSLEFT: 'Rata kiri, dengan teks menyesuaikan.'
CSSCLASSLEFTALONE: 'Rata kiri, mandiri.'
CSSCLASSRIGHT: 'Rata kanan, degan teks menyesuaikan.'
DETAILS: Rincian
EMAIL: 'Alamat email'
FILE: Berkas
FOLDER: Folder
FROMCMS: 'Dari CMS'
FROMCOMPUTER: 'Dari komputer Anda'
FROMWEB: 'Dari situs lain'
FindInFolder: 'Temukan di Folder'
IMAGEALT: 'Teks alternatif (alt)'
IMAGEALTTEXT: 'Teks alternatif (alt) - pengganti jika gambar tidak tampil'
IMAGEALTTEXTDESC: 'Tampil ke pembaca layar, atau jika gambar tidak tampil'
IMAGEDIMENSIONS: Dimensi
IMAGEHEIGHTPX: Tinggi
IMAGETITLE: 'Teks judul (tooltip) - untuk informasi tambahan tentang gambar'
IMAGETITLETEXT: 'Teks gambar (tooltip)'
IMAGETITLETEXTDESC: 'Untuk informasi tambahan tentang gambar'
IMAGEWIDTHPX: Lebar
LINK: 'Beri/edit link untuk teks yang di-highlight'
LINKANCHOR: 'Anchor halaman ini'
LINKDESCR: 'Deskripsi link'
INSERTMEDIA: 'Sisipkan Media'
LINK: 'Sisipkan Tautan'
LINKANCHOR: 'Jangkar pada laman ini'
LINKDESCR: 'Deskripsi tautan'
LINKEMAIL: 'Alamat email'
LINKEXTERNAL: 'Situs web lain'
LINKFILE: 'Download file'
LINKINTERNAL: 'Halaman pada situs'
LINKOPENNEWWIN: 'Buka link pada jendela baru?'
LINKTO: 'Link ke'
PAGE: Halaman
LINKEXTERNAL: 'Situs lain'
LINKFILE: 'Unduh berkas'
LINKINTERNAL: 'Laman pada situs'
LINKOPENNEWWIN: 'Buka tautan di jendela baru?'
LINKTO: 'Tautan ke'
PAGE: Laman
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' tidak dapat dijadikan sumber daya media.'
UpdateMEDIA: 'Perbarui Media'
SUBJECT: 'Subyek email'
Image:
PLURALNAME: Berkas
SINGULARNAME: Berkas
Image_Cached:
PLURALNAME: Berkas
SINGULARNAME: Berkas
Image_iframe_ss:
TITLE: 'Iframe Meng-upload Gambar'
TITLE: 'Iframe Pengunggahan Gambar'
LeftAndMain:
CANT_REORGANISE: 'Anda tidak diijinkan mengubah laman Tingkat Atas. Pengubahan Anda tidak tersimpan.'
DELETED: Terhapus.
DropdownBatchActionsDefault: Tindakan
HELP: Bantuan
PAGETYPE: 'Jenis laman:'
PERMAGAIN: 'Anda telah keluar dari situs. Jika ingin kembali masuk, isikan nama pengguna dan kata kunci di bawah ini.'
PERMALREADY: 'Maaf, Anda tidak dapat mengakses laman tersebut. Jika Anda ingin menggunakan akun lain, silakan masuk di sini'
PERMDEFAULT: 'Mohon pilih metode otentikasi dan isikan informasi login Anda.'
PLEASESAVE: 'Mohon Simpan Laman: Laman ini tidak dapat diperbarui karena belum disimpan.'
PreviewButton: Pratinjau
REORGANISATIONSUCCESSFUL: 'Pengaturan ulang struktur situs berhasil.'
SAVEDUP: Tersimpan.
ShowAsList: 'tampilkan sebagai daftar'
TooManyPages: 'Terlalu banyak laman'
ValidationError: 'Kesalahan validasi'
VersionUnknown: Tidak diketahui
LeftAndMain_Menu_ss:
Hello: Hai
LOGOUT: 'Keluar'
ListboxField:
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'
LoginAttempt:
Email: 'Alamat Email'
IP: 'Alamat IP'
PLURALNAME: 'Upaya Masuk'
SINGULARNAME: 'Upaya Masuk'
Status: Status
Member:
BUTTONCHANGEPASSWORD: 'Ubah Password'
ADDGROUP: 'Tambah kelompok'
BUTTONCHANGEPASSWORD: 'Ganti Kata Kunci'
BUTTONLOGIN: 'Masuk'
BUTTONLOGINOTHER: 'Masuk sebagai orang lain'
BUTTONLOSTPASSWORD: 'Saya lupa password saya'
CONFIRMNEWPASSWORD: 'Membenarkan Password Baru'
CONFIRMPASSWORD: 'Membenarkan Password'
EMAIL: E-mail
ENTEREMAIL: 'Mohon masukkan alamat email anda untuk mendapatkan link reset kata sandi anda.'
ERRORNEWPASSWORD: 'Anda memasukkan password baru secara berbeda, coba lagi'
ERRORPASSWORDNOTMATCH: 'Password Anda tidak cocok, coba lagi'
BUTTONLOGINOTHER: 'Masuk dengan akun lain'
BUTTONLOSTPASSWORD: 'Saya lupa kata kuncinya'
CANTEDIT: 'Tidak ada ijin'
CONFIRMNEWPASSWORD: 'Konfirmasi Penggantian Kata Kunci'
CONFIRMPASSWORD: 'Konfirmasi Kata Kunci'
DATEFORMAT: 'Format tanggal'
DefaultAdminFirstname: 'Pengelola Utama'
DefaultDateTime: default
EMAIL: Email
EMPTYNEWPASSWORD: 'Kata kunci baru tidak boleh kosong, mohon coba lagi'
ENTEREMAIL: 'Mohon isikan alamat email untuk menerima tautan penggantian kata kunci.'
ERRORLOCKEDOUT2: 'Akun Anda untuk sementara diblok karena terlalu banyak upaya masuk yang gagal. Mohon coba lagi setelah {count} menit.'
ERRORNEWPASSWORD: 'Anda mengisikan kata kunci baru yang tidak sama, coba lagi'
ERRORPASSWORDNOTMATCH: 'Kata kunci Anda tidak sama, mohon coba lagi'
ERRORWRONGCRED: 'Rincian yang diisikan tidak benar. Mohon coba lagi.'
FIRSTNAME: 'Nama Depan'
INTERFACELANG: 'Bahasa Interface'
NEWPASSWORD: 'Password Baru'
PLURALNAME: Anggota-anggota
REMEMBERME: 'Ingat saya kali berikutnya?'
SINGULARNAME: Anggota
SUBJECTPASSWORDCHANGED: 'Password Anda telah diubah'
SUBJECTPASSWORDRESET: 'Link mengubah total password Anda'
SURNAME: Nama Panggilan
VALIDATIONMEMBEREXISTS: 'Sudah ada member dengan email ini'
YOUROLDPASSWORD: 'Password lama Anda'
belongs_many_many_Groups: Grup-grup
db_LastVisited: 'Tanggal Kunjungan Terakhir'
db_LockedOutUntil: 'Terkunci sampai'
INTERFACELANG: 'Bahasa Antarmuka'
INVALIDNEWPASSWORD: 'Kata kunci berikut tidak dapat diterima: {password}'
LOGGEDINAS: 'Anda masuk sebagai {name}.'
NEWPASSWORD: 'Kata Kunci Baru'
NoPassword: 'Tidak ada kata kunci untuk pengguna ini.'
PASSWORD: Kata kunci
PASSWORDEXPIRED: 'Kata kunci Anda telah kadaluarsa. Mohon buat yang baru.'
PLURALNAME: Pengguna
REMEMBERME: 'Ingat akun saya?'
SINGULARNAME: Pengguna
SUBJECTPASSWORDCHANGED: 'Kata kunci Anda telah diganti'
SUBJECTPASSWORDRESET: 'Tautan penggantian kata kunci Anda'
SURNAME: Nama Belakang
TIMEFORMAT: 'Format waktu'
VALIDATIONMEMBEREXISTS: 'Pengguna dengan %s yang sama sudah ada'
ValidationIdentifierFailed: 'Tidak dapat menimpa pengguna #{id} dengan pengenal yang sama ({name} = {value}))'
WELCOMEBACK: 'Selamat Datang kembali, {firstname}'
YOUROLDPASSWORD: 'Kata kunci lama'
belongs_many_many_Groups: Kelompok
db_LastVisited: 'Kunjungan Terakhir'
db_Locale: 'Lokal Antarmuka'
db_LockedOutUntil: 'Kunci sampai'
db_NumVisit: 'Jumlah Kunjungan'
db_Password: Kata sandi
db_PasswordExpiry: 'Tanggal Kata Sandi Berakhir'
db_Password: Kata kunci
db_PasswordExpiry: 'Tanggal Kadaluarsa'
MemberAuthenticator:
TITLE: 'E-mail &amp; Kata Kunci'
MemberDatetimeOptionsetField:
TWODIGITDAY: 'Dua digit bulan'
TWODIGITHOUR: 'Dua angka jam (00 ke 23)'
TWODIGITYEAR: 'Dua-angka tahun'
AMORPM: 'AM (Ante meridiem) atau PM (Post meridiem)'
Custom: Custom
DATEFORMATBAD: 'Format tanggal tidak benar'
DAYNOLEADING: 'Angka tanggal tanpa nol di depan'
DIGITSDECFRACTIONSECOND: 'Satu atau lebih angka merepresentasikan pecahan desimal dari detik'
FOURDIGITYEAR: 'Tahun empat angka'
FULLNAMEMONTH: 'Nama lengkap bulan (misalnya Juni)'
HOURNOLEADING: 'Angka jam tanpa nol di depan'
MINUTENOLEADING: 'Angka menit tanpa nol di depan'
MONTHNOLEADING: 'Angka bulan tanpa nol di depan'
Preview: Pratinjau
SHORTMONTH: 'Nama singkat bulan (misalnya Jun)'
TWODIGITDAY: 'Tanggal dua angka'
TWODIGITHOUR: 'Jam dua angka (00 sampai 23)'
TWODIGITMINUTE: 'Menit dua angka (00 sampai 59)'
TWODIGITMONTH: 'Bulan dua angka (01=Januari, dll)'
TWODIGITSECOND: 'Detik dua angka (00 sampai 59)'
TWODIGITYEAR: 'Tahun dua angka'
Toggle: 'Tampilkan bantuan pemformatan'
MemberImportForm:
ResultCreated: 'Membuat {count} anggota'
ResultDeleted: 'Menghapus %d anggota'
ResultNone: 'Tidak ada perubahan'
Help1: '<p>Impor pengguna dalam <em>format CSV</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Tampilkan penggunaan mahir</a></small></p>'
Help2: "<div class=\"advanced\">\n\t<h4>Penggunaan mahir</h4>\n\t<ul>\n\t<li>Kolom yang dibolehkan: <em>%s</em></li>\n\t<li>Pengguna yang sudah terdata dihubungkan dengan nilai <em>Kode</em> uniknya, \n\tdan diperbarui dengan nilai apapun dari berkas yang diimpor.</li>\n\t<li>Kelompok dapat dihubungkan dengan kolom <em>Kelompok</em>. Kelompok diidentifikasi dengan properti <em>Kode</em>-nya,\n\tkelompok ganda dapat dipisahkan dengan tanda koma. Kelompok yang sudah terdata tidak terpengaruh.</li>\n\t</ul>\n\
</div>"
ResultCreated: '{count} pengguna dibuat'
ResultDeleted: '%d pengguna dihapus'
ResultNone: 'Tidak ada pengubahan'
ResultUpdated: '{count} pengguna diperbarui'
MemberPassword:
PLURALNAME: 'Kata kunci anggota'
PLURALNAME: 'Kata Kunci'
SINGULARNAME: 'Kata Kunci'
MemberTableField:
APPLY_FILTER: 'Terapkan saring'
APPLY_FILTER: 'Terapkan Saring'
ModelAdmin:
DELETE: Hapus
DELETEDRECORDS: 'Menghapus {count} rekod'
DELETEDRECORDS: '{count} data dihapus.'
EMPTYBEFOREIMPORT: 'Ganti data'
IMPORT: 'Impor dari CSV'
IMPORTEDRECORDS: '{count} data diimpor.'
NOCSVFILE: 'Mohon pilih berkas CSV untuk diimpor'
NOIMPORT: 'Tidak ada yang terimpor'
RESET: Reset
Title: 'Model Data'
UPDATEDRECORDS: '{count} data diperbarui.'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Kolom database'
IMPORTSPECRELATIONS: Hubungan
IMPORTSPECLINK: 'Tampilkan Spesifikasi untuk %s'
IMPORTSPECRELATIONS: Keterkaitan
IMPORTSPECTITLE: 'Spesifikasi untuk %s'
ModelAdmin_Tools_ss:
FILTER: Saring
IMPORT: Impor
@ -215,46 +454,135 @@ id:
MoneyField:
FIELDLABELAMOUNT: Jumlah
FIELDLABELCURRENCY: Mata Uang
NullableField:
IsNullLabel: 'N/A'
NumericField:
VALIDATION: '''{value}'' bukan angka, hanya angka yang dapat diterima isian ini'
Pagination:
Page: Laman
View: Tampilkan
PasswordValidator:
LOWCHARSTRENGTH: 'Mohon tingkatkan kekuatan kata kunci dengan menambah beberapa karakter berikut ini: %s'
PREVPASSWORD: 'Anda sudah pernah menggunakan kata kunci tersebut, mohon pilih kata kunci yang baru'
TOOSHORT: 'Kata kunci terlalu singkat, setidaknya harus terdiri dari %s karakter atau lebih'
Permission:
AdminGroup: Pengurus
FULLADMINRIGHTS: 'Hak-hak administratif yang penuh'
AdminGroup: Pengelola
CMS_ACCESS_CATEGORY: 'Akses CMS'
FULLADMINRIGHTS: 'Hak pengelolaan penuh'
FULLADMINRIGHTS_HELP: 'Abaikan dan kesampingkan semua perijinan yang terhubung lainnya.'
PLURALNAME: Perijinan
SINGULARNAME: Perijinan
PermissionCheckboxSetField:
AssignedTo: 'dihubungkan ke "{title}"'
FromGroup: 'diwarisi dari kelompok "{title}"'
FromRole: 'diwarisi dari peran "{title}"'
FromRoleOnGroup: 'diwarisi dari peran "%s" pada kelompok "%s"'
PermissionRole:
OnlyAdminCanApply: 'Hanya untuk pengelola'
PLURALNAME: Peran
SINGULARNAME: Peran
Title: Judul
PermissionRoleCode:
PermsError: 'Tidak dapat menghubungkan kode "%s" dengan perijinan khusus (memerlukan akses PENGELOLA)'
PLURALNAME: 'Kode Perijinan Peran'
SINGULARNAME: 'Kode Perijinan Peran'
Permissions:
PERMISSIONS_CATEGORY: 'Perijinan peran dan akses'
UserPermissionsIntro: 'Menghubungkan kelompok ke pengguna ini akan menyesuaikan perijinannya. Lihat bagian Kelompok untuk rincian perijinan pada kelompok individu.'
PhoneNumberField:
VALIDATION: 'Harap masukkan nomor telepon yang valid'
VALIDATION: 'Mohon isikan nomer telpon yang benar'
Security:
ALREADYLOGGEDIN: 'Anda tidak memiliki akses ke halaman ini. Jika anda memiliki keanggotaan yang dapat mengakses halaman ini, anda dapat masuk di bawah ini.'
BUTTONSEND: 'Kirimi saya link untuk mengeset ulang password '
CHANGEPASSWORDBELOW: 'Anda dapat mengubah password anda di bawah ini.'
CHANGEPASSWORDHEADER: 'Ubah password anda'
ENTERNEWPASSWORD: 'Harap masukkan password anda yang baru'
ERRORPASSWORDPERMISSION: 'Anda harus masuk terlebih dahulu untuk merubah password Anda!'
LOGGEDOUT: 'Anda telah keluar. Jika Anda ingin masuk lagi, masukkan surat kepercayaan Anda di bawah ini.'
NOTEPAGESECURED: 'Halaman ini diamankan. Masukkan surat kepercayaan Anda di bawah ini dan kami akan mengirim Anda ke jalur yang benar.'
NOTERESETPASSWORD: 'Masukkan alamat e-mail anda dan kami akan mengirimi anda link yang dapat anda gunakan untuk mengeset ulang password '
ALREADYLOGGEDIN: 'Anda tidak punya akses ke laman ini. Jika Anda punya akun lain dengan akses ke laman ini, silakan masuk kembali.'
BUTTONSEND: 'Kirimkan tautan penggantian kata kunci'
CHANGEPASSWORDBELOW: 'Anda dapat mengganti kata kunci di bawah ini.'
CHANGEPASSWORDHEADER: 'Ganti kata kunci'
ENTERNEWPASSWORD: 'Mohon isikan kata kunci yang baru.'
ERRORPASSWORDPERMISSION: 'Anda harus masuk untuk bisa mengganti kata kunci!'
LOGGEDOUT: 'Anda telah keluar. Jika ingin masuk kembali, isikan informasi akun Anda di sini.'
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.'
SecurityAdmin:
GROUPNAME: 'Nama grup'
MEMBERS: Anggota-anggota
PERMISSIONS: Ijin-ijin
ACCESS_HELP: 'Bolehkan menampilkan, menambah dan mengedit pengguna, termasuk mengatur perijinan dan perannya.'
APPLY_ROLES: 'Terapkan peran ke kelompok'
APPLY_ROLES_HELP: 'Bolehkan mengedit peran yang diberikan ke kelompok. Memerlukan perijinan "Akses ke bagian ''Pengguna''".'
EDITPERMISSIONS: 'Kelola perijinan untuk kelompok'
EDITPERMISSIONS_HELP: 'Bolehkan mengedit Perijinan dan Alamat IP untuk kelompok. Memerlukan perijinan "Akses ke bagian ''Pengamanan''".'
GROUPNAME: 'Nama kelompok'
IMPORTGROUPS: 'Impor kelompok'
IMPORTUSERS: 'Impor pengguna'
MEMBERS: Pengguna
MENUTITLE: Pengamanan
MemberListCaution: 'Perhatian: Menghapus pengguna dari daftar ini akan menghapus mereka dari semua kelompok dan database'
NEWGROUP: 'Kelompok Baru'
PERMISSIONS: Perijinan
ROLES: Peran
ROLESDESCRIPTION: 'Peran adalah set perijinan yang sudah ditentukan, dan dapat dihubungkan ke kelompok.<br />Dapat diwariskan dari kelompok induk jika diperlukan.'
TABROLES: Peran
Users: Pengguna
SecurityAdmin_MemberImportForm:
BtnImport: 'Impor dari CSV'
FileFieldLabel: 'Berkas CSV <small>(Ekstensi yang dibolehkan: *.csv)</small>'
SilverStripeNavigator:
Auto: Otomatis
ChangeViewMode: 'Ganti modus tampil'
Desktop: Desktop
DualWindowView: 'Jendela Ganda'
Edit: Edit
EditView: 'Modus edit'
Mobile: Tampilan selular
PreviewState: 'Status Pratinjau'
PreviewView: 'Modus pratinjau'
Responsive: Tampilan responsif
SplitView: 'Modus terpisah'
Tablet: Tablet
ViewDeviceWidth: 'Pilih lebar pratinjau'
Width: lebar
SiteTree:
TABMAIN: Utama
TableListField:
CSVEXPORT: 'Ekspor ke CSV'
Print: Cetak
TableListField_PageControls_ss:
OF: dari
TimeField:
VALIDATEFORMAT: 'Isikan format waktu yang benar ({format})'
ToggleField:
LESS: kurang
MORE: lebih
UploadField:
ATTACHFILE: 'Lampirkan berkas'
ATTACHFILES: 'Lampirkan berkas'
AttachFile: 'Lampirkan berkas'
CHOOSEANOTHERFILE: 'Pilih berkas lain'
CHOOSEANOTHERINFO: 'Ganti berkas ini dengan berkas lain dari penyimpanan'
DELETE: 'Hapus dari berkas'
DELETEINFO: 'Hapus berkas secara permanen dari penyimpanan'
DOEDIT: Simpan
EDIT: Ubah
EDITINFO: 'Ubah berkas ini'
FIELDNOTSET: 'Informasi berkas tidak ditemukan'
FROMCOMPUTER: 'Dari komputermu'
DROPFILE: 'taruh berkas'
DROPFILES: 'taruh berkas'
Dimensions: Dimensi
EDIT: Edit
EDITINFO: 'Edit berkas ini'
FIELDNOTSET: 'Informasi berkas tidak ada'
FROMCOMPUTER: 'Dari komputer Anda'
FROMCOMPUTERINFO: 'Pilih dari berkas'
FROMFILES: 'Dari berkas'
HOTLINKINFO: 'Informasi: Gambar ini akan ditautlangsungkan. Mohon pastikan Anda mendapat ijin dari pemilik situs untuk melakukannya.'
MAXNUMBEROFFILES: 'Jumlah maksimal {count} berkas terlampaui.'
MAXNUMBEROFFILESONE: 'Hanya dapat mengunggah satu berkas'
MAXNUMBEROFFILESSHORT: 'Dapat mengunggah {count} berkas'
OVERWRITEWARNING: 'Berkas dengan nama yang sama sudah ada'
REMOVE: Memindahkan
STARTALL: 'Jalankan semua'
Saved: Disimpan
UPLOADSINTO: 'simpan ke /{path}'
REMOVE: Buang
REMOVEINFO: 'Hapus berkas ini dari sini, tapi jangan hapus dari penyimpanan'
STARTALL: 'Mulai semua'
Saved: Tersimpan
UPLOADSINTO: 'disimpan ke /{path}'
Versioned:
has_many_Versions: Versi-versi
has_many_Versions: Versi
CheckboxSetField:
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'

588
lang/id_ID.yml Normal file
View File

@ -0,0 +1,588 @@
id_ID:
AssetAdmin:
NEWFOLDER: FolderBaru
SHOWALLOWEDEXTS: 'Tampilkan ekstensi yang dibolehkan'
AssetTableField:
CREATED: 'Pertama diunggah'
DIM: Dimensi
FILENAME: Nama berkas
FOLDER: Folder
LASTEDIT: 'Terakhir diubah'
OWNER: Pemilik
SIZE: 'Ukuran berkas'
TITLE: Judul
TYPE: 'Jenis berkas'
URL: URL
AssetUploadField:
ChooseFiles: 'Pilih berkas'
DRAGFILESHERE: 'Tarik berkas ke sini'
DROPAREA: 'Area Taruh'
EDITALL: 'Edit semua'
EDITANDORGANIZE: 'Edit & kelola'
EDITINFO: 'Edit berkas'
FILES: Berkas
FROMCOMPUTER: 'Pilih berkas dari komputer Anda'
FROMCOMPUTERINFO: 'Unggah dari komputer Anda'
TOTAL: Total
TOUPLOAD: 'Pilih berkas untuk diunggah...'
UPLOADINPROGRESS: 'Mohon tunggu... sedang mengunggah'
UPLOADOR: ATAU
BBCodeParser:
ALIGNEMENT: Perataan
ALIGNEMENTEXAMPLE: 'rata kanan'
BOLD: 'Teks Tebal'
BOLDEXAMPLE: Tebal
CODE: 'Blok Kode'
CODEDESCRIPTION: 'Blok kode tanpa format'
CODEEXAMPLE: 'Blok kode'
COLORED: 'Teks berwarna'
COLOREDEXAMPLE: 'teks biru'
EMAILLINK: 'Tautan email'
EMAILLINKDESCRIPTION: 'Buat tautan ke alamat email'
IMAGE: Gambar
IMAGEDESCRIPTION: 'Tampilkan gambar pada entri'
ITALIC: 'Teks Miring'
ITALICEXAMPLE: Miring
LINK: 'Tautan situs'
LINKDESCRIPTION: 'Tautan ke situs atau URL lain'
STRUCK: 'Teks Coret'
STRUCKEXAMPLE: Coret
UNDERLINE: 'Teks Garis Bawah'
UNDERLINEEXAMPLE: Garis Bawah
UNORDERED: 'daftar acak'
UNORDEREDDESCRIPTION: 'Daftar acak'
UNORDEREDEXAMPLE1: 'Item acak 1'
BackLink_Button_ss:
Back: Kembali
BasicAuth:
ENTERINFO: 'Mohon isikan nama pengguna dan kata kunci.'
ERRORNOTADMIN: 'Pengguna tersebut bukan pengelola.'
ERRORNOTREC: 'Nama pengguna dan kata kunci tidak dikenal'
Boolean:
ANY: Semua
NOANSWER: 'Tidak'
YESANSWER: 'Ya'
CMSLoadingScreen_ss:
LOADING: Memuat...
REQUIREJS: 'CMS memerlukan pengaktifan JavaScript.'
CMSMain:
ACCESS: 'Akses ke bagian ''{title}'''
ACCESSALLINTERFACES: 'Akses ke semua bagian CMS'
ACCESSALLINTERFACESHELP: 'Kesampingkan pengaturan akses yang spesifik.'
SAVE: Simpan
CMSPageHistoryController_versions_ss:
PREVIEW: 'Pratinjau situs'
CMSProfileController:
MENUTITLE: 'Profil Saya'
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Anda mengganti kata kunci menjadi'
CHANGEPASSWORDTEXT2: 'Anda sekarang dapat menggunakannya untuk masuk:'
EMAIL: Email
HELLO: Hai
PASSWORD: Kata kunci
CheckboxField:
NOANSWER: 'Tidak'
YESANSWER: 'Ya'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Lupa kata kunci?'
BUTTONLOGIN: 'Masuk kembali'
BUTTONLOGOUT: 'Keluar'
PASSWORDEXPIRED: '<p>Kata kunci Anda telah kadaluarsa. <a target="_top" href="{link}">Mohon buat yang baru.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Pengguna tidak dikenal. <a target="_top" href="{link}">Mohon otentikasi ulang di sini</a> untuk melanjutkan.</p>'
LoginMessage: '<p>Jika ada pekerjaan yang belum tersimpan, Anda dapat kembali dengan masuk di sini.</p>'
SUCCESS: Berhasil
SUCCESSCONTENT: '<p>Berhasil masuk. Jika tidak secara otomatis diarahkan, klik <a target="_top" href="{link}">di sini</a></p>'
TimedOutTitleAnonymous: 'Sesi Anda sudah habis.'
TimedOutTitleMember: 'Hai {name}!<br />Sesi Anda sudah habis.'
ConfirmedPasswordField:
ATLEAST: 'Kata kunci harus setidaknya terdiri dari {min} karakter.'
BETWEEN: 'Kata kunci harus terdiri dari minimal {min} sampai {max} karakter.'
MAXIMUM: 'Kata kunci tidak boleh lebih dari {max} karakter.'
SHOWONCLICKTITLE: 'Ganti Kata Kunci'
ContentController:
NOTLOGGEDIN: 'Belum masuk'
CreditCardField:
FIRST: pertama
FOURTH: keempat
SECOND: kedua
THIRD: ketiga
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Obyek Data'
SINGULARNAME: 'Obyek Data'
Date:
DAY: hari
DAYS: hari
HOUR: jam
HOURS: jam
LessThanMinuteAgo: 'kurang dari semenit'
MIN: mnt
MINS: mnt
MONTH: bulan
MONTHS: bulan
SEC: dtk
SECS: dtk
TIMEDIFFAGO: '{difference} yang lalu'
TIMEDIFFIN: 'pada {difference}'
YEAR: tahun
YEARS: tahun
DateField:
NOTSET: 'tidak diatur'
TODAY: hari ini
VALIDDATEFORMAT2: 'Mohon isikan format tanggal yang valid ({format})'
VALIDDATEMAXDATE: 'Tanggal Anda harus lebih lama atau sama dengan tanggal maksimum ({date})'
VALIDDATEMINDATE: 'Tanggal Anda harus lebih baru atau sama dengan tanggal minimum ({date})'
DatetimeField:
NOTSET: 'Tidak diatur'
Director:
INVALID_REQUEST: 'Permintaan tidak valid'
DropdownField:
CHOOSE: (Pilih)
CHOOSESEARCH: '(Pilih atau Cari)'
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'
EmailField:
VALIDATION: 'Mohon isikan alamat email'
Enum:
ANY: Semua
File:
AviType: 'Berkas video AVI'
Content: Konten
CssType: 'Berkas CSS'
DmgType: 'Imej cakram Apple'
DocType: 'Dokumen Word'
Filename: Nama berkas
GifType: 'Gambar GIF - bagus untuk diagram'
GzType: 'Berkas kompresi GZIP'
HtlType: 'Berkas HTML'
HtmlType: 'Berkas HTML'
INVALIDEXTENSION: 'Ekstensi tidak dibolehkan (valid: {extensions})'
INVALIDEXTENSIONSHORT: 'Ekstensi tidak dibolehkan'
IcoType: 'Gambar ikon'
JpgType: 'Gambar JPEG - bagus untuk foto'
JsType: 'Berkas Javascript'
Mp3Type: 'Berkas audio MP3'
MpgType: 'Berkas video MPEG'
NOFILESIZE: 'Ukuran berkas nol byte.'
NOVALIDUPLOAD: 'Berkas tidak diunggah dengan benar'
Name: Nama
PLURALNAME: Berkas
PdfType: 'Berkas PDF Adobe Acrobat'
PngType: 'Gambar PNG - bagus untuk format serba-bisa'
SINGULARNAME: Berkas
TOOLARGE: 'Ukuran berkas terlalu besar, maksimal {size} dibolehkan'
TOOLARGESHORT: 'Ukuran berkas melebihi {size}'
TiffType: 'Format gambar tertanda'
Title: Judul
WavType: 'Berkas audio WAV'
XlsType: 'Dokumen Excel'
ZipType: 'Berkas kompresi ZIP'
Filesystem:
SYNCRESULTS: 'Penyelarasan selesai: {createdcount} item dibuat, {deletedcount} item dihapus'
Folder:
PLURALNAME: Folder
SINGULARNAME: Folder
ForgotPasswordEmail_ss:
HELLO: Hai
TEXT1: 'Berikut ini '
TEXT2: 'tautan ganti kata kunci'
TEXT3: untuk
Form:
CSRF_FAILED_MESSAGE: 'Kemungkinan ada masalah teknis. Mohon klik tombol kembali, muat ulang browser, dan coba lagi.'
FIELDISREQUIRED: '{name} wajib diisi'
SubmitBtnLabel: Lanjut
VALIDATIONCREDITNUMBER: 'Mohon pastikan Anda sudah mengisi nomer kartu kredit {number} dengan benar'
VALIDATIONNOTUNIQUE: 'Nilai yang diisikan tidak unik'
VALIDATIONPASSWORDSDONTMATCH: 'Kata kunci tidak sesuai'
VALIDATIONPASSWORDSNOTEMPTY: 'Kata kunci tidak boleh kosong'
VALIDATIONSTRONGPASSWORD: 'Kata kunci harus setidaknya terdiri dari satu angka dan satu karakter alfanumerik'
VALIDATOR: Validasi
VALIDCURRENCY: 'Mohon isikan mata uang yang benar'
CSRF_EXPIRED_MESSAGE: 'Sesi Anda sudah habis. Mohon kirim ulang formulir.'
FormField:
Example: 'misalnya %s'
NONE: tidak ada
GridAction:
DELETE_DESCRIPTION: Hapus
Delete: Hapus
UnlinkRelation: Unlink
GridField:
Add: 'Tambah {name}'
Filter: Saring
FilterBy: 'Saring menurut '
Find: Cari
LEVELUP: 'Ke atas'
LinkExisting: 'Tautan yang Ada'
NewRecord: '%s baru'
NoItemsFound: 'Tidak ada data'
PRINTEDAT: 'Dicetak pada'
PRINTEDBY: 'Dicetak oleh'
PlaceHolder: 'Cari {type}'
PlaceHolderWithLabels: 'Cari {type} menurut {name}'
RelationSearch: 'Cari yang terkait'
ResetFilter: Reset
GridFieldAction_Delete:
DeletePermissionsFailure: 'Tidak ada ijin menghapus'
EditPermissionsFailure: 'Tidak ada ijin membuka tautan'
GridFieldDetailForm:
CancelBtn: Batal
Create: Buat
Delete: Hapus
DeletePermissionsFailure: 'Tidak ada ijin menghapus'
Deleted: '%s %s dihapus'
Save: Simpan
Saved: 'Simpan {name} {link}'
GridFieldEditButton_ss:
EDIT: Edit
GridFieldItemEditView:
Go_back: 'Kembali'
Group:
AddRole: 'Tambahkan peran untuk kelompok ini'
Code: 'Kode Kelompok'
DefaultGroupTitleAdministrators: Pengelola
DefaultGroupTitleContentAuthors: 'Penulis Konten'
Description: Deskripsi
GroupReminder: 'Jika Anda memilih kelompok induk, kelompok ini akan mengambil perannya'
HierarchyPermsError: 'Tidak dapat menghubungkan kelompok induk "%s" dengan perijinan khusus (memerlukan akses PENGELOLA)'
Locked: 'Terkunci?'
NoRoles: 'Tidak ada peran'
PLURALNAME: Kelompok
Parent: 'Kelompok Induk'
RolesAddEditLink: 'Kelola peran'
SINGULARNAME: Kelompok
Sort: 'Urutkan'
has_many_Permissions: Perijinan
many_many_Members: Pengguna
GroupImportForm:
Help1: '<p>Impor satu atau lebih kelompok di format <em>CSV</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Tampilkan penggunaan mahir</a></small></p>'
Help2: "<div class=\"advanced\">\n\t<h4>Penggunaan mahir</h4>\n\t<ul>\n\t<li>Kolom yang dibolehkan: <em>%s</em></li>\n\t<li>Kelompok yang sudah terdata dihubungkan dengan nilai <em>Kode</em> uniknya, dan diperbarui dengan nilai apapun dari berkas yang diimpor</li>\n\t<li>Hirarki kelompok dapat dibuat dengan kolom <em>ParentCode</em>.</li>\n\t<li>Kode perijinan dapat dihubungkan dengan kolom <em>PermissionCode</em>. Perijinan yang sudah ada tidak akan terpengaruh.</li>\n\t</ul>\n</div>"
ResultCreated: '{count} kelompok dibuat'
ResultDeleted: '%d kelompok dihapus'
ResultUpdated: '%d kelompok diperbarui'
Hierarchy:
InfiniteLoopNotAllowed: 'Putaran berulang ditemukan pada hirarki "{type}". Mohon ganti induk untuk mengatasi masalah ini'
HtmlEditorField:
ADDURL: 'Tambah URL'
ADJUSTDETAILSDIMENSIONS: 'Rincian &amp; dimensi'
ANCHORSCANNOTACCESSPAGE: 'Anda tidak dijinkan mengakses konten laman yang diminta.'
ANCHORSPAGENOTFOUND: 'Laman yang diminta tidak ditemukan.'
ANCHORVALUE: Jangkar
BUTTONADDURL: 'Tambah URL'
BUTTONINSERT: Sisip
BUTTONINSERTLINK: 'Sisip tautan'
BUTTONREMOVELINK: 'Hapus tautan'
BUTTONUpdate: Perbarui
CAPTIONTEXT: 'Keterangan'
CSSCLASS: 'Perataan / gaya teks'
CSSCLASSCENTER: 'Rata tengah, mandiri'
CSSCLASSLEFT: 'Rata kiri, dengan teks menyesuaikan.'
CSSCLASSLEFTALONE: 'Rata kiri, mandiri.'
CSSCLASSRIGHT: 'Rata kanan, degan teks menyesuaikan.'
DETAILS: Rincian
EMAIL: 'Alamat email'
FILE: Berkas
FOLDER: Folder
FROMCMS: 'Dari CMS'
FROMCOMPUTER: 'Dari komputer Anda'
FROMWEB: 'Dari situs lain'
FindInFolder: 'Temukan di Folder'
IMAGEALT: 'Teks alternatif (alt)'
IMAGEALTTEXT: 'Teks alternatif (alt) - pengganti jika gambar tidak tampil'
IMAGEALTTEXTDESC: 'Tampil ke pembaca layar, atau jika gambar tidak tampil'
IMAGEDIMENSIONS: Dimensi
IMAGEHEIGHTPX: Tinggi
IMAGETITLE: 'Teks judul (tooltip) - untuk informasi tambahan tentang gambar'
IMAGETITLETEXT: 'Teks gambar (tooltip)'
IMAGETITLETEXTDESC: 'Untuk informasi tambahan tentang gambar'
IMAGEWIDTHPX: Lebar
INSERTMEDIA: 'Sisipkan Media'
LINK: 'Sisipkan Tautan'
LINKANCHOR: 'Jangkar pada laman ini'
LINKDESCR: 'Deskripsi tautan'
LINKEMAIL: 'Alamat email'
LINKEXTERNAL: 'Situs lain'
LINKFILE: 'Unduh berkas'
LINKINTERNAL: 'Laman pada situs'
LINKOPENNEWWIN: 'Buka tautan di jendela baru?'
LINKTO: 'Tautan ke'
PAGE: Laman
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' tidak dapat dijadikan sumber daya media.'
UpdateMEDIA: 'Perbarui Media'
SUBJECT: 'Subyek email'
Image:
PLURALNAME: Berkas
SINGULARNAME: Berkas
Image_Cached:
PLURALNAME: Berkas
SINGULARNAME: Berkas
Image_iframe_ss:
TITLE: 'Iframe Pengunggahan Gambar'
LeftAndMain:
CANT_REORGANISE: 'Anda tidak diijinkan mengubah laman Tingkat Atas. Pengubahan Anda tidak tersimpan.'
DELETED: Terhapus.
DropdownBatchActionsDefault: Tindakan
HELP: Bantuan
PAGETYPE: 'Jenis laman:'
PERMAGAIN: 'Anda telah keluar dari situs. Jika ingin kembali masuk, isikan nama pengguna dan kata kunci di bawah ini.'
PERMALREADY: 'Maaf, Anda tidak dapat mengakses laman tersebut. Jika Anda ingin menggunakan akun lain, silakan masuk di sini'
PERMDEFAULT: 'Mohon pilih metode otentikasi dan isikan informasi login Anda.'
PLEASESAVE: 'Mohon Simpan Laman: Laman ini tidak dapat diperbarui karena belum disimpan.'
PreviewButton: Pratinjau
REORGANISATIONSUCCESSFUL: 'Pengaturan ulang struktur situs berhasil.'
SAVEDUP: Tersimpan.
ShowAsList: 'tampilkan sebagai daftar'
TooManyPages: 'Terlalu banyak laman'
ValidationError: 'Kesalahan validasi'
VersionUnknown: Tidak diketahui
LeftAndMain_Menu_ss:
Hello: Hai
LOGOUT: 'Keluar'
ListboxField:
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'
LoginAttempt:
Email: 'Alamat Email'
IP: 'Alamat IP'
PLURALNAME: 'Upaya Masuk'
SINGULARNAME: 'Upaya Masuk'
Status: Status
Member:
ADDGROUP: 'Tambah kelompok'
BUTTONCHANGEPASSWORD: 'Ganti Kata Kunci'
BUTTONLOGIN: 'Masuk'
BUTTONLOGINOTHER: 'Masuk dengan akun lain'
BUTTONLOSTPASSWORD: 'Saya lupa kata kuncinya'
CANTEDIT: 'Tidak ada ijin'
CONFIRMNEWPASSWORD: 'Konfirmasi Penggantian Kata Kunci'
CONFIRMPASSWORD: 'Konfirmasi Kata Kunci'
DATEFORMAT: 'Format tanggal'
DefaultAdminFirstname: 'Pengelola Utama'
DefaultDateTime: default
EMAIL: Email
EMPTYNEWPASSWORD: 'Kata kunci baru tidak boleh kosong, mohon coba lagi'
ENTEREMAIL: 'Mohon isikan alamat email untuk menerima tautan penggantian kata kunci.'
ERRORLOCKEDOUT2: 'Akun Anda untuk sementara diblok karena terlalu banyak upaya masuk yang gagal. Mohon coba lagi setelah {count} menit.'
ERRORNEWPASSWORD: 'Anda mengisikan kata kunci baru yang tidak sama, coba lagi'
ERRORPASSWORDNOTMATCH: 'Kata kunci Anda tidak sama, mohon coba lagi'
ERRORWRONGCRED: 'Rincian yang diisikan tidak benar. Mohon coba lagi.'
FIRSTNAME: 'Nama Depan'
INTERFACELANG: 'Bahasa Antarmuka'
INVALIDNEWPASSWORD: 'Kata kunci berikut tidak dapat diterima: {password}'
LOGGEDINAS: 'Anda masuk sebagai {name}.'
NEWPASSWORD: 'Kata Kunci Baru'
NoPassword: 'Tidak ada kata kunci untuk pengguna ini.'
PASSWORD: Kata kunci
PASSWORDEXPIRED: 'Kata kunci Anda telah kadaluarsa. Mohon buat yang baru.'
PLURALNAME: Pengguna
REMEMBERME: 'Ingat akun saya?'
SINGULARNAME: Pengguna
SUBJECTPASSWORDCHANGED: 'Kata kunci Anda telah diganti'
SUBJECTPASSWORDRESET: 'Tautan penggantian kata kunci Anda'
SURNAME: Nama Belakang
TIMEFORMAT: 'Format waktu'
VALIDATIONMEMBEREXISTS: 'Pengguna dengan %s yang sama sudah ada'
ValidationIdentifierFailed: 'Tidak dapat menimpa pengguna #{id} dengan pengenal yang sama ({name} = {value}))'
WELCOMEBACK: 'Selamat Datang kembali, {firstname}'
YOUROLDPASSWORD: 'Kata kunci lama'
belongs_many_many_Groups: Kelompok
db_LastVisited: 'Kunjungan Terakhir'
db_Locale: 'Lokal Antarmuka'
db_LockedOutUntil: 'Kunci sampai'
db_NumVisit: 'Jumlah Kunjungan'
db_Password: Kata kunci
db_PasswordExpiry: 'Tanggal Kadaluarsa'
MemberAuthenticator:
TITLE: 'E-mail &amp; Kata Kunci'
MemberDatetimeOptionsetField:
AMORPM: 'AM (Ante meridiem) atau PM (Post meridiem)'
Custom: Custom
DATEFORMATBAD: 'Format tanggal tidak benar'
DAYNOLEADING: 'Angka tanggal tanpa nol di depan'
DIGITSDECFRACTIONSECOND: 'Satu atau lebih angka merepresentasikan pecahan desimal dari detik'
FOURDIGITYEAR: 'Tahun empat angka'
FULLNAMEMONTH: 'Nama lengkap bulan (misalnya Juni)'
HOURNOLEADING: 'Angka jam tanpa nol di depan'
MINUTENOLEADING: 'Angka menit tanpa nol di depan'
MONTHNOLEADING: 'Angka bulan tanpa nol di depan'
Preview: Pratinjau
SHORTMONTH: 'Nama singkat bulan (misalnya Jun)'
TWODIGITDAY: 'Tanggal dua angka'
TWODIGITHOUR: 'Jam dua angka (00 sampai 23)'
TWODIGITMINUTE: 'Menit dua angka (00 sampai 59)'
TWODIGITMONTH: 'Bulan dua angka (01=Januari, dll)'
TWODIGITSECOND: 'Detik dua angka (00 sampai 59)'
TWODIGITYEAR: 'Tahun dua angka'
Toggle: 'Tampilkan bantuan pemformatan'
MemberImportForm:
Help1: '<p>Impor pengguna dalam <em>format CSV</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Tampilkan penggunaan mahir</a></small></p>'
Help2: "<div class=\"advanced\">\n\t<h4>Penggunaan mahir</h4>\n\t<ul>\n\t<li>Kolom yang dibolehkan: <em>%s</em></li>\n\t<li>Pengguna yang sudah terdata dihubungkan dengan nilai <em>Kode</em> uniknya, \n\tdan diperbarui dengan nilai apapun dari berkas yang diimpor.</li>\n\t<li>Kelompok dapat dihubungkan dengan kolom <em>Kelompok</em>. Kelompok diidentifikasi dengan properti <em>Kode</em>-nya,\n\tkelompok ganda dapat dipisahkan dengan tanda koma. Kelompok yang sudah terdata tidak terpengaruh.</li>\n\t</ul>\n\
</div>"
ResultCreated: '{count} pengguna dibuat'
ResultDeleted: '%d pengguna dihapus'
ResultNone: 'Tidak ada pengubahan'
ResultUpdated: '{count} pengguna diperbarui'
MemberPassword:
PLURALNAME: 'Kata Kunci'
SINGULARNAME: 'Kata Kunci'
MemberTableField:
APPLY_FILTER: 'Terapkan Saring'
ModelAdmin:
DELETE: Hapus
DELETEDRECORDS: '{count} data dihapus.'
EMPTYBEFOREIMPORT: 'Ganti data'
IMPORT: 'Impor dari CSV'
IMPORTEDRECORDS: '{count} data diimpor.'
NOCSVFILE: 'Mohon pilih berkas CSV untuk diimpor'
NOIMPORT: 'Tidak ada yang terimpor'
RESET: Reset
Title: 'Model Data'
UPDATEDRECORDS: '{count} data diperbarui.'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Kolom database'
IMPORTSPECLINK: 'Tampilkan Spesifikasi untuk %s'
IMPORTSPECRELATIONS: Keterkaitan
IMPORTSPECTITLE: 'Spesifikasi untuk %s'
ModelAdmin_Tools_ss:
FILTER: Saring
IMPORT: Impor
ModelSidebar_ss:
IMPORT_TAB_HEADER: Impor
SEARCHLISTINGS: Cari
MoneyField:
FIELDLABELAMOUNT: Jumlah
FIELDLABELCURRENCY: Mata Uang
NullableField:
IsNullLabel: 'N/A'
NumericField:
VALIDATION: '''{value}'' bukan angka, hanya angka yang dapat diterima isian ini'
Pagination:
Page: Laman
View: Tampilkan
PasswordValidator:
LOWCHARSTRENGTH: 'Mohon tingkatkan kekuatan kata kunci dengan menambah beberapa karakter berikut ini: %s'
PREVPASSWORD: 'Anda sudah pernah menggunakan kata kunci tersebut, mohon pilih kata kunci yang baru'
TOOSHORT: 'Kata kunci terlalu singkat, setidaknya harus terdiri dari %s karakter atau lebih'
Permission:
AdminGroup: Pengelola
CMS_ACCESS_CATEGORY: 'Akses CMS'
FULLADMINRIGHTS: 'Hak pengelolaan penuh'
FULLADMINRIGHTS_HELP: 'Abaikan dan kesampingkan semua perijinan yang terhubung lainnya.'
PLURALNAME: Perijinan
SINGULARNAME: Perijinan
PermissionCheckboxSetField:
AssignedTo: 'dihubungkan ke "{title}"'
FromGroup: 'diwarisi dari kelompok "{title}"'
FromRole: 'diwarisi dari peran "{title}"'
FromRoleOnGroup: 'diwarisi dari peran "%s" pada kelompok "%s"'
PermissionRole:
OnlyAdminCanApply: 'Hanya untuk pengelola'
PLURALNAME: Peran
SINGULARNAME: Peran
Title: Judul
PermissionRoleCode:
PermsError: 'Tidak dapat menghubungkan kode "%s" dengan perijinan khusus (memerlukan akses PENGELOLA)'
PLURALNAME: 'Kode Perijinan Peran'
SINGULARNAME: 'Kode Perijinan Peran'
Permissions:
PERMISSIONS_CATEGORY: 'Perijinan peran dan akses'
UserPermissionsIntro: 'Menghubungkan kelompok ke pengguna ini akan menyesuaikan perijinannya. Lihat bagian Kelompok untuk rincian perijinan pada kelompok individu.'
PhoneNumberField:
VALIDATION: 'Mohon isikan nomer telpon yang benar'
Security:
ALREADYLOGGEDIN: 'Anda tidak punya akses ke laman ini. Jika Anda punya akun lain dengan akses ke laman ini, silakan masuk kembali.'
BUTTONSEND: 'Kirimkan tautan penggantian kata kunci'
CHANGEPASSWORDBELOW: 'Anda dapat mengganti kata kunci di bawah ini.'
CHANGEPASSWORDHEADER: 'Ganti kata kunci'
ENTERNEWPASSWORD: 'Mohon isikan kata kunci yang baru.'
ERRORPASSWORDPERMISSION: 'Anda harus masuk untuk bisa mengganti kata kunci!'
LOGGEDOUT: 'Anda telah keluar. Jika ingin masuk kembali, isikan informasi akun Anda di sini.'
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.'
SecurityAdmin:
ACCESS_HELP: 'Bolehkan menampilkan, menambah dan mengedit pengguna, termasuk mengatur perijinan dan perannya.'
APPLY_ROLES: 'Terapkan peran ke kelompok'
APPLY_ROLES_HELP: 'Bolehkan mengedit peran yang diberikan ke kelompok. Memerlukan perijinan "Akses ke bagian ''Pengguna''".'
EDITPERMISSIONS: 'Kelola perijinan untuk kelompok'
EDITPERMISSIONS_HELP: 'Bolehkan mengedit Perijinan dan Alamat IP untuk kelompok. Memerlukan perijinan "Akses ke bagian ''Pengamanan''".'
GROUPNAME: 'Nama kelompok'
IMPORTGROUPS: 'Impor kelompok'
IMPORTUSERS: 'Impor pengguna'
MEMBERS: Pengguna
MENUTITLE: Pengamanan
MemberListCaution: 'Perhatian: Menghapus pengguna dari daftar ini akan menghapus mereka dari semua kelompok dan database'
NEWGROUP: 'Kelompok Baru'
PERMISSIONS: Perijinan
ROLES: Peran
ROLESDESCRIPTION: 'Peran adalah set perijinan yang sudah ditentukan, dan dapat dihubungkan ke kelompok.<br />Dapat diwariskan dari kelompok induk jika diperlukan.'
TABROLES: Peran
Users: Pengguna
SecurityAdmin_MemberImportForm:
BtnImport: 'Impor dari CSV'
FileFieldLabel: 'Berkas CSV <small>(Ekstensi yang dibolehkan: *.csv)</small>'
SilverStripeNavigator:
Auto: Otomatis
ChangeViewMode: 'Ganti modus tampil'
Desktop: Desktop
DualWindowView: 'Jendela Ganda'
Edit: Edit
EditView: 'Modus edit'
Mobile: Tampilan selular
PreviewState: 'Status Pratinjau'
PreviewView: 'Modus pratinjau'
Responsive: Tampilan responsif
SplitView: 'Modus terpisah'
Tablet: Tablet
ViewDeviceWidth: 'Pilih lebar pratinjau'
Width: lebar
SiteTree:
TABMAIN: Utama
TableListField:
CSVEXPORT: 'Ekspor ke CSV'
Print: Cetak
TableListField_PageControls_ss:
OF: dari
TimeField:
VALIDATEFORMAT: 'Isikan format waktu yang benar ({format})'
ToggleField:
LESS: kurang
MORE: lebih
UploadField:
ATTACHFILE: 'Lampirkan berkas'
ATTACHFILES: 'Lampirkan berkas'
AttachFile: 'Lampirkan berkas'
CHOOSEANOTHERFILE: 'Pilih berkas lain'
CHOOSEANOTHERINFO: 'Ganti berkas ini dengan berkas lain dari penyimpanan'
DELETE: 'Hapus dari berkas'
DELETEINFO: 'Hapus berkas secara permanen dari penyimpanan'
DOEDIT: Simpan
DROPFILE: 'taruh berkas'
DROPFILES: 'taruh berkas'
Dimensions: Dimensi
EDIT: Edit
EDITINFO: 'Edit berkas ini'
FIELDNOTSET: 'Informasi berkas tidak ada'
FROMCOMPUTER: 'Dari komputer Anda'
FROMCOMPUTERINFO: 'Pilih dari berkas'
FROMFILES: 'Dari berkas'
HOTLINKINFO: 'Informasi: Gambar ini akan ditautlangsungkan. Mohon pastikan Anda mendapat ijin dari pemilik situs untuk melakukannya.'
MAXNUMBEROFFILES: 'Jumlah maksimal {count} berkas terlampaui.'
MAXNUMBEROFFILESONE: 'Hanya dapat mengunggah satu berkas'
MAXNUMBEROFFILESSHORT: 'Dapat mengunggah {count} berkas'
OVERWRITEWARNING: 'Berkas dengan nama yang sama sudah ada'
REMOVE: Buang
REMOVEINFO: 'Hapus berkas ini dari sini, tapi jangan hapus dari penyimpanan'
STARTALL: 'Mulai semua'
Saved: Tersimpan
UPLOADSINTO: 'disimpan ke /{path}'
Versioned:
has_many_Versions: Versi
CheckboxSetField:
SOURCE_VALIDATION: 'Mohon pilih nilai dari daftar yang ada. ''{value}'' bukan pilihan valid'

View File

@ -60,6 +60,8 @@ lt:
ERRORNOTREC: 'Toks vartotojo vardas / slaptažodis neatpažintas'
Boolean:
ANY: Bet koks
NOANSWER: 'Ne'
YESANSWER: 'Taip'
CMSLoadingScreen_ss:
LOADING: Keliama...
REQUIREJS: 'Šiai TVS būtina įjungti JavaScript.'
@ -78,6 +80,23 @@ lt:
EMAIL: El. paštas
HELLO: Sveiki
PASSWORD: Slaptažodis
CheckboxField:
NOANSWER: 'Ne'
YESANSWER: 'Taip'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Prašome pasirinkti reikšmę iš pateikto sąrašo. ''{value}'' yra negalima reikšmė.'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Pamiršote slaptažodį?'
BUTTONLOGIN: 'Prisijungti'
BUTTONLOGOUT: 'Atsijungti'
PASSWORDEXPIRED: '<p>Jūsų slaptažodžio galiojimas pasibaigė. <a target="_top" href="{link}">Prašome sukurti naują.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Blogas vartotojas. Norėdami tęsti, prašome <a target="_top" href="{link}">prisijungti</a> iš naujo.</p>'
LoginMessage: '<p>Jei dar neišsaugojote padarytus pakeitimus, jūs galėsite tęsti darbą, prisijungę žemiau esančioje formoje.</p>'
SUCCESS: Sėkmingai
SUCCESSCONTENT: '<p>Sėkmingai prisijungėte. Jeigu jūsų automatiškai nenukreipia, <a target="_top" href="{link}">spauskite čia</a></p>'
TimedOutTitleAnonymous: 'Jūsų prisijungimo galiojimas pasibaigė.'
TimedOutTitleMember: 'Sveiki, {name}!<br />Jūsų prisijungimo galiojimas pasibaigė.'
ConfirmedPasswordField:
ATLEAST: 'Slaptažodžiai privalo būti bent {min} simbolių ilgio.'
BETWEEN: 'Slaptažodžiai privalo būti nuo {min} iki {max} simbolių ilgio.'
@ -124,6 +143,7 @@ lt:
DropdownField:
CHOOSE: (Pasirinkti)
CHOOSESEARCH: '(Pasirinkti arba Ieškoti)'
SOURCE_VALIDATION: 'Prašome pasirinkti reikšmę iš pateikto sąrašo. ''{value}'' yra negalima reikšmė.'
EmailField:
VALIDATION: 'Prašome suvesti el. pašto adresą'
Enum:
@ -171,7 +191,9 @@ lt:
TEXT2: 'slaptažodžio atstatymo nuoroda'
TEXT3: svetainei
Form:
CSRF_FAILED_MESSAGE: "Iškilo techninė problema. Prašome paspausti mygtuką Atgal,\nperkraukite naršyklės langą ir bandykite vėl."
CSRF_FAILED_MESSAGE: 'Iškilo techninė problema. Prašome paspausti mygtuką Atgal,
perkraukite naršyklės langą ir bandykite vėl.'
FIELDISREQUIRED: '{name} yra privalomas'
SubmitBtnLabel: Vykdyti
VALIDATIONCREDITNUMBER: 'Prašome įsitikinti, ar teisingai suvedėte kreditinės kortelės numerį {number}'
@ -238,7 +260,8 @@ lt:
many_many_Members: Vartotojai
GroupImportForm:
Help1: '<p>Importuoti vieną ar kelias grupes <em>CSV</em> formatu (kableliu atskirtos reikšmės). <small><a href="#" class="toggle-advanced">Rodyti detalesnį aprašymą</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Detalesnis aprašymas</h4>\n<ul>\n<li>Galimi stulpeliai: <em>%s</em></li>\n<li>Esamos grupės yra surišamos su jų unikalia <em>Code</em> reikšme ir atnaujinamos duomenimis iš importuojamos bylos</li>\n<li>Grupių hierarchija gali būti sukurta naudojant <em>ParentCode</em> stulpelį.</li>\n<li>Leidimų gali būti priskirti naudojant <em>PermissionCode</em> stulpelį. Esami leidimai nebus pakeisti.</li>\n</ul>\n</div>"
Help2: "<div class=\"advanced\">\n\t<h4>Sudėtingesni pasirinkimai</h4>\n\t<ul>\n\t<li>Galimi stulpeliai: <em>%s</em></li>\n\t<li>Esamos grupės yra surišamos su jų unikalia <em>Code</em> reikšme ir atnaujinamos duomenimis iš importuojamos bylos</li>\n\t<li>Grupių hierarchija gali būti sukurta naudojant <em>ParentCode</em> stulpelį.</li>\n\t<li>Leidimų kodai gali būti priskirti naudojant <em>PermissionCode</em> stulpelį. Esami leidimai nebus pakeisti.</li>\n\t</ul>\n\
</div>"
ResultCreated: 'Sukurta {count} grupių'
ResultDeleted: 'Ištrinta %d grupių'
ResultUpdated: 'Atnaujinta %d grupių'
@ -247,6 +270,8 @@ lt:
HtmlEditorField:
ADDURL: 'Pridėti URL'
ADJUSTDETAILSDIMENSIONS: 'Detalesnė inf. ir matmenys'
ANCHORSCANNOTACCESSPAGE: 'Neturite teisių pasiekti pasirinkto puslapio turinį.'
ANCHORSPAGENOTFOUND: 'Pasirinktas puslapis nerastas.'
ANCHORVALUE: Nuoroda
BUTTONADDURL: 'Pridėti URL'
BUTTONINSERT: Įterpti
@ -290,6 +315,7 @@ lt:
URL: URL adresas
URLNOTANOEMBEDRESOURCE: 'Nepavyko URL nuorodos ''{url}'' panaudoti media turiniui.'
UpdateMEDIA: 'Atnaujinti media'
SUBJECT: 'El. laiško tema'
Image:
PLURALNAME: Bylos
SINGULARNAME: Byla
@ -318,6 +344,8 @@ lt:
LeftAndMain_Menu_ss:
Hello: Sveiki
LOGOUT: 'Atsijungti'
ListboxField:
SOURCE_VALIDATION: 'Prašome pasirinkti reikšmę iš pateikto sąrašo. ''{value}'' yra negalima reikšmė.'
LoginAttempt:
Email: 'E. pašto adresas'
IP: 'IP adresas'
@ -350,6 +378,7 @@ lt:
NEWPASSWORD: 'Naujas slaptažodis'
NoPassword: 'Šis vartotojas neturi slaptažodžio.'
PASSWORD: Slaptažodis
PASSWORDEXPIRED: 'Jūsų slaptažodžio galiojimas pasibaigė. Prašome sukurti naują.'
PLURALNAME: Vartotojai
REMEMBERME: 'Prisiminti jungiantis kitą kartą?'
SINGULARNAME: Vartotojas
@ -456,8 +485,8 @@ lt:
SINGULARNAME: Rolė
Title: Pavadinimas
PermissionRoleCode:
PLURALNAME: 'Leidimų rolių kodai'
PermsError: 'Nepavyko priskirto kodo "%s" su priskirtais leidimais (būtina ADMIN prieiga)'
PLURALNAME: 'Leidimų rolių kodai'
SINGULARNAME: 'Leidimų rolių kodai'
Permissions:
PERMISSIONS_CATEGORY: 'Rolės ir priėjimo leidimai'
@ -557,3 +586,5 @@ lt:
UPLOADSINTO: 'saugo į /{path}'
Versioned:
has_many_Versions: Versijos
CheckboxSetField:
SOURCE_VALIDATION: 'Prašome pasirinkti reikšmę iš pateikto sąrašo. ''{value}'' yra negalima reikšmė.'

View File

@ -60,6 +60,8 @@ sk:
ERRORNOTREC: 'Toto používateľské meno / heslo nebolo rozpoznané'
Boolean:
ANY: Ktorýkoľvek
NOANSWER: 'Nie'
YESANSWER: 'Áno'
CMSLoadingScreen_ss:
LOADING: Načíta sa ...
REQUIREJS: 'CMS vyžaduje, aby ste mali JavaScript zapnutý.'
@ -78,6 +80,23 @@ sk:
EMAIL: E-mail
HELLO: Dobrý deň
PASSWORD: Heslo
CheckboxField:
NOANSWER: 'Nie'
YESANSWER: 'Áno'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v zozname. {value} nie je platná voľba'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Zabudnuté heslo?'
BUTTONLOGIN: 'Prihlásiť sa späť'
BUTTONLOGOUT: 'Odhlásiť sa'
PASSWORDEXPIRED: '<p>Vaše heslo bolo expirované. <a target="_top" href="{link}">Prosím zvoľte nové heslo.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Neplatný užívateľ. <a target="_top" href="{link}">Prosím overte sa znovu tu</a> pre pokračovanie.</p>'
LoginMessage: '<p>Ak máte akúkoľvek neuloženú prácu, môžete sa vrátiť na miesto, kde ste prestali, prihlásením sa späť nižšie.</p>'
SUCCESS: Úspešné
SUCCESSCONTENT: '<p>Úspešné prihlásenie. Ak nebudete automaticky presmerovaní <a target="_top" href="{link}">kliknite tu</a></p>'
TimedOutTitleAnonymous: 'Čas Vášho sedenia vypršal.'
TimedOutTitleMember: 'Ahoj {name}!<br />Čas Vášho sedenia vypršal.'
ConfirmedPasswordField:
ATLEAST: 'Heslá musia byť nejmenej {min} znakov dlhé.'
BETWEEN: 'Heslá musia byť {min} až {max} znakov dlhé.'
@ -124,6 +143,7 @@ sk:
DropdownField:
CHOOSE: (Vyberte si)
CHOOSESEARCH: '(Vybrať alebo vyhľadať)'
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v zozname. {value} nie je platná voľba'
EmailField:
VALIDATION: 'Prosím zadajte email adresu'
Enum:
@ -171,7 +191,7 @@ sk:
TEXT2: 'odkaz na resetovanie hesla'
TEXT3: pre
Form:
CSRF_FAILED_MESSAGE: "Zdá sa, že nastal technický problém. Prosím, kliknite na tlačidlo Späť,, \n\t\t\t\t\taktualizujte prehliadač a skúste to znova."
CSRF_FAILED_MESSAGE: 'Vyzerá to, že to musí být technický problem. Kliknite prosím na tlačítko späť, obnovte váš prehliadač, a skúste opäť.'
FIELDISREQUIRED: '{name} je požadované'
SubmitBtnLabel: Choď
VALIDATIONCREDITNUMBER: 'Uistite sa, že ste zadali číslo {number} kreditnej karty správne'
@ -238,7 +258,7 @@ sk:
many_many_Members: Členovia
GroupImportForm:
Help1: 'Importovať jednu alebo viac skupín v CSV formáte (čiarkov oddelené hodnoty). Zobraziť pokročilé použitie'
Help2: "<div class=\"advanced\">\n<h4>Pokročilé použitie</h4>\n<ul>\n<li>Povolené stĺpce: <em>%s</em></li>\n<li>Existujúce skupiny sú porovnávané ich unikátnou vlastnostou <em>Code</em>, a aktualizované s novými hodnotami z\nimportovaného súboru.</li>\n<li>Hierarchia skupín môže byť tvorená použitím stĺpce <em>ParentCode</em>.</li>\n<li>Kódy oprávnení môžu byť priradené stĺpcom <em>PermissionCode</em>. Existujúce oprávnenia nie sú smazáné.</li>\n</ul>\n</div>"
Help2: "<div class=\"advanced\">\n\t<h4>Pokročilé použitie</h4>\n\t<ul>\n\t<li>Povolené stĺpce: <em>%s</em></li>\n\t<li>Existujúce skupiny sú porovnávané s ich unikátnou <em>Code</em> hodnotou, a aktualizované s novými hodnotami z importovaného súbory</li>\n\t<li>Skupina hierarchií môže byť tvorená použitím <em>ParentCode</em> stĺpce.</li>\n\t<li>Kódy oprávnení môžu byť priradené <em>PermissionCode</em> stĺpcom. Existujúce oprávnenia nie sú smazáné.</li>\n\t</ul>\n</div>"
ResultCreated: 'Vytvorených {count} skupín'
ResultDeleted: 'Zmazané %d skupiny'
ResultUpdated: 'Aktualizované %d skupiny'
@ -247,6 +267,8 @@ sk:
HtmlEditorField:
ADDURL: 'Pridať URL'
ADJUSTDETAILSDIMENSIONS: 'Detaily &amp; rozmery'
ANCHORSCANNOTACCESSPAGE: 'Nemáte povolený prístup k obsahu cieľovej stránky.'
ANCHORSPAGENOTFOUND: 'Cielova stránka nebola nájdená.'
ANCHORVALUE: Odkaz
BUTTONADDURL: 'Pridať url'
BUTTONINSERT: Vložiť
@ -290,6 +312,7 @@ sk:
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' nemôže byť vložené do zdroja médií.'
UpdateMEDIA: 'Aktualizovať média'
SUBJECT: 'Predmet emailu'
Image:
PLURALNAME: Súbory
SINGULARNAME: Súbor
@ -318,6 +341,8 @@ sk:
LeftAndMain_Menu_ss:
Hello: Ahoj
LOGOUT: 'Odhlásiť sa'
ListboxField:
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v zozname. {value} nie je platná voľba.'
LoginAttempt:
Email: 'Emailová adresa'
IP: 'IP adreasa'
@ -350,6 +375,7 @@ sk:
NEWPASSWORD: 'Nové heslo'
NoPassword: 'Nie je tu heslo pre tohto člena.'
PASSWORD: Heslo
PASSWORDEXPIRED: 'Vaše heslo bolo expirované. Zvoľte nové heslo.'
PLURALNAME: Členovia
REMEMBERME: 'Pamätať si ma nabudúce?'
SINGULARNAME: Člen
@ -456,8 +482,8 @@ sk:
SINGULARNAME: Úloha
Title: Názov
PermissionRoleCode:
PLURALNAME: 'Kódy právomocí úloh'
PermsError: 'Nie je možné pripojiť kód "%s" s privilegovanými právami (vyžaduje ADMIN prístup)'
PLURALNAME: 'Kódy právomocí úloh'
SINGULARNAME: 'Kód právomocí úloh'
Permissions:
PERMISSIONS_CATEGORY: 'Úlohy a prístupové práva'
@ -557,3 +583,5 @@ sk:
UPLOADSINTO: 'uloží do /{path}'
Versioned:
has_many_Versions: verzie
CheckboxSetField:
SOURCE_VALIDATION: 'Prosím vyberte hodnotu v zozname. ''{value}'' nie je platná voľba'

553
lang/sr@latin.yml Normal file
View File

@ -0,0 +1,553 @@
sr@latin:
AssetAdmin:
NEWFOLDER: Nova fascikla
SHOWALLOWEDEXTS: 'Prikaži dozvoljene ekstenzije'
AssetTableField:
CREATED: 'Prvo dostavljeno'
DIM: Dimenzije
FILENAME: Ime datoteke
FOLDER: Fascikla
LASTEDIT: 'Poslednje promenjeno'
OWNER: Vlasnik
SIZE: 'Veličina'
TITLE: Naslov
TYPE: 'Tip'
URL: URL
AssetUploadField:
ChooseFiles: 'Izaberi datoteke'
DRAGFILESHERE: 'Prevuci datoteke ovde'
DROPAREA: 'Područje za ispuštanje'
EDITALL: 'Izmeni sve'
EDITANDORGANIZE: 'Izmeni i organizuj'
EDITINFO: 'Izmeni datoteke'
FILES: Datoteke
FROMCOMPUTER: 'Izaberite datoteke sa Vašeg računara'
FROMCOMPUTERINFO: 'Postavi sa Vašeg računara'
TOTAL: Ukupno
TOUPLOAD: 'Izaberite datoteke za postavljanje...'
UPLOADINPROGRESS: 'Molimo Vas da sačekate... Postavljanje je u toku'
UPLOADOR: ILI
BBCodeParser:
ALIGNEMENT: Poravnanje
ALIGNEMENTEXAMPLE: 'poravnat uz desnu stranu'
BOLD: 'Podebljan tekst'
BOLDEXAMPLE: Podebljano
CODE: 'Blok kôda'
CODEDESCRIPTION: 'Blok neformatizovanog kôda'
CODEEXAMPLE: 'Blok kôda'
COLORED: 'Obojen tekst'
COLOREDEXAMPLE: 'plavi tekst'
EMAILLINK: 'Veza e-pošte'
EMAILLINKDESCRIPTION: 'Napravite link do adrese e-pošte'
IMAGE: Slika
IMAGEDESCRIPTION: 'Prikaži sliku u mojoj poruci'
ITALIC: 'Iskošen tekst'
ITALICEXAMPLE: Iskošeno
LINK: 'Link veb sajta'
LINKDESCRIPTION: 'Link do drugog veb sajta ili URL'
STRUCK: 'Precrtan tekst'
STRUCKEXAMPLE: Precrtano
UNDERLINE: 'Podvučen tekst'
UNDERLINEEXAMPLE: Podvučeno
UNORDERED: 'Neuređena lista'
UNORDEREDDESCRIPTION: 'Neuređena lista'
UNORDEREDEXAMPLE1: 'stavka 1 neuređene liste'
BackLink_Button_ss:
Back: Nazad
BasicAuth:
ENTERINFO: 'Unesite korisničko ime i lozinku.'
ERRORNOTADMIN: 'Ovaj korisnik nije administrator.'
ERRORNOTREC: 'To korisničko ime / lozinka nije prepoznato'
Boolean:
ANY: Bilo koja
CMSLoadingScreen_ss:
LOADING: Učitavanje...
REQUIREJS: 'CMS zahteva omogućen JavaScript.'
CMSMain:
ACCESS: 'Pristup ''{title}'' sekciji'
ACCESSALLINTERFACES: 'Pristup svim sekcijama CMS-a'
ACCESSALLINTERFACESHELP: 'Nadjačava specifičnija podešavanja pristupa.'
SAVE: Sačuvaj
CMSPageHistoryController_versions_ss:
PREVIEW: 'Prethodni pregled veb sajta'
CMSProfileController:
MENUTITLE: 'Moj profil'
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Promenili ste svoju lozinku za '
CHANGEPASSWORDTEXT2: 'Sada možete da koristite sledeće podatke za prijavljivanje:'
EMAIL: E-pošta
HELLO: Zdravo
PASSWORD: Lozinka
ConfirmedPasswordField:
ATLEAST: 'Lozinka mora sadržati najmanje {min} znakova.'
BETWEEN: 'Lozinka mora sadržati najmanje {min}, a najviše {max} znakova.'
MAXIMUM: 'Lozinka može sadržati najviše {max} znakova.'
SHOWONCLICKTITLE: 'Promeni lozinku'
ContentController:
NOTLOGGEDIN: 'Niste prijavljeni'
CreditCardField:
FIRST: prvi
FOURTH: četvrti
SECOND: drugi
THIRD: treći
CurrencyField:
CURRENCYSYMBOL: din.
DataObject:
PLURALNAME: 'Objekti podataka'
SINGULARNAME: 'Objekat podataka'
Date:
DAY: dan
DAYS: dan/a
HOUR: sat
HOURS: sata/a
LessThanMinuteAgo: 'manje od minute'
MIN: minut
MINS: minuta
MONTH: mesec
MONTHS: meseci
SEC: sekunda
SECS: sekundi
TIMEDIFFAGO: '{difference} ranije'
TIMEDIFFIN: 'kroz {difference}'
YEAR: godina
YEARS: godinâ
DateField:
NOTSET: 'nije podešeno'
TODAY: danas
VALIDDATEFORMAT2: 'Molimo Vas da unesete ispravan format datuma ({format})'
VALIDDATEMAXDATE: 'Datum ne sme biti posle ({date})'
VALIDDATEMINDATE: 'Datum ne sme biti pre ({date})'
DatetimeField:
NOTSET: 'nije podešeno'
Director:
INVALID_REQUEST: 'Pogrešan zahtev'
DropdownField:
CHOOSE: (izaberite)
CHOOSESEARCH: '(Izaberi ili Pronađi)'
EmailField:
VALIDATION: 'Unesite adresu e-pošte'
Enum:
ANY: Bilo koji
File:
AviType: 'AVI video datotoeka'
Content: Sadržaj
CssType: 'CSS datoteka'
DmgType: 'Apple slika diska'
DocType: 'Word dokument'
Filename: Ime datoteke
GifType: 'GIF slika - dobro za dijagrame'
GzType: 'GZIP arhiva'
HtlType: 'HTML datoteka'
HtmlType: 'HTML datoteka'
INVALIDEXTENSION: 'Ekstenzija nije dozvoljena (dozvoljene: {extensions})'
INVALIDEXTENSIONSHORT: 'Ekstenzija nije dozvoljena'
IcoType: 'Ikona'
JpgType: 'JPEG slika - dobro za fotografije'
JsType: 'Javascript datoteka'
Mp3Type: 'MP3 audio datoteka'
MpgType: 'MPEG video datoteka'
NOFILESIZE: 'Datoteka je veličine 0 B.'
NOVALIDUPLOAD: 'Datoteka za prenos nije valjana'
Name: Ime
PLURALNAME: Datoteke
PdfType: 'Adobe Acrobat PDF datoteka'
PngType: 'PNG slika - dobar format opšte namene'
SINGULARNAME: Datoteka
TOOLARGE: 'Datoteka je prevelika; maksimalna dozvoljena veličina je {size}'
TOOLARGESHORT: 'Veličina datoteke premašuje {size}'
TiffType: 'Označeni format slike'
Title: Naslov
WavType: 'WAV audio datoteka'
XlsType: 'Excel dokument'
ZipType: 'ZIP arhiva'
Filesystem:
SYNCRESULTS: 'Sinhronizacija je završena: {createdcount} stavki je kreirano, {deletedcount} stavki je izbrisano'
Folder:
PLURALNAME: Fascikle
SINGULARNAME: Fascikla
ForgotPasswordEmail_ss:
HELLO: Zdravo
TEXT1: 'Evo ga Vaš'
TEXT2: 'link za resetovanje lozinke'
TEXT3: za
Form:
FIELDISREQUIRED: '{name} je obavezno'
SubmitBtnLabel: Idi
VALIDATIONCREDITNUMBER: 'Uverite se da ste ispravno uneli {number} broj kreditne kartice'
VALIDATIONNOTUNIQUE: 'Uneta vrednost nije jedinstvena'
VALIDATIONPASSWORDSDONTMATCH: 'Lozinke se ne poklapaju'
VALIDATIONPASSWORDSNOTEMPTY: 'Polja za lozinke ne smeju da budu prazna'
VALIDATIONSTRONGPASSWORD: 'Lozinke moraju ra sadrže bar jednu cifru i bar jedan alfanumerički znak'
VALIDATOR: Proverivač ispravnosti
VALIDCURRENCY: 'Unesite ispravnu valutu'
FormField:
Example: 'npr. %s'
NONE: bez
GridAction:
DELETE_DESCRIPTION: Izbriši
Delete: Izbriši
UnlinkRelation: Raskini link
GridField:
Add: 'Dodaj {name}'
Filter: Filter
FilterBy: 'Filtriraj po'
Find: Pronađi
LEVELUP: 'Nivo iznad'
LinkExisting: 'Postojanje linka'
NewRecord: 'Novi %s'
NoItemsFound: 'Nijedna stavka nije pronađena'
PRINTEDAT: 'Odštampano'
PRINTEDBY: 'Odštampao'
PlaceHolder: 'Pronađi {type}'
PlaceHolderWithLabels: 'Pronađi {type} po {name}'
RelationSearch: 'Pretraživanje relacije'
ResetFilter: Vrati u pređašnje stanje
GridFieldAction_Delete:
DeletePermissionsFailure: 'Nemate dozvolu za brisanje'
EditPermissionsFailure: 'Nemate dozvolu da raskinete link sa zapisom'
GridFieldDetailForm:
CancelBtn: Odustani
Create: Kreiraj
Delete: Izbriši
DeletePermissionsFailure: 'Nemate pravo brisanja'
Deleted: 'Izbrisano %s %s'
Save: Sačuvaj
Saved: 'Sačuvano {name} {link}'
GridFieldEditButton_ss:
EDIT: Izmeni
GridFieldItemEditView:
Go_back: 'Vrati se nazad'
Group:
AddRole: 'Dodaj ulogu za ovu grupu'
Code: 'Kôd grupe'
DefaultGroupTitleAdministrators: Administratori
DefaultGroupTitleContentAuthors: 'Autori sadržaja'
Description: Opis
GroupReminder: 'Ako izaberete roditeljsku grupu, ova grupa će preuzeti sve njene uloge'
HierarchyPermsError: 'Nije moguće dodeliti roditeljsku grupu "%s" sa privilegovanim dozvolama (zahteva Administratorski pristup)'
Locked: 'Zaključano?'
NoRoles: 'Uloge nisu pronađene'
PLURALNAME: Grupe
Parent: 'Roditeljska grupa'
RolesAddEditLink: 'Upravljaj ulogama'
SINGULARNAME: Grupa
Sort: 'Poredak sortiranja'
has_many_Permissions: Dozvole
many_many_Members: Članovi
GroupImportForm:
Help1: '<p>Uvezi jednu ili više grupa u <em>CSV</em> formatu (zarezima razdvojene vrednosti). <small><a href="#" class="toggle-advanced">Prikaži napredno korišćenje</a></small></p>'
ResultCreated: 'Kreirano {count} grupa'
ResultDeleted: 'Izbrisao %d grupa'
ResultUpdated: 'Ažurirano %d grupa'
Hierarchy:
InfiniteLoopNotAllowed: 'Otkrivena je beskonačna petlja u okviru "{type}" hijerarhije. Promenite roditelja da bi ste razrešili situaciju'
HtmlEditorField:
ADDURL: 'Dodaj URL'
ADJUSTDETAILSDIMENSIONS: 'Detalji &amp; dimenzije'
ANCHORVALUE: Sidro
BUTTONADDURL: 'Dodaj URL'
BUTTONINSERT: Umetni
BUTTONINSERTLINK: 'Umetni link'
BUTTONREMOVELINK: 'Ukloni link'
BUTTONUpdate: Ažuriraj
CAPTIONTEXT: 'Tekst oznake'
CSSCLASS: 'Poravnanje / stil'
CSSCLASSCENTER: 'Centrirano, samo za sebe.'
CSSCLASSLEFT: 'Sa leve strane, sa tekstom prelomljenim okolo.'
CSSCLASSLEFTALONE: 'Sa leve strane, samo za sebe'
CSSCLASSRIGHT: 'Sa desne strane, sa tekstom prelomljenim okolo.'
DETAILS: Detalji
EMAIL: 'Adresa e-pošte'
FILE: Datoteka
FOLDER: Fascikla
FROMCMS: 'Iz CMS-a'
FROMCOMPUTER: 'Sa Vašeg računara'
FROMWEB: 'Sa veba'
FindInFolder: 'Pronađi u fascikli'
IMAGEALT: 'Alternativni tekst (alt)'
IMAGEALTTEXT: 'Alternativni tekst (alt) - prikazuje se ako slika ne može biti prikazana'
IMAGEALTTEXTDESC: 'Prikazuje se čitačima ekrana ili ako slika ne može biti prikazana'
IMAGEDIMENSIONS: Dimenzije
IMAGEHEIGHTPX: Visina
IMAGETITLE: 'Tekst naslova (tooltip) - za dodatne informacije o slici'
IMAGETITLETEXT: 'Tekst naslova (tooltip)'
IMAGETITLETEXTDESC: 'Za dodatne informacije o slici'
IMAGEWIDTHPX: Širina
INSERTMEDIA: 'Umetni medijski resurs'
LINK: 'Link'
LINKANCHOR: 'Sidro na ovoj strani'
LINKDESCR: 'Opis linka'
LINKEMAIL: 'Adresa e-pošte'
LINKEXTERNAL: 'drugi vebsajt'
LINKFILE: 'Preuzmi datoteku'
LINKINTERNAL: 'stranu na sajtu'
LINKOPENNEWWIN: 'Otvoriti link u novom prozoru?'
LINKTO: 'Poveži na'
PAGE: Stranica
URL: URL
URLNOTANOEMBEDRESOURCE: 'URL ''{url}'' ne može biti pretvoren u medijski resurs.'
UpdateMEDIA: 'Ažuriraj medijski resurs'
Image:
PLURALNAME: Datoteke
SINGULARNAME: Datoteka
Image_Cached:
PLURALNAME: Datoteke
SINGULARNAME: Datoteka
Image_iframe_ss:
TITLE: 'Iframe za dostavljanje slika'
LeftAndMain:
CANT_REORGANISE: 'Nemate pravo da menjate stranice vršnog nivoa. Vaše izmene nisu sačuvane.'
DELETED: Izbrisano
DropdownBatchActionsDefault: Akcije
HELP: Pomoć
PAGETYPE: 'Tip stranice'
PERMAGAIN: 'Odjavljeni ste sa CMS-a. Ukoliko želite da se ponovo prijavite, unesite korisničko ime i lozinku.'
PERMALREADY: 'Ne možete da pristupite ovom delu CMS-a. Ako želite da se prijavite kao neko drugi, uradite to ispod'
PERMDEFAULT: 'Izaberite metodu autentifikacije i unesite podatke za pristup CMS-u.'
PreviewButton: Prethodni pregled
REORGANISATIONSUCCESSFUL: 'Stablo sajta je uspešno reorganizovano.'
SAVEDUP: Sačuvano.
ShowAsList: 'prikaži u vidu liste'
TooManyPages: 'Previše stranica'
ValidationError: 'Grešla pri proveri ispravnosti'
VersionUnknown: Nepoznato
LeftAndMain_Menu_ss:
Hello: Zdravo
LOGOUT: 'Odjavi se'
LoginAttempt:
Email: 'Adresa e-pošte'
IP: 'IP adresa'
PLURALNAME: 'Pokušaji prijave'
SINGULARNAME: 'Pokušaj prijave'
Status: Status
Member:
ADDGROUP: 'Dodaj grupu'
BUTTONCHANGEPASSWORD: 'Izmeni lozinku'
BUTTONLOGIN: 'Prijavi se'
BUTTONLOGINOTHER: 'Prijavite se kao neko drugi'
BUTTONLOSTPASSWORD: 'Zaboravio sam lozinku'
CANTEDIT: 'Nemate dozvolu da uradite to'
CONFIRMNEWPASSWORD: 'Potvrdite novu lozinku'
CONFIRMPASSWORD: 'Potvrdite lozinku'
DATEFORMAT: 'Format datuma'
DefaultAdminFirstname: 'Podrazumevani administrator'
DefaultDateTime: podrazumevano
EMAIL: E-pošta
EMPTYNEWPASSWORD: 'Nova lozinka ne može biti prazna. Pokušajte ponovo.'
ENTEREMAIL: 'Unesite adresu e-pošte da bi ste dobili link za resetovanje lozinke.'
ERRORLOCKEDOUT2: 'Vaš nalog je privremeno suspendovan zbog velikog broja neuspešnih pokušaja prijave. Pokušajte ponovo za {count} minuta.'
ERRORNEWPASSWORD: 'Nova lozinka koju ste uneli se ne poklapa. Pokušajte ponovo.'
ERRORPASSWORDNOTMATCH: 'Vaša trenutna lozinka se ne poklapa. Pokušajte ponovo.'
ERRORWRONGCRED: 'Pruženi detalji izgleda nisu korektni. Pokušajte ponovo.'
FIRSTNAME: 'Ime'
INTERFACELANG: 'Jezik interfejsa'
INVALIDNEWPASSWORD: 'Nismo mogli da prihvatimo lozinku: {password}'
LOGGEDINAS: 'Prijavljeni ste kao {name}.'
NEWPASSWORD: 'Nova lozinka'
NoPassword: 'Ne postoji lozinka za tog člana.'
PASSWORD: Lozinka
PLURALNAME: Članovi
REMEMBERME: 'Zapamti me za sledeći put'
SINGULARNAME: Član
SUBJECTPASSWORDCHANGED: 'Vaša lozinka je promenjena'
SUBJECTPASSWORDRESET: 'Link za resetovanje Vaše lozinke'
SURNAME: Prezime
TIMEFORMAT: 'Format vremena'
VALIDATIONMEMBEREXISTS: 'Već postoji član sa ovom adresom e-pošte'
ValidationIdentifierFailed: 'Nije moguće prepisati preko postojećeg člana #{id} sa istim identifikatorom ({name} = {value}))'
WELCOMEBACK: 'Dobro došli ponovo, {firstname}'
YOUROLDPASSWORD: 'Vaša stara lozinka'
belongs_many_many_Groups: Grupe
db_LastVisited: 'Datum poslednje posete'
db_Locale: 'Lokalitet interfejsa'
db_LockedOutUntil: 'Zaključan do'
db_NumVisit: 'Broj poseta'
db_Password: Lozinka
db_PasswordExpiry: 'Datum isteka lozinke'
MemberAuthenticator:
TITLE: 'Pošalji lozinku'
MemberDatetimeOptionsetField:
AMORPM: 'AM (Ante meridiem) or PM (Post meridiem)'
Custom: Prilagođen
DATEFORMATBAD: 'Neispravan format datuma'
DAYNOLEADING: 'Dan u mesecu bez vodeće nule'
DIGITSDECFRACTIONSECOND: 'Jedna ili više cifara koje predstaljaju deseti deo sekunde'
FOURDIGITYEAR: 'Četvorocifrena godina'
FULLNAMEMONTH: 'Puno ime meseca (npr. Jun)'
HOURNOLEADING: 'Sati bez vodeće nule'
MINUTENOLEADING: 'Minute bez vodeće nule'
MONTHNOLEADING: 'Mesec bez vodeće nule'
Preview: Prethodni pregled
SHORTMONTH: 'Kratko ime meseca (npr. Sept)'
TWODIGITDAY: 'Dvocifreni dan meseca'
TWODIGITHOUR: 'Dve cifre sati (00 do 23)'
TWODIGITMINUTE: 'Dve cifre minuta (00 do 59)'
TWODIGITMONTH: 'Dvocifreni mesec (01=Januar itd)'
TWODIGITSECOND: 'Dve cifre sekundi (00 do 59)'
TWODIGITYEAR: 'Dvocifrena godina'
Toggle: 'Prikaži pomoć za formatiranje'
MemberImportForm:
Help1: '<p>Uvezi korisnike u <em>CSV</em> formatu (zarezima razdvojene vrednosti). <small><a href="#" class="toggle-advanced">Prikaži napredno korišćenje</a></small></p>'
Help2: "<div class=\"advanced\"> <h4>Napredno korišćenje</h4> <ul> <li>Dozvoljene kolone: <em>%s</em></li> <li>Postojeće korisnici se prepoznaju po sopstvenom jedinstvenom svojstvu <em>Kôd</em> i ažuriraju novim vrednostima iz uvezene datoteke</li> <li>Grupe mogu biti dodeljene pomoću kolone <em>Grupe</em>. Grupe se identifikuju putem njihovog svojstva <em>Kôd</em>, a više grupa se razdvaja zarezom. Postojeće članstvo u grupama se ne briše.</li>\n</ul></div>"
ResultCreated: 'Kreirano {count} članova'
ResultDeleted: 'Izbrisano %d članova'
ResultNone: 'Bez promena'
ResultUpdated: 'Ažurirano {count} članova'
MemberPassword:
PLURALNAME: 'Lozinke članova'
SINGULARNAME: 'Lozinka člana'
MemberTableField:
APPLY_FILTER: 'Primeni filter'
ModelAdmin:
DELETE: Izbriši
DELETEDRECORDS: 'Izbrisano {count} zapisa'
EMPTYBEFOREIMPORT: 'Premesti podatke'
IMPORT: 'Uvezi iz CSV'
IMPORTEDRECORDS: 'Uvezeno {count} zapisa'
NOCSVFILE: 'Izaberite CSV datoteku za uvoz'
NOIMPORT: 'Nema ničega za uvoz'
RESET: Vrati u pređašnje stanje
Title: 'Modeli podataka'
UPDATEDRECORDS: 'Ažurirano {count} zapisa'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Kolone baze podataka'
IMPORTSPECLINK: 'Prikaži specifikaciju za %s'
IMPORTSPECRELATIONS: Relacije
IMPORTSPECTITLE: 'Specifikacija za %s'
ModelAdmin_Tools_ss:
FILTER: Filter
IMPORT: Uvezi
ModelSidebar_ss:
IMPORT_TAB_HEADER: Uvezi
SEARCHLISTINGS: Pretraga
MoneyField:
FIELDLABELAMOUNT: Iznos
FIELDLABELCURRENCY: Valuta
NullableField:
IsNullLabel: 'je Null'
NumericField:
VALIDATION: '''{value}'' nije broj. Samo brojevi mogu biti prihvaćeni za ovo polje'
Pagination:
Page: Stranica
View: Pregled
PasswordValidator:
LOWCHARSTRENGTH: 'Pojačajte lozinku dodavanjem nekih od sledećih znakova: %s'
PREVPASSWORD: 'Već ste koristili navedenu lozinku u prošlosti. Stoga, izaberite drugu lozinku'
TOOSHORT: 'Lozinka je prekratka. Lozinka mora sadržati bar %s znakova'
Permission:
AdminGroup: Administrator
CMS_ACCESS_CATEGORY: 'Pristup CMS-u'
FULLADMINRIGHTS: 'Puna administrativna prava'
FULLADMINRIGHTS_HELP: 'Nadjačava sve druge dodeljene dozvole.'
PLURALNAME: Dozvole
SINGULARNAME: Dozvola
PermissionCheckboxSetField:
AssignedTo: 'dodeljeno "{title}"'
FromGroup: 'nasleđeno od grupe "{title}"'
FromRole: 'nasleđeno od uloge "{title}"'
FromRoleOnGroup: 'nasleđeno iz uloge "%s" za grupu "%s"'
PermissionRole:
OnlyAdminCanApply: 'Može primenjivati samo administrator'
PLURALNAME: Uloge
SINGULARNAME: Uloga
Title: Naslov
PermissionRoleCode:
PermsError: 'Nije moguće dodeliti kôd "%s" sa privilegovanim dozvolama (zahteva Administratorski pristup)'
SINGULARNAME: 'Kôd uloge za dozvole'
Permissions:
PERMISSIONS_CATEGORY: 'Uloge i prava pristupa'
UserPermissionsIntro: 'Dodavanjem ovog korisnika u grupu biće prilagođena i njegova prava pristupa. Podrobnija objašnjenja o pravima pristupa za pojedinačne grupe možete pronaći u sekciji "Grupe".'
PhoneNumberField:
VALIDATION: 'Unesite ispravan broj telefona'
Security:
ALREADYLOGGEDIN: 'Nemate dozvolu za pristup ovoj strani. Ukoliko imate drugi nalog kojim možete da pristupite ovoj strani, prijavite se.'
BUTTONSEND: 'Pošalji mi link za resetovanje lozinke'
CHANGEPASSWORDBELOW: 'Ovde možete da promenite svoju lozinku.'
CHANGEPASSWORDHEADER: 'Promeni moju lozinku'
ENTERNEWPASSWORD: 'Unesite novu lozinku.'
ERRORPASSWORDPERMISSION: 'Morate da budete prijavljeni da biste promenili svoju lozinku!'
LOGGEDOUT: 'Odjavljeni ste. Ukoliko želite da se ponovo prijavite, unesite svoje podatke.'
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.'
SecurityAdmin:
ACCESS_HELP: 'Omogućava špregled, dodavanje i izmene korisnika, kao i dodeljivanje prava pristupa i uloga korisnicima.'
APPLY_ROLES: 'Dodaj uloge grupama'
APPLY_ROLES_HELP: 'Mogućnost izmena ulogâ grupâ. Zahteva dozvolu za pristup odeljku "Korisnici".'
EDITPERMISSIONS: 'Upravljaj pravima pristupa grupâ'
EDITPERMISSIONS_HELP: 'Mogućnost menjanja Prava pristupa i IP adresa grupâ. Zahteva dozvolu za pristup odeljku "Bezbednost".'
GROUPNAME: 'Ime grupe'
IMPORTGROUPS: 'Uvezi grupe'
IMPORTUSERS: 'Uvezi korisnike'
MEMBERS: Članovi
MENUTITLE: Bezbednost
MemberListCaution: 'Pažnja: Uklanjanje članova iz ove liste ukloniće ih iz svih grupa i iz baze podataka'
NEWGROUP: 'Nova grupa'
PERMISSIONS: Dozvole
ROLES: Uloge
ROLESDESCRIPTION: 'Uloge su predefinisani skupovi ovlašćenja i mogu biti dodeljene grupama.<br />Nasleđuju se od roditeljskih grupa ako je potrebno.'
TABROLES: Uloge
Users: Korisnici
SecurityAdmin_MemberImportForm:
BtnImport: 'Uvezi iz CSV'
FileFieldLabel: 'CSV datoteka <small>(Dozvoljene ekstenzije: *.csv)</small>'
SilverStripeNavigator:
Auto: Auto
ChangeViewMode: 'Promeni môd pregleda'
Desktop: Radna površina
DualWindowView: 'Dvostruki prozor'
Edit: Izmeni
EditView: 'Môd izmena'
Mobile: Mobilno
PreviewState: 'Stanje prethodnog pregleda'
PreviewView: 'Môd prethodnog pregleda'
Responsive: Prilagodljiv
SplitView: 'Razdeljeni môd'
Tablet: Tablet
ViewDeviceWidth: 'Izaberite širinu prethodnog pregleda'
Width: širina
SiteTree:
TABMAIN: Glavno
TableListField:
CSVEXPORT: 'Izvezi u CSV'
Print: Štampaj
TableListField_PageControls_ss:
OF: od
TimeField:
VALIDATEFORMAT: 'Unesite ispravan format vremena ({format})'
ToggleField:
LESS: manje
MORE: više
UploadField:
ATTACHFILE: 'Priključi datoteku'
ATTACHFILES: 'Priključi datoteke'
AttachFile: 'Priključi datoteku(e)'
CHOOSEANOTHERFILE: 'Izaberi drugu datoteku'
CHOOSEANOTHERINFO: 'Zameni ovu datoteku drugom sa servera'
DELETE: 'Izbriši iz datoteka'
DELETEINFO: 'Trajno izbriši ovu datoteku sa servera'
DOEDIT: Sačuvaj
DROPFILE: 'ispusti datoteku'
DROPFILES: 'ispusti datoteke'
Dimensions: Dimenzije
EDIT: Izmeni
EDITINFO: 'Izmeni ovu datoteku'
FIELDNOTSET: 'Informacije o datoteci nisu pronađene'
FROMCOMPUTER: 'Sa Vašeg računara'
FROMCOMPUTERINFO: 'Izaberi među datotekama'
FROMFILES: 'Iz datoteka'
HOTLINKINFO: 'Napomena: ova slika će biti ubačena pomoću hotlinka. Uverite se da imate ovlašćenje kreatora origilanog sajta da uradite to.'
MAXNUMBEROFFILES: 'Maksimalan broj datoteka ({count}) je premašen.'
MAXNUMBEROFFILESONE: 'Može postaviti samo jednu datoteku'
MAXNUMBEROFFILESSHORT: 'Može postaviti samo {count} datoteka'
OVERWRITEWARNING: 'Datoteka sa istim imenom već postoji'
REMOVE: Ukloni
REMOVEINFO: 'Uklonu ovu datoteku odavde, ali je ne briši sa servera'
STARTALL: 'Započni sve'
Saved: Sačuvano.
UPLOADSINTO: 'postalja u /{path}'
Versioned:
has_many_Versions: Verzije

View File

@ -60,6 +60,8 @@ sv:
ERRORNOTREC: 'Användarnamnet / lösenordet hittas inte'
Boolean:
ANY: Vilken som helst
NOANSWER: 'Nej'
YESANSWER: 'Ja'
CMSLoadingScreen_ss:
LOADING: Laddar...
REQUIREJS: 'CMS:et kräver att du har javascript aktiverat.'
@ -78,6 +80,22 @@ sv:
EMAIL: E-post
HELLO: Hej
PASSWORD: Lösenord
CheckboxField:
NOANSWER: 'Nej'
YESANSWER: 'Ja'
CheckboxFieldSetField:
SOURCE_VALIDATION: 'Vänligen välj att värde i listan. {value} är inget giltigt val'
CMSMemberLoginForm:
BUTTONFORGOTPASSWORD: 'Glömt lösenord?'
BUTTONLOGIN: 'Logga in igen'
BUTTONLOGOUT: 'Logga ut'
PASSWORDEXPIRED: '<p>Ditt lösenard har gått ut. <a target="_top" href="{link}">Vänligen ange ett nytt.</a></p>'
CMSSecurity:
INVALIDUSER: '<p>Ogiltig användare. <a target="_top" href="{link}">Vänligen ange dina inloggnings-uppgifter igen</a> för att fortsätta.</p>'
LoginMessage: '<p>Om du har osparade ändringar kan du fortsätta där du slutade genom att logga in igen nedan.</p>'
SUCCESSCONTENT: '<p>Inloggningen lyckades. <a target="_top" href="{link}">Klicka här</a> om du inte skickas vidare automatiskt.</p>'
TimedOutTitleAnonymous: 'Din session har upphört.'
TimedOutTitleMember: 'Hej {name}!<br />Din session har upphört.'
ConfirmedPasswordField:
ATLEAST: 'Lösenord måste vara minst {min} tecken långa.'
BETWEEN: 'Lösenord måste vara {min} till {max} tecken långa.'
@ -124,6 +142,7 @@ sv:
DropdownField:
CHOOSE: (Välj)
CHOOSESEARCH: '(Välj eller sök)'
SOURCE_VALIDATION: 'Vänligen välj att värde i listan. {value} är inget giltigt val'
EmailField:
VALIDATION: 'Var snäll och ange en epostadress'
Enum:
@ -171,7 +190,7 @@ sv:
TEXT2: 'Återställningslänk för lösenord'
TEXT3: för
Form:
CSRF_FAILED_MESSAGE: "Ett tekniskt fel uppstod. Var god klicka på bakåt-knappen,\n⇥⇥⇥⇥⇥ladda om webbläsaren och försök igen."
CSRF_FAILED_MESSAGE: 'Ett tekniskt fel uppstod. Var god klicka på bakåt-knappen, ladda om sidan i webbläsaren och försök igen'
FIELDISREQUIRED: '{name} är obligatoriskt'
SubmitBtnLabel: Kör
VALIDATIONCREDITNUMBER: 'Kontrollera att du angav kortnummret {number} rätt'
@ -238,7 +257,6 @@ sv:
many_many_Members: Medlemmar
GroupImportForm:
Help1: '<p>Importera en eller flera grupper i <em>CSV</em>- format (komma-separerade värden). <small><a href="#" class ="toggle-advanced">Visa avancerade val</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Avancerat </h4>\n<ul>\n<li>Tillåtna kolumner: <em>%s</em></li>\n<li>Existerade användare matchas av deras unika <em>kod</em>-attribut och uppdateras med alla nya värden från den importerade filen</li>\n<li>Grupper kan anges i <em>Grupp</em>-kolumnen. Grupper identiferas av deras <em>Code</em>-attribut. Anges flera grupper separeras dessa med kommatecken. Existerande användarrättigheter till grupperna tas inte bort.</li>\n</ul>\n</div>"
ResultCreated: 'Skapade {count} grupper'
ResultDeleted: 'Raderade %d grupper'
ResultUpdated: 'Uppdaterade %d grupper'
@ -247,6 +265,8 @@ sv:
HtmlEditorField:
ADDURL: 'Lägg till URL'
ADJUSTDETAILSDIMENSIONS: 'Detaljer &amp; dimensioner'
ANCHORSCANNOTACCESSPAGE: 'Du har inte tillåtelse att se innehållet på sidan'
ANCHORSPAGENOTFOUND: 'Målsidan hittades inte'
ANCHORVALUE: Ankare
BUTTONADDURL: 'Lägg till URL'
BUTTONINSERT: Infoga
@ -290,6 +310,7 @@ sv:
URL: URL
URLNOTANOEMBEDRESOURCE: 'URLen ''{url}'' gick inte att omvandla till ett media.'
UpdateMEDIA: 'Uppdatera media'
SUBJECT: 'Ämne'
Image:
PLURALNAME: Filer
SINGULARNAME: Fil
@ -307,7 +328,7 @@ sv:
PERMAGAIN: 'Du har blivit utloggad. Om du vill logga in igen anger du dina uppgifter nedan.'
PERMALREADY: 'Tyvärr så har du inte tillträde till den delen av CMSet. Om du vill logga in med en annan användare kan du göra det nedan'
PERMDEFAULT: 'Var god välj en inloggningsmetod och fyll i dina uppgifter för att logga in i CMSet.'
PLEASESAVE: 'Var god spara sidan. Den kan inte uppdateras för att den har inte sparats ännu.'
PLEASESAVE: 'Var god spara sidan. Den kan inte uppdateras eftersom den har inte sparats ännu.'
PreviewButton: Förhandsgranska
REORGANISATIONSUCCESSFUL: 'Omorganisationen av sidträdet luyckades.'
SAVEDUP: Sparad.
@ -318,6 +339,8 @@ sv:
LeftAndMain_Menu_ss:
Hello: Hej
LOGOUT: 'Logga ut'
ListboxField:
SOURCE_VALIDATION: 'Vänligen välj att värde i listan. {value} är inget giltigt val'
LoginAttempt:
Email: 'E-postadress'
IP: 'IP-adress'
@ -350,6 +373,7 @@ sv:
NEWPASSWORD: 'Nytt lösenord'
NoPassword: 'Det finns inget lösenord för den här medlemmen'
PASSWORD: Lösenord
PASSWORDEXPIRED: 'Ditt lösenord har gått ut. Vänligen ange ett nytt.'
PLURALNAME: Medlemmar
REMEMBERME: 'Kom ihåg mig nästa gång?'
SINGULARNAME: Medlem
@ -456,7 +480,6 @@ sv:
SINGULARNAME: Roll
Title: Rollnamn
PermissionRoleCode:
PLURALNAME: 'Koder för rollrättigheter'
PermsError: 'Koden "%s" kan inte ges privilegierad tillgång (adminrättigheter krävs)'
SINGULARNAME: 'Kodför rollrättigheter'
Permissions:
@ -557,3 +580,5 @@ sv:
UPLOADSINTO: 'sparas till /{path}'
Versioned:
has_many_Versions: Versioner
CheckboxSetField:
SOURCE_VALIDATION: 'Vänligen välj att värde i listan. {value} är inget giltigt val'

View File

@ -116,7 +116,11 @@ class DataDifferencer extends ViewableData {
$toTitle = '';
if($this->toRecord->hasMethod($relName)) {
$relObjTo = $this->toRecord->$relName();
$toTitle = $relObjTo->hasMethod('Title') || $relObjTo->hasField('Title') ? $relObjTo->Title : '';
if($relObjTo) {
$toTitle = ($relObjTo->hasMethod('Title') || $relObjTo->hasField('Title')) ? $relObjTo->Title : '';
} else {
$toTitle = '';
}
}
if(!$this->fromRecord) {
@ -134,7 +138,12 @@ class DataDifferencer extends ViewableData {
$fromTitle = '';
if($this->fromRecord->hasMethod($relName)) {
$relObjFrom = $this->fromRecord->$relName();
$fromTitle = $relObjFrom->hasMethod('Title') || $relObjFrom->hasField('Title') ? $relObjFrom->Title : '';
if($relObjFrom) {
$fromTitle = ($relObjFrom->hasMethod('Title') || $relObjFrom->hasField('Title')) ? $relObjFrom->Title : '';
} else {
$fromTitle = '';
}
}
if(isset($relObjFrom) && $relObjFrom instanceof Image) {
// TODO Use CMSThumbnail (see above)

View File

@ -180,6 +180,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
protected static $_cache_get_one;
protected static $_cache_get_class_ancestry;
protected static $_cache_composite_fields = array();
protected static $_cache_is_composite_field = array();
protected static $_cache_custom_database_fields = array();
protected static $_cache_field_labels = array();
@ -360,14 +361,25 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @return string Class name of composite field if it exists
*/
public static function is_composite_field($class, $name, $aggregated = true) {
if(!isset(DataObject::$_cache_composite_fields[$class])) self::cache_composite_fields($class);
$key = $class . '_' . $name . '_' . (string)$aggregated;
if(isset(DataObject::$_cache_composite_fields[$class][$name])) {
return DataObject::$_cache_composite_fields[$class][$name];
if(!isset(DataObject::$_cache_is_composite_field[$key])) {
$isComposite = null;
} else if($aggregated && $class != 'DataObject' && ($parentClass=get_parent_class($class)) != 'DataObject') {
return self::is_composite_field($parentClass, $name);
if(!isset(DataObject::$_cache_composite_fields[$class])) {
self::cache_composite_fields($class);
}
if(isset(DataObject::$_cache_composite_fields[$class][$name])) {
$isComposite = DataObject::$_cache_composite_fields[$class][$name];
} elseif($aggregated && $class != 'DataObject' && ($parentClass=get_parent_class($class)) != 'DataObject') {
$isComposite = self::is_composite_field($parentClass, $name);
}
DataObject::$_cache_is_composite_field[$key] = ($isComposite) ? $isComposite : false;
}
return DataObject::$_cache_is_composite_field[$key] ?: null;
}
/**
@ -3184,6 +3196,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
DataObject::$_cache_db = array();
DataObject::$_cache_get_one = array();
DataObject::$_cache_composite_fields = array();
DataObject::$_cache_is_composite_field = array();
DataObject::$_cache_custom_database_fields = array();
DataObject::$_cache_get_class_ancestry = array();
DataObject::$_cache_field_labels = array();

View File

@ -315,7 +315,7 @@ class Hierarchy extends DataExtension {
}
// Set jstree open state, or mark it as a leaf (closed) if there are no children
if(!$this->$numChildrenMethod()) {
if(!$this->owner->$numChildrenMethod()) {
$classes .= " jstree-leaf closed";
} elseif($this->isTreeOpened()) {
$classes .= " jstree-open";

View File

@ -10,7 +10,7 @@ interface SS_List extends ArrayAccess, Countable, IteratorAggregate {
/**
* Returns all the items in the list in an array.
*
* @return arary
* @return array
*/
public function toArray();

View File

@ -44,8 +44,9 @@ class SS_Transliterator extends Object {
'þ'=>'b', 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r',
'Ā'=>'A', 'ā'=>'a', 'Ē'=>'E', 'ē'=>'e', 'Ī'=>'I', 'ī'=>'i', 'Ō'=>'O', 'ō'=>'o', 'Ū'=>'U', 'ū'=>'u',
'œ'=>'oe', 'ß'=>'ss', 'ij'=>'ij', 'ą'=>'a','ę'=>'e', 'ė'=>'e', 'į'=>'i','ų'=>'u','ū'=>'u', 'Ą'=>'A',
'Ę'=>'E', 'Ė'=>'E', 'Į'=>'I','Ų'=>'U','Ū'=>'u',
'Ę'=>'E', 'Ė'=>'E', 'Į'=>'I','Ų'=>'U','Ū'=>'U',
"ľ"=>"l", "Ľ"=>"L", "ť"=>"t", "Ť"=>"T", "ů"=>"u", "Ů"=>"U",
'ł'=>'l', 'Ł'=>'L', 'ń'=>'n', 'Ń'=>'N', 'ś'=>'s', 'Ś'=>'S', 'ź'=>'z', 'Ź'=>'Z', 'ż'=>'z', 'Ż'=>'Z',
);
return strtr($source, $table);

View File

@ -105,15 +105,18 @@ class SQLSelect extends SQLConditionalExpression {
*
* <code>
* // pass fields to select as single parameter array
* $query->setSelect(array("Col1","Col2"))->setFrom("MyTable");
* $query->setSelect(array('"Col1"', '"Col2"'))->setFrom('"MyTable"');
*
* // pass fields to select as multiple parameters
* $query->setSelect("Col1", "Col2")->setFrom("MyTable");
* $query->setSelect('"Col1"', '"Col2"')->setFrom('"MyTable"');
*
* // Set a list of selected fields as aliases
* $query->setSelect(array('Name' => '"Col1"', 'Details' => '"Col2"')->setFrom('"MyTable"');
* </code>
*
* @param string|array $fields
* @param bool $clear Clear existing select fields?
* @return self Self reference
* @param string|array $fields Field names should be ANSI SQL quoted. Array keys should be unquoted.
* @param boolean $clear Clear existing select fields?
* @return $this Self reference
*/
public function setSelect($fields) {
$this->select = array();
@ -128,17 +131,10 @@ class SQLSelect extends SQLConditionalExpression {
/**
* Add to the list of columns to be selected by the query.
*
* <code>
* // pass fields to select as single parameter array
* $query->addSelect(array("Col1","Col2"))->setFrom("MyTable");
* @see setSelect for example usage
*
* // pass fields to select as multiple parameters
* $query->addSelect("Col1", "Col2")->setFrom("MyTable");
* </code>
*
* @param string|array $fields
* @param bool $clear Clear existing select fields?
* @return self Self reference
* @param string|array $fields Field names should be ANSI SQL quoted. Array keys should be unquoted.
* @return $this Self reference
*/
public function addSelect($fields) {
if (func_num_args() > 1) {
@ -146,9 +142,13 @@ class SQLSelect extends SQLConditionalExpression {
} else if(!is_array($fields)) {
$fields = array($fields);
}
foreach($fields as $idx => $field) {
$this->selectField($field, is_numeric($idx) ? null : $idx);
if(preg_match('/^(.*) +AS +"([^"]*)"/i', $field, $matches)) {
Deprecation::notice("3.0", "Use selectField() to specify column aliases");
$this->selectField($matches[1], $matches[2]);
} else {
$this->selectField($field, is_numeric($idx) ? null : $idx);
}
}
return $this;
@ -157,10 +157,10 @@ class SQLSelect extends SQLConditionalExpression {
/**
* Select an additional field.
*
* @param $field string The field to select (escaped SQL statement)
* @param $alias string The alias of that field (escaped SQL statement).
* @param string $field The field to select (ansi quoted SQL identifier or statement)
* @param string|null $alias The alias of that field (unquoted SQL identifier).
* Defaults to the unquoted column name of the $field parameter.
* @return self Self reference
* @return $this Self reference
*/
public function selectField($field, $alias = null) {
if(!$alias) {
@ -264,9 +264,9 @@ class SQLSelect extends SQLConditionalExpression {
* @example $sql->setOrderBy(array("Column" => "ASC", "ColumnTwo" => "DESC"));
*
* @param string|array $clauses Clauses to add (escaped SQL statement)
* @param string $dir Sort direction, ASC or DESC
* @param string $direction Sort direction, ASC or DESC
*
* @return self Self reference
* @return $this Self reference
*/
public function setOrderBy($clauses = null, $direction = null) {
$this->orderby = array();
@ -284,7 +284,7 @@ class SQLSelect extends SQLConditionalExpression {
*
* @param string|array $clauses Clauses to add (escaped SQL statements)
* @param string $direction Sort direction, ASC or DESC
* @return self Self reference
* @return $this Self reference
*/
public function addOrderBy($clauses = null, $direction = null) {
if(empty($clauses)) return $this;
@ -350,9 +350,9 @@ class SQLSelect extends SQLConditionalExpression {
/**
* Extract the direction part of a single-column order by clause.
*
* @param String
* @param String
* @return Array A two element array: array($column, $direction)
* @param string $value
* @param string $defaultDirection
* @return array A two element array: array($column, $direction)
*/
private function getDirectionFromString($value, $defaultDirection = null) {
if(preg_match('/^(.*)(asc|desc)$/i', $value, $matches)) {
@ -550,7 +550,7 @@ class SQLSelect extends SQLConditionalExpression {
$clone->setSelect(array("count($column)"));
}
$clone->setGroupBy(array());;
$clone->setGroupBy(array());
return $clone->execute()->value();
}

View File

@ -9,15 +9,15 @@
* @package framework
* @subpackage misc
*/
class ShortcodeParser {
class ShortcodeParser extends Object {
public function img_shortcode($attrs) {
return "<img src='".$attrs['src']."'>";
}
private static $instances = array();
protected static $instances = array();
private static $active_instance = 'default';
protected static $active_instance = 'default';
// --------------------------------------------------------------------------------------------------------------
@ -33,7 +33,7 @@ class ShortcodeParser {
*/
public static function get($identifier = 'default') {
if(!array_key_exists($identifier, self::$instances)) {
self::$instances[$identifier] = new ShortcodeParser();
self::$instances[$identifier] = static::create();
}
return self::$instances[$identifier];
@ -45,7 +45,7 @@ class ShortcodeParser {
* @return ShortcodeParser
*/
public static function get_active() {
return self::get(self::$active_instance);
return static::get(self::$active_instance);
}
/**
@ -140,15 +140,15 @@ class ShortcodeParser {
}
}
private static $marker_class = '--ss-shortcode-marker';
protected static $marker_class = '--ss-shortcode-marker';
private static $block_level_elements = array(
protected static $block_level_elements = array(
'address', 'article', 'aside', 'audio', 'blockquote', 'canvas', 'dd', 'div', 'dl', 'fieldset', 'figcaption',
'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'ol', 'output', 'p',
'pre', 'section', 'table', 'ul'
);
private static $attrrx = '
protected static $attrrx = '
([^\s\/\'"=,]+) # Name
\s* = \s*
(?:
@ -158,11 +158,11 @@ class ShortcodeParser {
)
';
private static function attrrx() {
protected static function attrrx() {
return '/'.self::$attrrx.'/xS';
}
private static $tagrx = '
protected static $tagrx = '
# HTML Tag
<(?<element>(?:"[^"]*"[\'"]*|\'[^\']*\'[\'"]*|[^\'">])+)>
@ -182,7 +182,7 @@ class ShortcodeParser {
(?<cesc2>\]?)
';
private static function tagrx() {
protected static function tagrx() {
return '/'.sprintf(self::$tagrx, self::$attrrx).'/xS';
}
@ -207,7 +207,7 @@ class ShortcodeParser {
protected function extractTags($content) {
$tags = array();
if(preg_match_all(self::tagrx(), $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
if(preg_match_all(static::tagrx(), $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
foreach($matches as $match) {
// Ignore any elements
if (empty($match['open'][0]) && empty($match['close'][0])) continue;
@ -216,7 +216,7 @@ class ShortcodeParser {
$attrs = array();
if (!empty($match['attrs'][0])) {
preg_match_all(self::attrrx(), $match['attrs'][0], $attrmatches, PREG_SET_ORDER);
preg_match_all(static::attrrx(), $match['attrs'][0], $attrmatches, PREG_SET_ORDER);
foreach ($attrmatches as $attr) {
list($whole, $name, $value) = array_values(array_filter($attr));

View File

@ -332,10 +332,14 @@ $gf_grid_x: 16px;
}
}
&:hover {
background: #FFFAD6 !important;
background: #FFFAD6;
}
&:first-child {
background: transparent;
&:hover {
background: #FFFAD6;
}
}
&.ss-gridfield-even {
background: $gf_colour_zebra;
@ -343,9 +347,16 @@ $gf_grid_x: 16px;
&.ss-gridfield-last {
border-bottom: none;
}
&:hover {
background: #FFFAD6;
}
}
&.even {
background: $gf_colour_zebra;
&:hover {
background: #FFFAD6;
}
}
th {

View File

@ -107,13 +107,13 @@ class ChangePasswordForm extends Form {
$member->LockedOutUntil = null;
$member->FailedLoginCount = null;
$member->write();
if (isset($_REQUEST['BackURL'])
&& $_REQUEST['BackURL']
// absolute redirection URLs may cause spoofing
if (!empty($_REQUEST['BackURL'])
// absolute redirection URLs may cause spoofing
&& Director::is_site_url($_REQUEST['BackURL'])
) {
return $this->controller->redirect($_REQUEST['BackURL']);
$url = Director::absoluteURL($_REQUEST['BackURL']);
return $this->controller->redirect($url);
}
else {
// Redirect to default location - the login form saying "You are logged in as..."

View File

@ -194,7 +194,7 @@ JS;
* )
*
* @param array $data
* @return void
* @return SS_HTTPResponse
*/
protected function logInUserAndRedirect($data) {
Session::clear('SessionForms.MemberLoginForm.Email');
@ -213,18 +213,21 @@ JS;
}
// Absolute redirection URLs may cause spoofing
if(isset($_REQUEST['BackURL']) && $_REQUEST['BackURL'] && Director::is_site_url($_REQUEST['BackURL']) ) {
return $this->controller->redirect($_REQUEST['BackURL']);
}
// Spoofing attack, redirect to homepage instead of spoofing url
if(isset($_REQUEST['BackURL']) && $_REQUEST['BackURL'] && !Director::is_site_url($_REQUEST['BackURL'])) {
return $this->controller->redirect(Director::absoluteBaseURL());
if(!empty($_REQUEST['BackURL'])) {
$url = $_REQUEST['BackURL'];
if(Director::is_site_url($url) ) {
$url = Director::absoluteURL($url);
} else {
// Spoofing attack, redirect to homepage instead of spoofing url
$url = Director::absoluteBaseURL();
}
return $this->controller->redirect($url);
}
// If a default login dest has been set, redirect to that.
if (Security::config()->default_login_dest) {
return $this->controller->redirect(Director::absoluteBaseURL() . Security::config()->default_login_dest);
if ($url = Security::config()->default_login_dest) {
$url = Controller::join_links(Director::absoluteBaseURL(), $url);
return $this->controller->redirect($url);
}
// Redirect the user to the page where they came from

View File

@ -329,14 +329,15 @@ class ControllerTest extends FunctionalTest {
public function testRedirectBackByReferer() {
$internalRelativeUrl = '/some-url';
$internalAbsoluteUrl = Controller::join_links(Director::absoluteBaseURL(), '/some-url');
$response = $this->get('ControllerTest_Controller/redirectbacktest', null,
array('Referer' => $internalRelativeUrl));
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals($internalRelativeUrl, $response->getHeader('Location'),
$this->assertEquals($internalAbsoluteUrl, $response->getHeader('Location'),
"Redirects on internal relative URLs"
);
$internalAbsoluteUrl = Director::absoluteBaseURL() . '/some-url';
$response = $this->get('ControllerTest_Controller/redirectbacktest', null,
array('Referer' => $internalAbsoluteUrl));
$this->assertEquals(302, $response->getStatusCode());
@ -354,9 +355,11 @@ class ControllerTest extends FunctionalTest {
public function testRedirectBackByBackUrl() {
$internalRelativeUrl = '/some-url';
$internalAbsoluteUrl = Controller::join_links(Director::absoluteBaseURL(), '/some-url');
$response = $this->get('ControllerTest_Controller/redirectbacktest?BackURL=' . urlencode($internalRelativeUrl));
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals($internalRelativeUrl, $response->getHeader('Location'),
$this->assertEquals($internalAbsoluteUrl, $response->getHeader('Location'),
"Redirects on internal relative URLs"
);

View File

@ -25,10 +25,12 @@ class DirectorTest extends SapphireTest {
if(!self::$originalRequestURI) {
self::$originalRequestURI = $_SERVER['REQUEST_URI'];
}
$_SERVER['REQUEST_URI'] = 'http://www.mysite.com';
$this->originalGet = $_GET;
$this->originalSession = $_SESSION;
$_SESSION = array();
Config::inst()->update('Director', 'rules', array(
'DirectorTestRule/$Action/$ID/$OtherID' => 'DirectorTestRequest_Controller',
'en-nz/$Action/$ID/$OtherID' => array(
@ -136,27 +138,47 @@ class DirectorTest extends SapphireTest {
}
public function testAlternativeBaseURL() {
// Get original protocol and hostname
$rootURL = Director::protocolAndHost();
// relative base URLs - you should end them in a /
Config::inst()->update('Director', 'alternate_base_url', '/relativebase/');
$_SERVER['REQUEST_URI'] = "$rootURL/relativebase/sub-page/";
$this->assertEquals('/relativebase/', Director::baseURL());
$this->assertEquals(Director::protocolAndHost() . '/relativebase/', Director::absoluteBaseURL());
$this->assertEquals(Director::protocolAndHost() . '/relativebase/subfolder/test',
Director::absoluteURL('subfolder/test'));
$this->assertEquals($rootURL . '/relativebase/', Director::absoluteBaseURL());
$this->assertEquals(
$rootURL . '/relativebase/subfolder/test',
Director::absoluteURL('subfolder/test')
);
// absolute base URLs - you should end them in a /
Config::inst()->update('Director', 'alternate_base_url', 'http://www.example.org/');
$_SERVER['REQUEST_URI'] = "http://www.example.org/";
$_SERVER['REQUEST_URI'] = "http://www.example.org/sub-page/";
$this->assertEquals('http://www.example.org/', Director::baseURL());
$this->assertEquals('http://www.example.org/', Director::absoluteBaseURL());
$this->assertEquals('http://www.example.org/', Director::absoluteURL(''));
$this->assertEquals('http://www.example.org/subfolder/test', Director::absoluteURL('subfolder/test'));
$this->assertEquals('http://www.example.org/sub-page/', Director::absoluteURL(''));
$this->assertEquals('http://www.example.org/', Director::absoluteURL('', true));
/*
* See Legacy behaviour in testAbsoluteURL - sub-pages with '/' in the string are not correctly evaluated
$this->assertEquals(
'http://www.example.org/sub-page/subfolder/test',
Director::absoluteURL('subfolder/test')
);*/
$this->assertEquals(
'http://www.example.org/subfolder/test',
Director::absoluteURL('subfolder/test', true)
);
// Setting it to false restores functionality
Config::inst()->update('Director', 'alternate_base_url', false);
$_SERVER['REQUEST_URI'] = $rootURL;
$this->assertEquals(BASE_URL.'/', Director::baseURL());
$this->assertEquals(Director::protocolAndHost().BASE_URL.'/', Director::absoluteBaseURL(BASE_URL));
$this->assertEquals(Director::protocolAndHost().BASE_URL . '/subfolder/test',
Director::absoluteURL('subfolder/test'));
$this->assertEquals($rootURL.BASE_URL.'/', Director::absoluteBaseURL(BASE_URL));
$this->assertEquals(
$rootURL.BASE_URL . '/subfolder/test',
Director::absoluteURL('subfolder/test')
);
}
/**
@ -418,6 +440,9 @@ class DirectorTest extends SapphireTest {
}
public function testIsHttps() {
if(!TRUSTED_PROXY) {
$this->markTestSkipped('Test cannot be run without trusted proxy');
}
// nothing available
$headers = array(
'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'

View File

@ -32,18 +32,26 @@ class ParameterConfirmationTokenTest extends SapphireTest {
return array($answer, $slash);
}
protected $oldHost = null;
public function setUp() {
parent::setUp();
$this->oldHost = $_SERVER['HTTP_HOST'];
$_GET['parameterconfirmationtokentest_notoken'] = 'value';
$_GET['parameterconfirmationtokentest_empty'] = '';
$_GET['parameterconfirmationtokentest_withtoken'] = '1';
$_GET['parameterconfirmationtokentest_withtokentoken'] = 'dummy';
$_GET['parameterconfirmationtokentest_nulltoken'] = '1';
$_GET['parameterconfirmationtokentest_nulltokentoken'] = null;
$_GET['parameterconfirmationtokentest_emptytoken'] = '1';
$_GET['parameterconfirmationtokentest_emptytokentoken'] = '';
}
public function tearDown() {
foreach($_GET as $param) {
if(stripos($param, 'parameterconfirmationtokentest_') === 0) unset($_GET[$param]);
}
$_SERVER['HTTP_HOST'] = $this->oldHost;
parent::tearDown();
}
@ -52,25 +60,33 @@ class ParameterConfirmationTokenTest extends SapphireTest {
$emptyParameter = new ParameterConfirmationTokenTest_Token('parameterconfirmationtokentest_empty');
$withToken = new ParameterConfirmationTokenTest_ValidToken('parameterconfirmationtokentest_withtoken');
$withoutParameter = new ParameterConfirmationTokenTest_Token('parameterconfirmationtokentest_noparam');
$nullToken = new ParameterConfirmationTokenTest_Token('parameterconfirmationtokentest_nulltoken');
$emptyToken = new ParameterConfirmationTokenTest_Token('parameterconfirmationtokentest_emptytoken');
// Check parameter
$this->assertTrue($withoutToken->parameterProvided());
$this->assertTrue($emptyParameter->parameterProvided()); // even if empty, it's still provided
$this->assertTrue($withToken->parameterProvided());
$this->assertFalse($withoutParameter->parameterProvided());
$this->assertTrue($nullToken->parameterProvided());
$this->assertTrue($emptyToken->parameterProvided());
// Check token
$this->assertFalse($withoutToken->tokenProvided());
$this->assertFalse($emptyParameter->tokenProvided());
$this->assertTrue($withToken->tokenProvided());
$this->assertTrue($withToken->tokenProvided()); // Actually forced to true for this test
$this->assertFalse($withoutParameter->tokenProvided());
$this->assertFalse($nullToken->tokenProvided());
$this->assertFalse($emptyToken->tokenProvided());
// Check if reload is required
$this->assertTrue($withoutToken->reloadRequired());
$this->assertTrue($emptyParameter->reloadRequired());
$this->assertFalse($withToken->reloadRequired());
$this->assertFalse($withoutParameter->reloadRequired());
$this->assertTrue($nullToken->reloadRequired());
$this->assertTrue($emptyToken->reloadRequired());
// Check suppression
$this->assertTrue(isset($_GET['parameterconfirmationtokentest_notoken']));
$withoutToken->suppress();
@ -121,4 +137,4 @@ class ParameterConfirmationTokenTest extends SapphireTest {
}
}
}
}

View File

@ -9,7 +9,9 @@ class FixtureBlueprintTest extends SapphireTest {
protected $extraDataObjects = array(
'FixtureFactoryTest_DataObject',
'FixtureFactoryTest_DataObjectRelation'
'FixtureFactoryTest_DataObjectRelation',
'FixtureBlueprintTest_SiteTree',
'FixtureBlueprintTest_Page'
);
public function testCreateWithRelationshipExtraFields() {
@ -180,6 +182,40 @@ class FixtureBlueprintTest extends SapphireTest {
$this->assertEquals(99, $obj->ID);
}
public function testCreateWithLastEdited() {
$extpectedDate = '2010-12-14 16:18:20';
$blueprint = new FixtureBlueprint('FixtureFactoryTest_DataObject');
$obj = $blueprint->createObject('lastedited', array('LastEdited' => $extpectedDate));
$this->assertNotNull($obj);
$this->assertEquals($extpectedDate, $obj->LastEdited);
$obj = FixtureFactoryTest_DataObject::get()->byID($obj->ID);
$this->assertEquals($extpectedDate, $obj->LastEdited);
}
public function testCreateWithClassAncestry() {
$data = array(
'Title' => 'My Title',
'Created' => '2010-12-14 16:18:20',
'LastEdited' => '2010-12-14 16:18:20',
'PublishDate' => '2015-12-09 06:03:00'
);
$blueprint = new FixtureBlueprint('FixtureBlueprintTest_Article');
$obj = $blueprint->createObject('home', $data);
$this->assertNotNull($obj);
$this->assertEquals($data['Title'], $obj->Title);
$this->assertEquals($data['Created'], $obj->Created);
$this->assertEquals($data['LastEdited'], $obj->LastEdited);
$this->assertEquals($data['PublishDate'], $obj->PublishDate);
$obj = FixtureBlueprintTest_Article::get()->byID($obj->ID);
$this->assertNotNull($obj);
$this->assertEquals($data['Title'], $obj->Title);
$this->assertEquals($data['Created'], $obj->Created);
$this->assertEquals($data['LastEdited'], $obj->LastEdited);
$this->assertEquals($data['PublishDate'], $obj->PublishDate);
}
public function testCallbackOnBeforeCreate() {
$blueprint = new FixtureBlueprint('FixtureFactoryTest_DataObject');
$this->_called = 0;
@ -232,3 +268,32 @@ class FixtureBlueprintTest extends SapphireTest {
}
}
/**
* @package framework
* @subpackage tests
*/
class FixtureBlueprintTest_SiteTree extends DataObject implements TestOnly {
private static $db = array(
"Title" => "Varchar"
);
}
/**
* @package framework
* @subpackage tests
*/
class FixtureBlueprintTest_Page extends FixtureBlueprintTest_SiteTree {
private static $db = array(
'PublishDate' => 'SS_DateTime'
);
}
/**
* @package framework
* @subpackage tests
*/
class FixtureBlueprintTest_Article extends FixtureBlueprintTest_Page {
}

View File

@ -393,9 +393,13 @@ class FileTest extends SapphireTest {
$this->objFromFixture('Member', 'frontend')->logIn();
$this->assertFalse($file->canEdit(), "Permissionless users can't edit files");
// Test cms non-asset user
// Test global CMS section users
$this->objFromFixture('Member', 'cms')->logIn();
$this->assertFalse($file->canEdit(), "Basic CMS users can't edit files");
$this->assertTrue($file->canEdit(), "Users with all CMS section access can edit files");
// Test cms access users without file access
$this->objFromFixture('Member', 'security')->logIn();
$this->assertFalse($file->canEdit(), "Security CMS users can't edit files");
// Test asset-admin user
$this->objFromFixture('Member', 'assetadmin')->logIn();

View File

@ -35,6 +35,8 @@ Permission:
Code: CMS_ACCESS_LeftAndMain
assetadmin:
Code: CMS_ACCESS_AssetAdmin
securityadmin:
Code: CMS_ACCESS_SecurityAdmin
Group:
admins:
Title: Administrators
@ -42,9 +44,12 @@ Group:
cmsusers:
Title: 'CMS Users'
Permissions: =>Permission.cmsmain
securityusers:
Title: 'Security Users'
Permissions: =>Permission.securityadmin
assetusers:
Title: 'Asset Users'
Permissions: =>Permission.cmsmain, =>Permission.assetadmin
Permissions: =>Permission.assetadmin
Member:
frontend:
Email: frontend@example.com
@ -57,3 +62,6 @@ Member:
assetadmin:
Email: assetadmin@silverstripe.com
Groups: =>Group.assetusers
security:
Email: security@silverstripe.com
Groups: =>Group.securityusers

View File

@ -785,7 +785,37 @@ class FieldListTest extends SapphireTest {
$this->assertEquals(2, $tabB2->fieldPosition('B_insertafter'));
$this->assertEquals(3, $tabB2->fieldPosition('B_post'));
}
/**
* FieldList::changeFieldOrder() should place specified fields in given
* order then add any unspecified remainders at the end. Can be given an
* array or list of arguments.
*/
public function testChangeFieldOrder() {
$fieldNames = array('A','B','C','D','E');
$setArray = new FieldList();
$setArgs = new FieldList();
foreach ($fieldNames as $fN) {
$setArray->push(new TextField($fN));
$setArgs->push(new TextField($fN));
}
$setArray->changeFieldOrder(array('D','B','E'));
$this->assertEquals(0, $setArray->fieldPosition('D'));
$this->assertEquals(1, $setArray->fieldPosition('B'));
$this->assertEquals(2, $setArray->fieldPosition('E'));
$this->assertEquals(3, $setArray->fieldPosition('A'));
$this->assertEquals(4, $setArray->fieldPosition('C'));
$setArgs->changeFieldOrder('D','B','E');
$this->assertEquals(0, $setArgs->fieldPosition('D'));
$this->assertEquals(1, $setArgs->fieldPosition('B'));
$this->assertEquals(2, $setArgs->fieldPosition('E'));
$this->assertEquals(3, $setArgs->fieldPosition('A'));
$this->assertEquals(4, $setArgs->fieldPosition('C'));
unset($setArray, $setArgs);
}
public function testFieldPosition() {
$set = new FieldList(
new TextField('A'),

View File

@ -14,13 +14,13 @@ class MoneyFieldTest extends SapphireTest {
$o = new MoneyFieldTest_Object();
$m = new Money();
$m->setAmount(1.23);
$m->setAmount(123456.78);
$m->setCurrency('EUR');
$f = new MoneyField('MyMoney', 'MyMoney', $m);
$f->saveInto($o);
$this->assertEquals($o->MyMoney->getAmount(), 1.23);
$this->assertEquals($o->MyMoney->getCurrency(), 'EUR');
$this->assertEquals(123456.78, $o->MyMoney->getAmount());
$this->assertEquals('EUR', $o->MyMoney->getCurrency());
}
public function testSetValueAsMoney() {
@ -29,13 +29,13 @@ class MoneyFieldTest extends SapphireTest {
$f = new MoneyField('MyMoney', 'MyMoney');
$m = new Money();
$m->setAmount(1.23);
$m->setAmount(123456.78);
$m->setCurrency('EUR');
$f->setValue($m);
$f->saveInto($o);
$this->assertEquals($o->MyMoney->getAmount(), 1.23);
$this->assertEquals($o->MyMoney->getCurrency(), 'EUR');
$this->assertEquals(123456.78, $o->MyMoney->getAmount());
$this->assertEquals('EUR', $o->MyMoney->getCurrency());
}
public function testSetValueAsArray() {
@ -43,11 +43,11 @@ class MoneyFieldTest extends SapphireTest {
$f = new MoneyField('MyMoney', 'MyMoney');
$f->setValue(array('Currency'=>'EUR','Amount'=>1.23));
$f->setValue(array('Currency'=>'EUR','Amount'=>123456.78));
$f->saveInto($o);
$this->assertEquals($o->MyMoney->getAmount(), 1.23);
$this->assertEquals($o->MyMoney->getCurrency(), 'EUR');
$this->assertEquals(123456.78, $o->MyMoney->getAmount());
$this->assertEquals('EUR', $o->MyMoney->getCurrency());
}
/**
@ -59,12 +59,13 @@ class MoneyFieldTest extends SapphireTest {
$o = new MoneyFieldTest_CustomSetter_Object();
$f = new MoneyField('CustomMoney', 'Test Money Field');
$f->setValue(array('Currency'=>'EUR','Amount'=>1.23));
$f->setValue(array('Currency'=>'EUR','Amount'=>123456.78));
$f->saveInto($o);
$this->assertEquals($o->MyMoney->getAmount(), (2 * 1.23) );
$this->assertEquals($o->MyMoney->getCurrency(), 'EUR');
$this->assertEquals((2 * 123456.78), $o->MyMoney->getAmount());
$this->assertEquals('EUR', $o->MyMoney->getCurrency());
}
}
class MoneyFieldTest_Object extends DataObject implements TestOnly {

View File

@ -14,6 +14,21 @@ class SQLQueryTest extends SapphireTest {
'SQLQueryTestChild'
);
public function testCount() {
//basic counting
$qry = SQLQueryTest_DO::get()->dataQuery()->getFinalisedQuery();
$qry->setGroupBy('Common');
$ids = $this->allFixtureIDs('SQLQueryTest_DO');
$this->assertEquals(count($ids), $qry->count('"SQLQueryTest_DO"."ID"'));
//test with `having`
if (DB::get_conn() instanceof MySQLDatabase) {
$qry->setHaving('"Date" > 2012-02-01');
$this->assertEquals(1, $qry->count('"SQLQueryTest_DO"."ID"'));
}
}
public function testEmptyQueryReturnsNothing() {
$query = new SQLQuery();
$this->assertSQLEquals('', $query->sql($parameters));
@ -578,6 +593,35 @@ class SQLQueryTest extends SapphireTest {
}
}
public function testSelect() {
$query = new SQLQuery('"Title"', '"MyTable"');
$query->addSelect('"TestField"');
$this->assertEquals(
'SELECT "Title", "TestField" FROM "MyTable"',
$query->sql()
);
// Test replacement of select
$query->setSelect(array(
'Field' => '"Field"',
'AnotherAlias' => '"AnotherField"'
));
$this->assertEquals(
'SELECT "Field", "AnotherField" AS "AnotherAlias" FROM "MyTable"',
$query->sql()
);
// Check that ' as ' selects don't get mistaken as aliases
$query->addSelect(array(
'Relevance' => "MATCH (Title, MenuTitle) AGAINST ('Two as One')"
));
$this->assertEquals(
'SELECT "Field", "AnotherField" AS "AnotherAlias", MATCH (Title, MenuTitle) AGAINST (' .
'\'Two as One\') AS "Relevance" FROM "MyTable"',
$query->sql()
);
}
/**
* Test passing in a LIMIT with OFFSET clause string.
*/

View File

@ -309,13 +309,19 @@ class SecurityTest extends FunctionalTest {
/* UNEXPIRED PASSWORD GO THROUGH WITHOUT A HITCH */
$goodResponse = $this->doTestLoginForm('sam@silverstripe.com' , '1nitialPassword');
$this->assertEquals(302, $goodResponse->getStatusCode());
$this->assertEquals(Director::baseURL() . 'test/link', $goodResponse->getHeader('Location'));
$this->assertEquals(
Controller::join_links(Director::absoluteBaseURL(), 'test/link'),
$goodResponse->getHeader('Location')
);
$this->assertEquals($this->idFromFixture('Member', 'test'), $this->session()->inst_get('loggedInAs'));
/* EXPIRED PASSWORDS ARE SENT TO THE CHANGE PASSWORD FORM */
$expiredResponse = $this->doTestLoginForm('expired@silverstripe.com' , '1nitialPassword');
$this->assertEquals(302, $expiredResponse->getStatusCode());
$this->assertEquals(Director::baseURL() . 'Security/changepassword', $expiredResponse->getHeader('Location'));
$this->assertEquals(
Controller::join_links(Director::baseURL(), 'Security/changepassword'),
$expiredResponse->getHeader('Location')
);
$this->assertEquals($this->idFromFixture('Member', 'expiredpassword'),
$this->session()->inst_get('loggedInAs'));
@ -323,7 +329,10 @@ class SecurityTest extends FunctionalTest {
$this->mainSession->followRedirection();
$changedResponse = $this->doTestChangepasswordForm('1nitialPassword', 'changedPassword');
$this->assertEquals(302, $changedResponse->getStatusCode());
$this->assertEquals(Director::baseURL() . 'test/link', $changedResponse->getHeader('Location'));
$this->assertEquals(
Controller::join_links(Director::absoluteBaseURL(), 'test/link'),
$changedResponse->getHeader('Location')
);
}
public function testChangePasswordForLoggedInUsers() {
@ -333,13 +342,19 @@ class SecurityTest extends FunctionalTest {
$this->get('Security/changepassword?BackURL=test/back');
$changedResponse = $this->doTestChangepasswordForm('1nitialPassword', 'changedPassword');
$this->assertEquals(302, $changedResponse->getStatusCode());
$this->assertEquals(Director::baseURL() . 'test/back', $changedResponse->getHeader('Location'));
$this->assertEquals(
Controller::join_links(Director::absoluteBaseURL(), 'test/back'),
$changedResponse->getHeader('Location')
);
$this->assertEquals($this->idFromFixture('Member', 'test'), $this->session()->inst_get('loggedInAs'));
// Check if we can login with the new password
$goodResponse = $this->doTestLoginForm('sam@silverstripe.com' , 'changedPassword');
$this->assertEquals(302, $goodResponse->getStatusCode());
$this->assertEquals(Director::baseURL() . 'test/link', $goodResponse->getHeader('Location'));
$this->assertEquals(
Controller::join_links(Director::absoluteBaseURL(), 'test/link'),
$goodResponse->getHeader('Location')
);
$this->assertEquals($this->idFromFixture('Member', 'test'), $this->session()->inst_get('loggedInAs'));
}

View File

@ -410,7 +410,7 @@ class SimpleUrl {
*/
function asString() {
$path = $this->_path;
$scheme = $identity = $host = $encoded = $fragment = '';
$scheme = $identity = $host = $port = $encoded = $fragment = '';
if ($this->_username && $this->_password) {
$identity = $this->_username . ':' . $this->_password . '@';
}
@ -419,13 +419,16 @@ class SimpleUrl {
$scheme .= "://";
$host = $this->getHost();
}
if ($this->getPort() && $this->getPort() != 80 ) {
$port = ':'.$this->getPort();
}
if (substr($this->_path, 0, 1) == '/') {
$path = $this->normalisePath($this->_path);
}
$encoded = $this->getEncodedRequest();
$fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
$coords = $this->getX() === false ? '' : '?' . $this->getX() . ',' . $this->getY();
return "$scheme$identity$host$path$encoded$fragment$coords";
return "$scheme$identity$host$port$path$encoded$fragment$coords";
}
/**

View File

@ -1,8 +1,7 @@
<?php
/**
* Requirements tracker, for javascript and css.
* @todo Document the requirements tracker, and discuss it with the others.
* Requirements tracker for JavaScript and CSS.
*
* @package framework
* @subpackage view
@ -10,7 +9,7 @@
class Requirements implements Flushable {
/**
* Triggered early in the request when someone requests a flush.
* Triggered early in the request when a flush is requested
*/
public static function flush() {
self::delete_all_combined_files();
@ -18,7 +17,8 @@ class Requirements implements Flushable {
/**
* Enable combining of css/javascript files.
* @param boolean $enable
*
* @param bool $enable
*/
public static function set_combined_files_enabled($enable) {
self::backend()->set_combined_files_enabled($enable);
@ -26,14 +26,16 @@ class Requirements implements Flushable {
/**
* Checks whether combining of css/javascript files is enabled.
* @return boolean
*
* @return bool
*/
public static function get_combined_files_enabled() {
return self::backend()->get_combined_files_enabled();
}
/**
* Set the relative folder e.g. "assets" for where to store combined files
* Set the relative folder e.g. 'assets' for where to store combined files
*
* @param string $folder Path to folder
*/
public static function set_combined_files_folder($folder) {
@ -41,8 +43,10 @@ class Requirements implements Flushable {
}
/**
* Set whether we want to suffix requirements with the time /
* location on to the requirements
* Set whether to add caching query params to the requests for file-based requirements.
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
* while automatically busting this cache every time the file is changed.
*
* @param bool
*/
@ -51,7 +55,7 @@ class Requirements implements Flushable {
}
/**
* Return whether we want to suffix requirements
* Check whether we want to suffix requirements
*
* @return bool
*/
@ -60,9 +64,10 @@ class Requirements implements Flushable {
}
/**
* Instance of requirements for storage
* Instance of the requirements for storage. You can create your own backend to change the
* default JS and CSS inclusion behaviour.
*
* @var Requirements
* @var Requirements_Backend
*/
private static $backend = null;
@ -76,74 +81,78 @@ class Requirements implements Flushable {
/**
* Setter method for changing the Requirements backend
*
* @param Requirements $backend
* @param Requirements_Backend $backend
*/
public static function set_backend(Requirements_Backend $backend) {
self::$backend = $backend;
}
/**
* Register the given javascript file as required.
*
* See {@link Requirements_Backend::javascript()} for more info
*
* Register the given JavaScript file as required.
*
* @param string $file Relative to docroot
*/
public static function javascript($file) {
self::backend()->javascript($file);
}
/**
* Add the javascript code to the header of the page
* Register the given JavaScript code into the list of requirements
*
* See {@link Requirements_Backend::customScript()} for more info
* @param script The script content
* @param uniquenessID Use this to ensure that pieces of code only get added once.
* @param string $script The script content as a string (without enclosing <script> tag)
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public static function customScript($script, $uniquenessID = null) {
self::backend()->customScript($script, $uniquenessID);
}
/**
* Include custom CSS styling to the header of the page.
* Return all registered custom scripts
*
* See {@link Requirements_Backend::customCSS()}
* @return array
*/
public static function get_custom_scripts() {
return self::backend()->get_custom_scripts();
}
/**
* Register the given CSS styles into the list of requirements
*
* @param string $script CSS selectors as a string (without <style> tag enclosing selectors).
* @param int $uniquenessID Group CSS by a unique ID as to avoid duplicate custom CSS in header
* @param string $script CSS selectors as a string (without enclosing <style> tag)
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public static function customCSS($script, $uniquenessID = null) {
self::backend()->customCSS($script, $uniquenessID);
}
/**
* Add the following custom code to the <head> section of the page.
* See {@link Requirements_Backend::insertHeadTags()}
* Add the following custom HTML code to the <head> section of the page
*
* @param string $html
* @param string $uniquenessID
* @param string $html Custom HTML code
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public static function insertHeadTags($html, $uniquenessID = null) {
self::backend()->insertHeadTags($html, $uniquenessID);
}
/**
* Load the given javascript template with the page.
* See {@link Requirements_Backend::javascriptTemplate()}
* Include the content of the given JavaScript file in the list of requirements. Dollar-sign
* variables will be interpolated with values from $vars similar to a .ss template.
*
* @param file The template file to load.
* @param vars The array of variables to load. These variables are loaded via string search & replace.
* @param string $file The template file to load, relative to docroot
* @param string[]|int[] $vars The array of variables to interpolate.
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public static function javascriptTemplate($file, $vars, $uniquenessID = null) {
self::backend()->javascriptTemplate($file, $vars, $uniquenessID);
}
/**
* Register the given stylesheet file as required.
* See {@link Requirements_Backend::css()}
* Register the given stylesheet into the list of requirements.
*
* @param $file String Filenames should be relative to the base, eg, 'framework/javascript/tree/tree.css'
* @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @see http://www.w3.org/TR/REC-CSS2/media.html
* @param string $file The CSS file to load, relative to site root
* @param string $media Comma-separated list of media types to use in the link tag
* (e.g. 'screen,projector')
*/
public static function css($file, $media = null) {
self::backend()->css($file, $media);
@ -152,116 +161,167 @@ class Requirements implements Flushable {
/**
* Registers the given themeable stylesheet as required.
*
* A CSS file in the current theme path name "themename/css/$name.css" is
* first searched for, and it that doesn't exist and the module parameter is
* set then a CSS file with that name in the module is used.
* A CSS file in the current theme path name 'themename/css/$name.css' is first searched for,
* and it that doesn't exist and the module parameter is set then a CSS file with that name in
* the module is used.
*
* NOTE: This API is experimental and may change in the future.
*
* @param string $name The name of the file - e.g. "/css/File.css" would have
* the name "File".
* @param string $module The module to fall back to if the css file does not
* exist in the current theme.
* @param string $media The CSS media attribute.
* @param string $name The name of the file - eg '/css/File.css' would have the name 'File'
* @param string $module The module to fall back to if the css file does not exist in the
* current theme.
* @param string $media Comma-separated list of media types to use in the link tag
* (e.g. 'screen,projector')
*/
public static function themedCSS($name, $module = null, $media = null) {
return self::backend()->themedCSS($name, $module, $media);
}
/**
* Clear either a single or all requirements.
* Caution: Clearing single rules works only with customCSS and customScript if you specified a {@uniquenessID}.
* Clear either a single or all requirements
*
* Caution: Clearing single rules added via customCSS and customScript only works if you
* originally specified a $uniquenessID.
*
* See {@link Requirements_Backend::clear()}
*
* @param $file String
* @param string|int $fileOrID
*/
public static function clear($fileOrID = null) {
self::backend()->clear($fileOrID);
}
/**
* Blocks inclusion of a specific file
* See {@link Requirements_Backend::block()}
*
* @param unknown_type $fileOrID
*/
public static function block($fileOrID) {
self::backend()->block($fileOrID);
}
/**
* Removes an item from the blocking-list.
* See {@link Requirements_Backend::unblock()}
*
* @param string $fileOrID
*/
public static function unblock($fileOrID) {
self::backend()->unblock($fileOrID);
}
/**
* Removes all items from the blocking-list.
* See {@link Requirements_Backend::unblock_all()}
*/
public static function unblock_all() {
self::backend()->unblock_all();
}
/**
* Restore requirements cleared by call to Requirements::clear
* See {@link Requirements_Backend::restore()}
*/
public static function restore() {
self::backend()->restore();
}
/**
* Update the given HTML content with the appropriate include tags for the registered
* requirements.
* See {@link Requirements_Backend::includeInHTML()} for more information.
* Block inclusion of a specific file
*
* The difference between this and {@link clear} is that the calling order does not matter;
* {@link clear} must be called after the initial registration, whereas {@link block} can be
* used in advance. This is useful, for example, to block scripts included by a superclass
* without having to override entire functions and duplicate a lot of code.
*
* @param string $templateFilePath Absolute path for the *.ss template file
* @param string $content HTML content that has already been parsed from the $templateFilePath
* through {@link SSViewer}.
* @return string HTML content thats augumented with the requirements before the closing <head> tag.
* Note that blocking should be used sparingly because it's hard to trace where an file is
* being blocked from.
*
* @param string|int $fileOrID
*/
public static function block($fileOrID) {
self::backend()->block($fileOrID);
}
/**
* Remove an item from the block list
*
* @param string|int $fileOrID
*/
public static function unblock($fileOrID) {
self::backend()->unblock($fileOrID);
}
/**
* Removes all items from the block list
*/
public static function unblock_all() {
self::backend()->unblock_all();
}
/**
* Update the given HTML content with the appropriate include tags for the registered
* requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
* including a head and body tag.
*
* @param string $templateFile No longer used, only retained for compatibility
* @param string $content HTML content that has already been parsed from the $templateFile
* through {@link SSViewer}
* @return string HTML content augmented with the requirements tags
*/
public static function includeInHTML($templateFile, $content) {
return self::backend()->includeInHTML($templateFile, $content);
}
/**
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
* HTTP Response
*
* @param SS_HTTPResponse $response
*/
public static function include_in_response(SS_HTTPResponse $response) {
return self::backend()->include_in_response($response);
}
/**
* Add i18n files from the given javascript directory.
* Add i18n files from the given javascript directory. SilverStripe expects that the given
* directory will contain a number of JavaScript files named by language: en_US.js, de_DE.js,
* etc.
*
* @param String
* @param Boolean
* @param Boolean
*
* See {@link Requirements_Backend::add_i18n_javascript()} for more information.
* @param string $langDir The JavaScript lang directory, relative to the site root, e.g.,
* 'framework/javascript/lang'
* @param bool $return Return all relative file paths rather than including them in
* requirements
* @param bool $langOnly Only include language files, not the base libraries
*
* @return array
*/
public static function add_i18n_javascript($langDir, $return = false, $langOnly = false) {
return self::backend()->add_i18n_javascript($langDir, $return, $langOnly);
}
/**
* Concatenate several css or javascript files into a single dynamically generated file.
* See {@link Requirements_Backend::combine_files()} for more info.
* Concatenate several css or javascript files into a single dynamically generated file. This
* increases performance by fewer HTTP requests.
*
* @param string $combinedFileName
* @param array $files
* The combined file is regenerated based on every file modification time. Optionally a
* rebuild can be triggered by appending ?flush=1 to the URL. If all files to be combined are
* JavaScript, we use the external JSMin library to minify the JavaScript.
*
* All combined files will have a comment on the start of each concatenated file denoting their
* original position. For easier debugging, we only minify JavaScript if not in development
* mode ({@link Director::isDev()}).
*
* CAUTION: You're responsible for ensuring that the load order for combined files is
* retained - otherwise combining JavaScript files can lead to functional errors in the
* JavaScript logic, and combining CSS can lead to incorrect inheritance. You can also
* only include each file once across all includes and comibinations in a single page load.
*
* CAUTION: Combining CSS Files discards any "media" information.
*
* Example for combined JavaScript:
* <code>
* Requirements::combine_files(
* 'foobar.js',
* array(
* 'mysite/javascript/foo.js',
* 'mysite/javascript/bar.js',
* )
* );
* </code>
*
* Example for combined CSS:
* <code>
* Requirements::combine_files(
* 'foobar.css',
* array(
* 'mysite/javascript/foo.css',
* 'mysite/javascript/bar.css',
* )
* );
* </code>
*
* @param string $combinedFileName Filename of the combined file relative to docroot
* @param array $files Array of filenames relative to docroot
* @param string $media
*
* @return bool|void
*/
public static function combine_files($combinedFileName, $files, $media = null) {
self::backend()->combine_files($combinedFileName, $files, $media);
}
/**
* Returns all combined files.
* See {@link Requirements_Backend::get_combine_files()}
* Return all combined files; keys are the combined file names, values are lists of
* files being combined.
*
* @return array
*/
@ -270,8 +330,7 @@ class Requirements implements Flushable {
}
/**
* Deletes all dynamically generated combined files from the filesystem.
* See {@link Requirements_Backend::delete_combine_files()}
* Delete all dynamically generated combined files from the filesystem
*
* @param string $combinedFileName If left blank, all combined files are deleted.
*/
@ -281,7 +340,7 @@ class Requirements implements Flushable {
/**
* Deletes all generated combined files in the configured combined files directory,
* but doesn't delete the directory itself.
* but doesn't delete the directory itself
*/
public static function delete_all_combined_files() {
return self::backend()->delete_all_combined_files();
@ -295,44 +354,36 @@ class Requirements implements Flushable {
}
/**
* See {@link combine_files()}.
* Do the heavy lifting involved in combining (and, in the case of JavaScript minifying) the
* combined files.
*/
public static function process_combined_files() {
return self::backend()->process_combined_files();
}
/**
* Returns all custom scripts
* See {@link Requirements_Backend::get_custom_scripts()}
* Set whether you want to write the JS to the body of the page rather than at the end of the
* head tag.
*
* @return array
*/
public static function get_custom_scripts() {
return self::backend()->get_custom_scripts();
}
/**
* Set whether you want to write the JS to the body of the page or
* in the head section
*
* @see Requirements_Backend::set_write_js_to_body()
* @param boolean
* @param bool
*/
public static function set_write_js_to_body($var) {
self::backend()->set_write_js_to_body($var);
}
/**
* Set the javascript to be forced to end of the HTML, or use the default.
* Useful if you use inline <script> tags, that don't need the javascripts
* included via Requirements::require();
* Set whether to force the JavaScript to end of the body. Useful if you use inline script tags
* that don't rely on scripts included via {@link Requirements::javascript()).
*
* @param boolean $var If true, force the javascripts to be included at the bottom.
* @param boolean $var If true, force the JavaScript to be included at the bottom of the page
*/
public static function set_force_js_to_bottom($var) {
self::backend()->set_force_js_to_bottom($var);
}
/**
* Output debugging information
*/
public static function debug() {
return self::backend()->debug();
}
@ -346,72 +397,70 @@ class Requirements implements Flushable {
class Requirements_Backend {
/**
* Do we want requirements to suffix onto the requirement link
* tags for caching or is it disabled. Getter / Setter available
* through {@link Requirements::set_suffix_requirements()}
*
* Whether to add caching query params to the requests for file-based requirements.
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
* while automatically busting this cache every time the file is changed.
*
* @var bool
*/
protected $suffix_requirements = true;
/**
* Enable combining of css/javascript files.
* Whether to combine CSS and JavaScript files
*
* @var boolean
* @var bool
*/
protected $combined_files_enabled = true;
/**
* Paths to all required .js files relative to the webroot.
* Paths to all required JavaScript files relative to docroot
*
* @var array $javascript
*/
protected $javascript = array();
/**
* Paths to all required .css files relative to the webroot.
* Paths to all required CSS files relative to the docroot.
*
* @var array $css
*/
protected $css = array();
/**
* All custom javascript code that is inserted
* directly at the bottom of the HTML <head> tag.
* All custom javascript code that is inserted into the page's HTML
*
* @var array $customScript
*/
protected $customScript = array();
/**
* All custom CSS rules which are inserted
* directly at the bottom of the HTML <head> tag.
* All custom CSS rules which are inserted directly at the bottom of the HTML <head> tag
*
* @var array $customCSS
*/
protected $customCSS = array();
/**
* All custom HTML markup which is added before
* the closing <head> tag, e.g. additional metatags.
* This is preferred to entering tags directly into
* All custom HTML markup which is added before the closing <head> tag, e.g. additional
* metatags.
*/
protected $customHeadTags = array();
/**
* Remembers the filepaths of all cleared Requirements
* through {@link clear()}.
* Remembers the file paths or uniquenessIDs of all Requirements cleared through
* {@link clear()}, so that they can be restored later.
*
* @var array $disabled
*/
protected $disabled = array();
/**
* The filepaths (relative to webroot) or
* uniquenessIDs of any included requirements
* which should be blocked when executing {@link inlcudeInHTML()}.
* This is useful to e.g. prevent core classes to modifying
* Requirements without subclassing the entire functionality.
* The file paths (relative to docroot) or uniquenessIDs of any included requirements which
* should be blocked when executing {@link inlcudeInHTML()}. This is useful, for example,
* to block scripts included by a superclass without having to override entire functions and
* duplicate a lot of code.
*
* Use {@link unblock()} or {@link unblock_all()} to revert changes.
*
* @var array $blocked
@ -419,82 +468,93 @@ class Requirements_Backend {
protected $blocked = array();
/**
* See {@link combine_files()}.
* A list of combined files registered via {@link combine_files()}. Keys are the output file
* names, values are lists of input files.
*
* @var array $combine_files
*/
public $combine_files = array();
/**
* Using the JSMin library to minify any
* javascript file passed to {@link combine_files()}.
* Use the JSMin library to minify any javascript file passed to {@link combine_files()}.
*
* @var boolean
* @var bool
*/
public $combine_js_with_jsmin = true;
/**
* Setting for whether or not a file header should be written when
* combining files.
* Whether or not file headers should be written when combining files
*
* @var boolean
*/
public $write_header_comment = true;
/**
* @var string By default, combined files are stored in assets/_combinedfiles.
* Set this by calling Requirements::set_combined_files_folder()
* Where to save combined files. By default they're placed in assets/_combinedfiles, however
* this may be an issue depending on your setup, especially for CSS files which often contain
* relative paths.
*
* @var string
*/
protected $combinedFilesFolder = null;
/**
* Put all javascript includes at the bottom of the template
* before the closing <body> tag instead of the <head> tag.
* This means script downloads won't block other HTTP-requests,
* which can be a performance improvement.
* Caution: Doesn't work when modifying the DOM from those external
* scripts without listening to window.onload/document.ready
* (e.g. toplevel document.write() calls).
*
* @see http://developer.yahoo.com/performance/rules.html#js_bottom
*
* @var boolean
* Put all JavaScript includes at the bottom of the template before the closing <body> tag,
* rather than the default behaviour of placing them at the end of the <head> tag. This means
* script downloads won't block other HTTP requests, which can be a performance improvement.
*
* @var bool
*/
public $write_js_to_body = true;
/**
* Force the javascripts to the bottom of the page, even if there's a
* <script> tag in the body already
*
* Force the JavaScript to the bottom of the page, even if there's a script tag in the body already
*
* @var boolean
*/
protected $force_js_to_bottom = false;
/**
* Enable or disable the combination of CSS and JavaScript files
*
* @param $enable
*/
public function set_combined_files_enabled($enable) {
$this->combined_files_enabled = (bool) $enable;
}
/**
* Check whether file combination is enabled.
*
* @return bool
*/
public function get_combined_files_enabled() {
return $this->combined_files_enabled;
}
/**
* @param String $folder
* Set the folder to save combined files in. By default they're placed in assets/_combinedfiles,
* however this may be an issue depending on your setup, especially for CSS files which often
* contain relative paths.
*
* @param string $folder
*/
public function setCombinedFilesFolder($folder) {
$this->combinedFilesFolder = $folder;
}
/**
* @return String Folder relative to the webroot
* @return string Folder relative to the webroot
*/
public function getCombinedFilesFolder() {
return ($this->combinedFilesFolder) ? $this->combinedFilesFolder : ASSETS_DIR . '/_combinedfiles';
}
/**
* Set whether we want to suffix requirements with the time /
* location on to the requirements
* Set whether to add caching query params to the requests for file-based requirements.
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
* while automatically busting this cache every time the file is changed.
*
* @param bool
*/
@ -503,7 +563,7 @@ class Requirements_Backend {
}
/**
* Return whether we want to suffix requirements
* Check whether we want to suffix requirements
*
* @return bool
*/
@ -512,45 +572,47 @@ class Requirements_Backend {
}
/**
* Set whether you want the files written to the head or the body. It
* writes to the body by default which can break some scripts
* Set whether you want to write the JS to the body of the page rather than at the end of the
* head tag.
*
* @param boolean
* @param bool
*/
public function set_write_js_to_body($var) {
$this->write_js_to_body = $var;
}
/**
* Forces the javascript to the end of the body, just before the closing body-tag.
* Forces the JavaScript requirements to the end of the body, right before the closing tag
*
* @param boolean
* @param bool
*/
public function set_force_js_to_bottom($var) {
$this->force_js_to_bottom = $var;
}
/**
* Register the given javascript file as required.
* Filenames should be relative to the base, eg, 'framework/javascript/loader.js'
* Register the given JavaScript file as required.
*
* @param string $file Relative to docroot
*/
public function javascript($file) {
$this->javascript[$file] = true;
}
/**
* Returns an array of all included javascript
* Returns an array of all required JavaScript
*
* @return array
*/
public function get_javascript() {
return array_keys(array_diff_key($this->javascript,$this->blocked));
return array_keys(array_diff_key($this->javascript, $this->blocked));
}
/**
* Add the javascript code to the header of the page
* @todo Make Requirements automatically put this into a separate file :-)
* @param script The script content
* @param uniquenessID Use this to ensure that pieces of code only get added once.
* Register the given JavaScript code into the list of requirements
*
* @param string $script The script content as a string (without enclosing <script> tag)
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public function customScript($script, $uniquenessID = null) {
if($uniquenessID) $this->customScript[$uniquenessID] = $script;
@ -560,10 +622,27 @@ class Requirements_Backend {
}
/**
* Include custom CSS styling to the header of the page.
* Return all registered custom scripts
*
* @param string $script CSS selectors as a string (without <style> tag enclosing selectors).
* @param int $uniquenessID Group CSS by a unique ID as to avoid duplicate custom CSS in header
* @return array
*/
public function get_custom_scripts() {
$requirements = "";
if($this->customScript) {
foreach($this->customScript as $script) {
$requirements .= "$script\n";
}
}
return $requirements;
}
/**
* Register the given CSS styles into the list of requirements
*
* @param string $script CSS selectors as a string (without enclosing <style> tag)
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public function customCSS($script, $uniquenessID = null) {
if($uniquenessID) $this->customCSS[$uniquenessID] = $script;
@ -571,10 +650,10 @@ class Requirements_Backend {
}
/**
* Add the following custom code to the <head> section of the page.
* Add the following custom HTML code to the <head> section of the page
*
* @param string $html
* @param string $uniquenessID
* @param string $html Custom HTML code
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public function insertHeadTags($html, $uniquenessID = null) {
if($uniquenessID) $this->customHeadTags[$uniquenessID] = $html;
@ -582,9 +661,12 @@ class Requirements_Backend {
}
/**
* Load the given javascript template with the page.
* @param file The template file to load.
* @param vars The array of variables to load. These variables are loaded via string search & replace.
* Include the content of the given JavaScript file in the list of requirements. Dollar-sign
* variables will be interpolated with values from $vars similar to a .ss template.
*
* @param string $file The template file to load, relative to docroot
* @param string[]|int[] $vars The array of variables to interpolate.
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
*/
public function javascriptTemplate($file, $vars, $uniquenessID = null) {
$script = file_get_contents(Director::getAbsFile($file));
@ -601,11 +683,11 @@ class Requirements_Backend {
}
/**
* Register the given stylesheet file as required.
* Register the given stylesheet into the list of requirements.
*
* @param $file String Filenames should be relative to the base, eg, 'framework/javascript/tree/tree.css'
* @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @see http://www.w3.org/TR/REC-CSS2/media.html
* @param string $file The CSS file to load, relative to site root
* @param string $media Comma-separated list of media types to use in the link tag
* (e.g. 'screen,projector')
*/
public function css($file, $media = null) {
$this->css[$file] = array(
@ -613,28 +695,22 @@ class Requirements_Backend {
);
}
/**
* Get the list of registered CSS file requirements, excluding blocked files
*
* @return array
*/
public function get_css() {
return array_diff_key($this->css, $this->blocked);
}
/**
* Needed to actively prevent the inclusion of a file,
* e.g. when using your own jQuery version.
* Blocking should only be used as an exception, because
* it is hard to trace back. You can just block items with an
* ID, so make sure you add an unique identifier to customCSS() and customScript().
* Clear either a single or all requirements
*
* @param string $fileOrID
*/
public function block($fileOrID) {
$this->blocked[$fileOrID] = $fileOrID;
}
/**
* Clear either a single or all requirements.
* Caution: Clearing single rules works only with customCSS and customScript if you specified a {@uniquenessID}.
* Caution: Clearing single rules added via customCSS and customScript only works if you
* originally specified a $uniquenessID.
*
* @param $file String
* @param string|int $fileOrID
*/
public function clear($fileOrID = null) {
if($fileOrID) {
@ -659,21 +735,6 @@ class Requirements_Backend {
}
}
/**
* Removes an item from the blocking-list.
* CAUTION: Does not "re-add" any previously blocked elements.
* @param string $fileOrID
*/
public function unblock($fileOrID) {
if(isset($this->blocked[$fileOrID])) unset($this->blocked[$fileOrID]);
}
/**
* Removes all items from the blocking-list.
*/
public function unblock_all() {
$this->blocked = array();
}
/**
* Restore requirements cleared by call to Requirements::clear
*/
@ -684,18 +745,48 @@ class Requirements_Backend {
$this->customCSS = $this->disabled['customCSS'];
$this->customHeadTags = $this->disabled['customHeadTags'];
}
/**
* Block inclusion of a specific file
*
* The difference between this and {@link clear} is that the calling order does not matter;
* {@link clear} must be called after the initial registration, whereas {@link block} can be
* used in advance. This is useful, for example, to block scripts included by a superclass
* without having to override entire functions and duplicate a lot of code.
*
* Note that blocking should be used sparingly because it's hard to trace where an file is
* being blocked from.
*
* @param string|int $fileOrID
*/
public function block($fileOrID) {
$this->blocked[$fileOrID] = $fileOrID;
}
/**
* Remove an item from the block list
*
* @param string|int $fileOrID
*/
public function unblock($fileOrID) {
if(isset($this->blocked[$fileOrID])) unset($this->blocked[$fileOrID]);
}
/**
* Removes all items from the block list
*/
public function unblock_all() {
$this->blocked = array();
}
/**
* Update the given HTML content with the appropriate include tags for the registered
* requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
* including a <head> tag. The requirements will insert before the closing <head> tag automatically.
* including a head and body tag.
*
* @todo Calculate $prefix properly
*
* @param string $templateFilePath Absolute path for the *.ss template file
* @param string $content HTML content that has already been parsed from the $templateFilePath
* through {@link SSViewer}.
* @return string HTML content thats augumented with the requirements before the closing <head> tag.
* @param string $templateFile No longer used, only retained for compatibility
* @param string $content HTML content that has already been parsed from the $templateFile
* through {@link SSViewer}
* @return string HTML content augmented with the requirements tags
*/
public function includeInHTML($templateFile, $content) {
if(
@ -715,8 +806,7 @@ class Requirements_Backend {
}
}
// add all inline javascript *after* including external files which
// they might rely on
// Add all inline JavaScript *after* including external files they might rely on
if($this->customScript) {
foreach(array_diff_key($this->customScript,$this->blocked) as $script) {
$jsRequirements .= "<script type=\"text/javascript\">\n//<![CDATA[\n";
@ -746,9 +836,8 @@ class Requirements_Backend {
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
// We put script tags into the body, for performance.
// We forcefully put it at the bottom instead of before
// the first script-tag occurence
// Forcefully put the scripts at the bottom of the body instead of before the first
// script tag.
$content = preg_replace("/(<\/body[^>]*>)/i", $jsRequirements . "\\1", $content);
// Put CSS at the bottom of the head
@ -756,8 +845,7 @@ class Requirements_Backend {
} elseif($this->write_js_to_body) {
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
// We put script tags into the body, for performance.
// If your template already has script tags in the body, then we try to put our script
// tags just before those. Otherwise, we put it at the bottom.
$p2 = stripos($content, '<body');
@ -766,7 +854,7 @@ class Requirements_Backend {
$commentTags = array();
$canWriteToBody = ($p1 !== false)
&&
//check that the script tag is not inside a html comment tag
// Check that the script tag is not inside a html comment tag
!(
preg_match('/.*(?|(<!--)|(-->))/U', $content, $commentTags, 0, $p1)
&&
@ -791,7 +879,10 @@ class Requirements_Backend {
}
/**
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the HTTP response
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
* HTTP Response
*
* @param SS_HTTPResponse $response
*/
public function include_in_response(SS_HTTPResponse $response) {
$this->process_combined_files();
@ -819,12 +910,17 @@ class Requirements_Backend {
}
/**
* Add i18n files from the given javascript directory. SilverStripe expects that the given directory
* will contain a number of java script files named by language: en_US.js, de_DE.js, etc.
* Add i18n files from the given javascript directory. SilverStripe expects that the given
* directory will contain a number of JavaScript files named by language: en_US.js, de_DE.js,
* etc.
*
* @param String The javascript lang directory, relative to the site root, e.g., 'framework/javascript/lang'
* @param Boolean Return all relative file paths rather than including them in requirements
* @param Boolean Only include language files, not the base libraries
* @param string $langDir The JavaScript lang directory, relative to the site root, e.g.,
* 'framework/javascript/lang'
* @param bool $return Return all relative file paths rather than including them in
* requirements
* @param bool $langOnly Only include language files, not the base libraries
*
* @return array
*/
public function add_i18n_javascript($langDir, $return = false, $langOnly = false) {
$files = array();
@ -863,10 +959,10 @@ class Requirements_Backend {
}
/**
* Finds the path for specified file.
* Finds the path for specified file
*
* @param string $fileOrUrl
* @return string|boolean
* @return string|bool
*/
protected function path_for_file($fileOrUrl) {
if(preg_match('{^//|http[s]?}', $fileOrUrl)) {
@ -896,27 +992,22 @@ class Requirements_Backend {
}
/**
* Concatenate several css or javascript files into a single dynamically generated
* file (stored in {@link Director::baseFolder()}). This increases performance
* by fewer HTTP requests.
* Concatenate several css or javascript files into a single dynamically generated file. This
* increases performance by fewer HTTP requests.
*
* The combined file is regenerated
* based on every file modification time. Optionally a rebuild can be triggered
* by appending ?flush=1 to the URL.
* If all files to be combined are javascript, we use the external JSMin library
* to minify the javascript. This can be controlled by {@link $combine_js_with_jsmin}.
* The combined file is regenerated based on every file modification time. Optionally a
* rebuild can be triggered by appending ?flush=1 to the URL. If all files to be combined are
* JavaScript, we use the external JSMin library to minify the JavaScript. This can be
* controlled using {@link $combine_js_with_jsmin}.
*
* All combined files will have a comment on the start of each concatenated file
* denoting their original position. For easier debugging, we recommend to only
* minify javascript if not in development mode ({@link Director::isDev()}).
* All combined files will have a comment on the start of each concatenated file denoting their
* original position. For easier debugging, we only minify JavaScript if not in development
* mode ({@link Director::isDev()}).
*
* CAUTION: You're responsible for ensuring that the load order for combined files
* is retained - otherwise combining javascript files can lead to functional errors
* in the javascript logic, and combining css can lead to wrong styling inheritance.
* Depending on the javascript logic, you also have to ensure that files are not included
* in more than one combine_files() call.
* Best practice is to include every javascript file in exactly *one* combine_files()
* directive to avoid the issues mentioned above - this is enforced by this function.
* CAUTION: You're responsible for ensuring that the load order for combined files is
* retained - otherwise combining JavaScript files can lead to functional errors in the
* JavaScript logic, and combining CSS can lead to incorrect inheritance. You can also
* only include each file once across all includes and combinations in a single page load.
*
* CAUTION: Combining CSS Files discards any "media" information.
*
@ -925,9 +1016,9 @@ class Requirements_Backend {
* Requirements::combine_files(
* 'foobar.js',
* array(
* 'mysite/javascript/foo.js',
* 'mysite/javascript/bar.js',
* )
* 'mysite/javascript/foo.js',
* 'mysite/javascript/bar.js',
* )
* );
* </code>
*
@ -935,22 +1026,18 @@ class Requirements_Backend {
* <code>
* Requirements::combine_files(
* 'foobar.css',
* array(
* 'mysite/javascript/foo.css',
* 'mysite/javascript/bar.css',
* )
* array(
* 'mysite/javascript/foo.css',
* 'mysite/javascript/bar.css',
* )
* );
* </code>
*
* @see http://code.google.com/p/jsmin-php/
*
* @todo Should we enforce unique inclusion of files, or leave it to the developer? Can auto-detection cause
* breaks?
*
* @param string $combinedFileName Filename of the combined file (will be stored in {@link Director::baseFolder()}
* by default)
* @param array $files Array of filenames relative to the webroot
* @param string $media Comma-separated list of media-types (e.g. "screen,projector").
* @param string $combinedFileName Filename of the combined file relative to docroot
* @param array $files Array of filenames relative to docroot
* @param string $media
*
* @return bool|void
*/
public function combine_files($combinedFileName, $files, $media = null) {
// duplicate check
@ -1004,8 +1091,10 @@ class Requirements_Backend {
$this->combine_files[$combinedFileName] = $files;
}
/**
* Returns all combined files.
/**
* Return all combined files; keys are the combined file names, values are lists of
* files being combined.
*
* @return array
*/
public function get_combine_files() {
@ -1013,7 +1102,7 @@ class Requirements_Backend {
}
/**
* Deletes all dynamically generated combined files from the filesystem.
* Delete all dynamically generated combined files from the filesystem
*
* @param string $combinedFileName If left blank, all combined files are deleted.
*/
@ -1043,16 +1132,19 @@ class Requirements_Backend {
}
}
/**
* Clear all registered CSS and JavaScript file combinations
*/
public function clear_combined_files() {
$this->combine_files = array();
}
/**
* See {@link combine_files()}
*
* Do the heavy lifting involved in combining (and, in the case of JavaScript minifying) the
* combined files.
*/
public function process_combined_files() {
// The class_exists call prevents us from loading SapphireTest.php (slow) just to know that
// The class_exists call prevents us loading SapphireTest.php (slow) just to know that
// SapphireTest isn't running :-)
if(class_exists('SapphireTest', false)) $runningTest = SapphireTest::is_running_test();
else $runningTest = false;
@ -1077,7 +1169,7 @@ class Requirements_Backend {
// Work out the relative URL for the combined files from the base folder
$combinedFilesFolder = ($this->getCombinedFilesFolder()) ? ($this->getCombinedFilesFolder() . '/') : '';
// Figure out which ones apply to this pageview
// Figure out which ones apply to this request
$combinedFiles = array();
$newJSRequirements = array();
$newCSSRequirements = array();
@ -1112,8 +1204,9 @@ class Requirements_Backend {
Filesystem::makeFolder(dirname($combinedFilePath));
}
// If the file isn't writeable, don't even bother trying to make the combined file and return (falls back
// to uncombined). Complex test because is_writable fails if the file doesn't exist yet.
// If the file isn't writeable, don't even bother trying to make the combined file and return. The
// files will be included individually instead. This is a complex test because is_writable fails
// if the file doesn't exist yet.
if((file_exists($combinedFilePath) && !is_writable($combinedFilePath))
|| (!file_exists($combinedFilePath) && !is_writable(dirname($combinedFilePath)))
) {
@ -1133,7 +1226,7 @@ class Requirements_Backend {
}
$refresh = $srcLastMod > filemtime($combinedFilePath);
} else {
// file doesn't exist, or refresh was explicitly required
// File doesn't exist, or refresh was explicitly required
$refresh = true;
}
@ -1151,8 +1244,7 @@ class Requirements_Backend {
}
if ($this->write_header_comment) {
// write a header comment for each file for easier identification and debugging
// also the semicolon between each file is required for jQuery to be combinable properly
// Write a header comment for each file for easier identification and debugging. The semicolon between each file is required for jQuery to be combined properly and protects against unterminated statements.
$combinedData .= "/****** FILE: $file *****/\n";
}
@ -1168,7 +1260,7 @@ class Requirements_Backend {
}
if($failedToMinify){
// Failed to minify, use unminified. This warning is raised at the end to allow code execution
// Failed to minify, use unminified files instead. This warning is raised at the end to allow code execution
// to complete in case this warning is caught inside a try-catch block.
user_error('Failed to minify '.$file.', exception: '.$e->getMessage(), E_USER_WARNING);
}
@ -1181,12 +1273,19 @@ class Requirements_Backend {
}
}
// @todo Alters the original information, which means you can't call this
// method repeatedly - it will behave different on the second call!
// Note: Alters the original information, which means you can't call this method repeatedly - it will behave
// differently on the subsequent calls
$this->javascript = $newJSRequirements;
$this->css = $newCSSRequirements;
}
/**
* Minify the given $content according to the file type indicated in $filename
*
* @param string $filename
* @param string $content
* @return string
*/
protected function minifyFile($filename, $content) {
// if we have a javascript file and jsmin is enabled, minify the content
$isJS = stripos($filename, '.js');
@ -1200,20 +1299,18 @@ class Requirements_Backend {
return $content;
}
public function get_custom_scripts() {
$requirements = "";
if($this->customScript) {
foreach($this->customScript as $script) {
$requirements .= "$script\n";
}
}
return $requirements;
}
/**
* @see Requirements::themedCSS()
* Registers the given themeable stylesheet as required.
*
* A CSS file in the current theme path name 'themename/css/$name.css' is first searched for,
* and it that doesn't exist and the module parameter is set then a CSS file with that name in
* the module is used.
*
* @param string $name The name of the file - eg '/css/File.css' would have the name 'File'
* @param string $module The module to fall back to if the css file does not exist in the
* current theme.
* @param string $media Comma-separated list of media types to use in the link tag
* (e.g. 'screen,projector')
*/
public function themedCSS($name, $module = null, $media = null) {
$theme = SSViewer::get_theme_folder();
@ -1234,6 +1331,9 @@ class Requirements_Backend {
}
}
/**
* Output debugging information.
*/
public function debug() {
Debug::show($this->javascript);
Debug::show($this->css);

View File

@ -1,11 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="silverstripe_version" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Block Scripts" stopProcessing="true">
<match url="([^\\/]+)\.(php|php3|php4|php5|phtml|inc)$" />
<conditions trackAllCaptures="true">
<add input="{REQUEST_FILENAME}" pattern="\b(main|rpc|tiny_mce_gzip)\.php$" negate="true" />
</conditions>
<action type="AbortRequest" />
</rule>
<rule name="Block Version" stopProcessing="true">
<match url="\bsilverstripe_version$" />
<action type="AbortRequest" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>