Merge remote-tracking branch 'origin/3.1'

Conflicts:
	docs/en/misc/contributing/issues.md
	docs/en/reference/uploadfield.md
	forms/HtmlEditorField.php
	i18n/i18n.php
	javascript/HtmlEditorField.js
	model/DB.php
	model/Image.php
	model/SQLQuery.php
This commit is contained in:
Mateusz Uzdowski 2014-08-13 17:06:38 +12:00
commit 8bf3853887
72 changed files with 3018 additions and 1267 deletions

View File

@ -267,8 +267,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
if(empty($_REQUEST['executeForm']) && !$this->request->isAjax()) $this->extend('accessedCMS');
// Set the members html editor config
HtmlEditorConfig::set_active(Member::currentUser()->getHtmlEditorConfigForCMS());
if(Member::currentUser()) {
HtmlEditorConfig::set_active(Member::currentUser()->getHtmlEditorConfigForCMS());
}
// Set default values in the config if missing. These things can't be defined in the config
// file because insufficient information exists when that is being processed
$htmlEditorConfig = HtmlEditorConfig::get_active();
@ -797,7 +799,12 @@ class LeftAndMain extends Controller implements PermissionProvider {
? $filter->getChildrenMethod()
: 'AllChildrenIncludingDeleted';
if(!$numChildrenMethod) $numChildrenMethod = 'numChildren';
if(!$numChildrenMethod) {
$numChildrenMethod = 'numChildren';
if($filter && $filter->getNumChildrenMethod()) {
$numChildrenMethod = $filter->getNumChildrenMethod();
}
}
if(!$filterFunction) $filterFunction = ($filter) ? array($filter, 'isPageIncluded') : null;
// Get the tree root
@ -810,7 +817,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
$obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod);
// Ensure current page is exposed
if($p = $this->currentPage()) $obj->markToExpose($p);
// This call flushes the Hierarchy::$marked cache when the current node is deleted
// @see CMSMain::getRecord()
// This will make it impossible to show children under a deleted parent page
// if($p = $this->currentPage()) $obj->markToExpose($p);
// NOTE: SiteTree/CMSMain coupling :-(
if(class_exists('SiteTree')) {
@ -821,9 +831,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
// getChildrenAsUL is a flexible and complex way of traversing the tree
$controller = $this;
$recordController = ($this->stat('tree_class') == 'SiteTree') ? singleton('CMSPageEditController') : $this;
$titleFn = function(&$child) use(&$controller, &$recordController) {
$titleFn = function(&$child, $numChildrenMethod) use(&$controller, &$recordController) {
$link = Controller::join_links($recordController->Link("show"), $child->ID);
return LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child))->forTemplate();
$node = LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child), $numChildrenMethod);
return $node->forTemplate();
};
// Limit the amount of nodes shown for performance reasons.
@ -931,6 +942,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
if($id === "") continue; // $id may be a blank string, which is invalid and should be skipped over
$record = $this->getRecord($id);
if(!$record) continue; // In case a page is no longer available
$recordController = ($this->stat('tree_class') == 'SiteTree')
? singleton('CMSPageEditController')
: $this;
@ -1866,10 +1878,22 @@ class LeftAndMain_TreeNode extends ViewableData {
*/
protected $isCurrent;
public function __construct($obj, $link = null, $isCurrent = false) {
/**
* @var string
*/
protected $numChildrenMethod;
/**
* @param $obj
* @param null $link
* @param bool $isCurrent
* @param $numChildrenMethod
*/
public function __construct($obj, $link = null, $isCurrent = false, $numChildrenMethod='numChildren') {
$this->obj = $obj;
$this->link = $link;
$this->isCurrent = $isCurrent;
$this->numChildrenMethod = $numChildrenMethod;
}
/**
@ -1890,7 +1914,7 @@ class LeftAndMain_TreeNode extends ViewableData {
}
public function getClasses() {
$classes = $this->obj->CMSTreeClasses();
$classes = $this->obj->CMSTreeClasses($this->numChildrenMethod);
if($this->isCurrent) $classes .= " current";
$flags = $this->obj->hasMethod('getStatusFlags') ? $this->obj->getStatusFlags() : false;
if ($flags) {

View File

@ -11,6 +11,8 @@
*/
$(".cms .field.cms-description-tooltip").entwine({
onmatch: function() {
this._super();
var descriptionEl = this.find('.description'), inputEl, tooltipEl;
if(descriptionEl.length) {
this
@ -19,8 +21,8 @@
.tooltip({content: descriptionEl.html()});
descriptionEl.remove();
}
}
});
},
});
$(".cms .field.cms-description-tooltip :input").entwine({
onfocusin: function(e) {

View File

@ -517,7 +517,8 @@
});
$('.cms-edit-form').entwine({
onadd: function() {
onadd: function() {
this._super();
$('.cms-preview')._initialiseFromContent();
}
});

View File

@ -585,11 +585,14 @@ jQuery.noConflict();
var newFragments = {}, newContentEls;
// If content type is text/json (ignoring charset and other parameters)
if(xhr.getResponseHeader('Content-Type').match(/^text\/json[ \t]*;?/i)) {
if(xhr.getResponseHeader('Content-Type').match(/^((text)|(application))\/json[ \t]*;?/i)) {
newFragments = data;
} else {
// Fall back to replacing the content fragment if HTML is returned
$data = $(data);
var fragment = document.createDocumentFragment();
jQuery.clean( [ data ], document, fragment, [] );
$data = $(jQuery.merge( [], fragment.childNodes ));
// Try and guess the fragment if none is provided
// TODO: data-pjax-fragment might actually give us the fragment. For now we just check most common case
@ -948,8 +951,8 @@ jQuery.noConflict();
setTimeout(function() {
form.clickedButton = null;
}, 10);
}
});
}
});
this.redraw();
this._super();

View File

@ -1,6 +1,6 @@
(function($) {
$.entwine('ss', function($){
$('.memberdatetimeoptionset').entwine({
onmatch: function() {
this.find('.description .toggle-content').hide();

View File

@ -0,0 +1,10 @@
{
"LeftAndMain.CONFIRMUNSAVED": "Ĉu vi vere volas navigi for de ĉi tiu paĝo?\n\nAVERTO: Viaj ŝanĝoj ne estas konservitaj.\n\nPremu je Akcepti por daŭrigi, aŭ Nuligi por resti ĉe la aktuala paĝo.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "AVERTO: Viaj ŝanĝoj ne estas konservitaj.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ĉu vi vere volas forigi %s grupojn?",
"ModelAdmin.SAVED": "Konservita",
"ModelAdmin.REALLYDELETE": "Ĉi vi vere volas forigi?",
"ModelAdmin.DELETED": "Forigita",
"ModelAdmin.VALIDATIONERROR": "Validiga eraro",
"LeftAndMain.PAGEWASDELETED": "Ĉi tiu paĝo estas forigita. Por redakti paĝon, elektu ĝin maldekstre."
}

View File

@ -0,0 +1,10 @@
{
"LeftAndMain.CONFIRMUNSAVED": "Er du sikker på at du vil forlate denne siden?\n\nADVARSEL: Endringene din har ikke blitt lagret.\n\nTrykk OK for å fortsette eller Avbryt for å holde deg på samme side.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "ADVARSEL: Endringene dine har ikke blitt lagret.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vil du virkelig slette %s grupper?",
"ModelAdmin.SAVED": "Lagret",
"ModelAdmin.REALLYDELETE": "Vil du virkelig slette?",
"ModelAdmin.DELETED": "Slettet",
"ModelAdmin.VALIDATIONERROR": "Valideringsfeil",
"LeftAndMain.PAGEWASDELETED": "Denne siden ble slettet. For å redigere en side, velg den fra listen til venstre."
}

View File

@ -5,6 +5,6 @@
"ModelAdmin.SAVED": "Opgeslagen",
"ModelAdmin.REALLYDELETE": "Weet u zeker dat u wilt verwijderen?",
"ModelAdmin.DELETED": "Verwijderd",
"ModelAdmin.VALIDATIONERROR": "Validatie fout",
"LeftAndMain.PAGEWASDELETED": "Deze pagina is verwijderd. Om een pagina aan te passen, selecteer pagina aan de linkerkant."
"ModelAdmin.VALIDATIONERROR": "Validatiefout",
"LeftAndMain.PAGEWASDELETED": "Deze pagina is verwijderd. Om een pagina aan te passen, selecteer deze aan de linkerkant."
}

View File

@ -0,0 +1,10 @@
{
"LeftAndMain.CONFIRMUNSAVED": "Res želite zapusitit stran?\n\nOPOZORILO: spremembe niso bile shranjene\n\nKliknite OK za nadaljevanje ali Prekliči, da ostanete na trenutni strani.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "OPOZORILO: spremembe niso bile shranjene.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Izbrišem %s skupin?",
"ModelAdmin.SAVED": "Shranjeno",
"ModelAdmin.REALLYDELETE": "Izbrišem?",
"ModelAdmin.DELETED": "Izbrisano",
"ModelAdmin.VALIDATIONERROR": "Napaka pri preverjanju",
"LeftAndMain.PAGEWASDELETED": "Stran je bila izbrisana. Za urejanje izberite stran na levi."
}

View File

@ -200,6 +200,52 @@ class LeftAndMainTest extends FunctionalTest {
$this->session()->inst_set('loggedInAs', null);
}
/**
* Test {@see LeftAndMain::updatetreenodes}
*/
public function testUpdateTreeNodes() {
$page1 = $this->objFromFixture('LeftAndMainTest_Object', 'page1');
$page2 = $this->objFromFixture('LeftAndMainTest_Object', 'page2');
$page3 = $this->objFromFixture('LeftAndMainTest_Object', 'page3');
$page31 = $this->objFromFixture('LeftAndMainTest_Object', 'page31');
$page32 = $this->objFromFixture('LeftAndMainTest_Object', 'page32');
$this->logInWithPermission('ADMIN');
// Check page
$result = $this->get('LeftAndMainTest_Controller/updatetreenodes?ids='.$page1->ID);
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody(), true);
$pageData = $data[$page1->ID];
$this->assertEquals(0, $pageData['ParentID']);
$this->assertEquals($page2->ID, $pageData['NextID']);
$this->assertEmpty($pageData['PrevID']);
// check subpage
$result = $this->get('LeftAndMainTest_Controller/updatetreenodes?ids='.$page31->ID);
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody(), true);
$pageData = $data[$page31->ID];
$this->assertEquals($page3->ID, $pageData['ParentID']);
$this->assertEquals($page32->ID, $pageData['NextID']);
$this->assertEmpty($pageData['PrevID']);
// Multiple pages
$result = $this->get('LeftAndMainTest_Controller/updatetreenodes?ids='.$page1->ID.','.$page2->ID);
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody(), true);
$this->assertEquals(2, count($data));
// Invalid IDs
$result = $this->get('LeftAndMainTest_Controller/updatetreenodes?ids=-3');
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody(), true);
$this->assertEquals(0, count($data));
}
}
/**
@ -228,4 +274,6 @@ class LeftAndMainTest_Object extends DataObject implements TestOnly {
'Hierarchy'
);
public function CMSTreeClasses() {}
}

View File

@ -1,65 +1,94 @@
LeftAndMainTest_Object:
page1:
Title: Page 1
Sort: 1
page2:
Title: Page 2
Sort: 2
page3:
Title: Page 3
Sort: 3
page31:
Title: Page 3.1
Parent: =>LeftAndMainTest_Object.page3
Sort: 1
page32:
Title: Page 3.2
Parent: =>LeftAndMainTest_Object.page3
Sort: 2
page4:
Title: Page 4
Sort: 4
page5:
Title: Page 5
Sort: 5
page6:
Title: Page 6
Sort: 6
page7:
Title: Page 7
Sort: 7
page8:
Title: Page 8
Sort: 8
page9:
Title: Page 9
Sort: 9
page10:
Title: Page 10
Sort: 10
page11:
Title: Page 11
Sort: 11
page12:
Title: Page 12
Sort: 12
page13:
Title: Page 13
Sort: 13
page14:
Title: Page 14
Sort: 14
page15:
Title: Page 15
Sort: 15
page16:
Title: Page 16
Sort: 16
page17:
Title: Page 17
Sort: 17
page18:
Title: Page 18
Sort: 18
page19:
Title: Page 19
Sort: 19
page20:
Title: Page 20
Sort: 20
page21:
Title: Page 21
Sort: 21
page22:
Title: Page 22
Sort: 22
page23:
Title: Page 23
Sort: 23
page24:
Title: Page 24
Sort: 24
page25:
Title: Page 25
Sort: 25
page26:
Title: Page 26
Sort: 26
home:
Title: Home
URLSegment: home
Sort: 0
Group:
admin:
Title: Administrators

View File

@ -22,6 +22,8 @@ interface AfterCallAspect {
* The name of the method being called
* @param string $args
* The arguments that were passed to the method call
* @param mixed $result
* The result of calling the method on the real object
*/
public function afterCall($proxied, $method, $args);
public function afterCall($proxied, $method, $args, $result);
}

View File

@ -13,14 +13,35 @@ class AopProxyService {
public $afterCall = array();
public $proxied;
/**
* Because we don't know exactly how the proxied class is usually called,
* provide a default constructor
*/
public function __construct() {
}
public function __call($method, $args) {
if (method_exists($this->proxied, $method)) {
$continue = true;
$result = null;
if (isset($this->beforeCall[$method])) {
$result = $this->beforeCall[$method]->beforeCall($this->proxied, $method, $args);
if ($result === false) {
$continue = false;
$methods = $this->beforeCall[$method];
if (!is_array($methods)) {
$methods = array($methods);
}
foreach ($methods as $handler) {
$alternateReturn = null;
$proceed = $handler->beforeCall($this->proxied, $method, $args, $alternateReturn);
if ($proceed === false) {
$continue = false;
// if something is set in, use it
if ($alternateReturn) {
$result = $alternateReturn;
}
}
}
}
@ -28,11 +49,20 @@ class AopProxyService {
$result = call_user_func_array(array($this->proxied, $method), $args);
if (isset($this->afterCall[$method])) {
$this->afterCall[$method]->afterCall($this->proxied, $method, $args, $result);
$methods = $this->afterCall[$method];
if (!is_array($methods)) {
$methods = array($methods);
}
foreach ($methods as $handler) {
$return = $handler->afterCall($this->proxied, $method, $args, $result);
if (!is_null($return)) {
$result = $return;
}
}
}
return $result;
}
return $result;
}
}
}

View File

@ -20,6 +20,9 @@ interface BeforeCallAspect {
* The name of the method being called
* @param string $args
* The arguments that were passed to the method call
* @param mixed $alternateReturn
* An alternative return value that should be passed
* to the caller. Only has effect of beforeCall returns false
*/
public function beforeCall($proxied, $method, $args);
public function beforeCall($proxied, $method, $args, &$alternateReturn);
}

View File

@ -44,7 +44,7 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name { position: relative; z-index: 1; margin: 3px 0 3px 50px; width: 50%; color: #5e5e5e; background: #eeeded; background: rgba(255, 255, 255, 0.8); -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; line-height: 24px; height: 22px; padding: 0 5px; text-align: left; cursor: pointer; display: table; table-layout: fixed; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .name { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); display: inline; float: left; max-width: 50%; font-weight: normal; padding: 0 5px 0 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status { position: relative; float: right; padding: 0 0 0 5px; max-width: 30%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { max-width: 70%; position: absolute; right: 5px; text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.6); color: #cc0000; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.6); color: #cc0000; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-warning-text { color: #b7a403; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-success-text { color: #1f9433; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-actions { position: absolute; top: 0; right: 0; left: 0; z-index: 0; color: #f00; font-size: 14px; }

View File

@ -11,7 +11,7 @@
Used in side panels and action tabs
*/
.ss-uploadfield .clear { clear: both; }
.ss-uploadfield .middleColumn { width: 510px; padding: 0; background: #fff; border: 1px solid #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -o-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); }
.ss-uploadfield .middleColumn { min-width: 510px; max-width: 600px; width: 100%; margin-left: 0; clear: both; padding: 0; background: #fff; border: 1px solid #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -o-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); }
.ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -webkit-box-shadow: gray 0 0 4px 0 inset; -moz-box-shadow: gray 0 0 4px 0 inset; box-shadow: gray 0 0 4px 0 inset; border: 2px dashed gray; background: #d0d3d5; display: none; margin-right: 15px; }

View File

@ -3,22 +3,22 @@
/**
* SilverStripe-specific testing object designed to support functional testing of your web app. It simulates get/post
* requests, form submission, and can validate resulting HTML, looking up content by CSS selector.
*
*
* The example below shows how it works.
*
*
* <code>
* public function testMyForm() {
* // Visit a URL
* $this->get("your/url");
*
*
* // Submit a form on the page that you get in response
* $this->submitForm("MyForm_ID", array("Email" => "invalid email ^&*&^"));
*
* // Validate the content that is returned
* $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));
* }
* }
* </code>
*
*
* @package framework
* @subpackage testing
*/
@ -27,37 +27,48 @@ class FunctionalTest extends SapphireTest {
* Set this to true on your sub-class to disable the use of themes in this test.
* This can be handy for functional testing of modules without having to worry about whether a user has changed
* behaviour by replacing the theme.
*
* @var bool
*/
protected static $disable_themes = false;
/**
* Set this to true on your sub-class to use the draft site by default for every test in this class.
*
* @var bool
*/
protected static $use_draft_site = false;
/**
* @var TestSession
*/
protected $mainSession = null;
/**
* CSSContentParser for the most recently requested page.
*
*
* @var CSSContentParser
*/
protected $cssParser = null;
/**
* If this is true, then 30x Location headers will be automatically followed.
* If not, then you will have to manaully call $this->mainSession->followRedirection() to follow them.
* However, this will let you inspect the intermediary headers
*
* @var bool
*/
protected $autoFollowRedirection = true;
/**
* @var String
* @var string
*/
protected $originalTheme = null;
/**
* Returns the {@link Session} object for this test
*
* @return Session
*/
public function session() {
return $this->mainSession->session();
@ -66,7 +77,7 @@ class FunctionalTest extends SapphireTest {
public function setUp() {
// Skip calling FunctionalTest directly.
if(get_class($this) == "FunctionalTest") $this->skipTest = true;
parent::setUp();
$this->mainSession = new TestSession();
@ -75,22 +86,22 @@ class FunctionalTest extends SapphireTest {
$this->originalTheme = Config::inst()->get('SSViewer', 'theme');
Config::inst()->update('SSViewer', 'theme', null);
}
// Switch to draft site, if necessary
if(static::get_use_draft_site()) {
$this->useDraftSite();
}
// Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case
// basis.
BasicAuth::protect_entire_site(false);
SecurityToken::disable();
}
public function tearDown() {
SecurityToken::enable();
parent::tearDown();
unset($this->mainSession);
@ -102,6 +113,12 @@ class FunctionalTest extends SapphireTest {
/**
* Submit a get request
* @uses Director::test()
*
* @param string $url
* @param Session $session
* @param array $headers
* @param array $cookies
* @return SS_HTTPResponse
*/
public function get($url, $session = null, $headers = null, $cookies = null) {
$this->cssParser = null;
@ -114,7 +131,15 @@ class FunctionalTest extends SapphireTest {
/**
* Submit a post request
*
* @uses Director::test()
* @param string $url
* @param array $data
* @param array $headers
* @param Session $session
* @param string $body
* @param array $cookies
* @return SS_HTTPResponse
*/
public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null) {
$this->cssParser = null;
@ -124,24 +149,24 @@ class FunctionalTest extends SapphireTest {
}
return $response;
}
/**
* Submit the form with the given HTML ID, filling it out with the given data.
* Acts on the most recent response.
*
*
* Any data parameters have to be present in the form, with exact form field name
* and values, otherwise they are removed from the submission.
*
*
* Caution: Parameter names have to be formatted
* as they are in the form submission, not as they are interpreted by PHP.
* Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
* Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
*
*
* @see http://www.simpletest.org/en/form_testing_documentation.html
*
* @param String $formID HTML 'id' attribute of a form (loaded through a previous response)
* @param String $button HTML 'name' attribute of the button (NOT the 'id' attribute)
* @param Array $data Map of GET/POST data.
*
* @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
* @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
* @param array $data Map of GET/POST data.
* @return SS_HTTPResponse
*/
public function submitForm($formID, $button = null, $data = array()) {
@ -152,9 +177,11 @@ class FunctionalTest extends SapphireTest {
}
return $response;
}
/**
* Return the most recent content
*
* @return string
*/
public function content() {
return $this->mainSession->lastContent();
@ -162,7 +189,7 @@ class FunctionalTest extends SapphireTest {
/**
* Find an attribute in a SimpleXMLElement object by name.
* @param SimpleXMLElement object
* @param SimpleXMLElement $object
* @param string $attribute Name of attribute to find
* @return SimpleXMLElement object of the attribute
*/
@ -175,24 +202,24 @@ class FunctionalTest extends SapphireTest {
}
return $found;
}
/**
* Return a CSSContentParser for the most recent content.
*
*
* @return CSSContentParser
*/
public function cssParser() {
if(!$this->cssParser) $this->cssParser = new CSSContentParser($this->mainSession->lastContent());
return $this->cssParser;
}
/**
* Assert that the most recently queried page contains a number of content tags specified by a CSS selector.
* The given CSS selector will be applied to the HTML of the most recent page. The content of every matching tag
* will be examined. The assertion fails if one of the expectedMatches fails to appear.
*
* Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
*
*
* @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
* @param array|string $expectedMatches The content of at least one of the matched tags
* @throws PHPUnit_Framework_AssertionFailedError
@ -200,22 +227,22 @@ class FunctionalTest extends SapphireTest {
*/
public function assertPartialMatchBySelector($selector, $expectedMatches) {
if(is_string($expectedMatches)) $expectedMatches = array($expectedMatches);
$items = $this->cssParser()->getBySelector($selector);
$actuals = array();
if($items) foreach($items as $item) $actuals[trim(preg_replace("/[ \n\r\t]+/", " ", $item. ''))] = true;
foreach($expectedMatches as $match) {
$this->assertTrue(
isset($actuals[$match]),
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
);
return false;
}
return true;
}
@ -225,7 +252,7 @@ class FunctionalTest extends SapphireTest {
* will be examined. The assertion fails if one of the expectedMatches fails to appear.
*
* Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
*
*
* @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
* @param array|string $expectedMatches The content of *all* matching tags as an array
* @throws PHPUnit_Framework_AssertionFailedError
@ -233,19 +260,19 @@ class FunctionalTest extends SapphireTest {
*/
public function assertExactMatchBySelector($selector, $expectedMatches) {
if(is_string($expectedMatches)) $expectedMatches = array($expectedMatches);
$items = $this->cssParser()->getBySelector($selector);
$actuals = array();
if($items) foreach($items as $item) $actuals[] = trim(preg_replace("/[ \n\r\t]+/", " ", $item. ''));
$this->assertTrue(
$expectedMatches == $actuals,
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'"
);
return true;
}
@ -263,21 +290,21 @@ class FunctionalTest extends SapphireTest {
*/
public function assertPartialHTMLMatchBySelector($selector, $expectedMatches) {
if(is_string($expectedMatches)) $expectedMatches = array($expectedMatches);
$items = $this->cssParser()->getBySelector($selector);
$actuals = array();
if($items) foreach($items as $item) $actuals[$item->asXML()] = true;
foreach($expectedMatches as $match) {
$this->assertTrue(
isset($actuals[$match]),
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
);
}
return true;
}
@ -287,7 +314,7 @@ class FunctionalTest extends SapphireTest {
* will be examined. The assertion fails if one of the expectedMatches fails to appear.
*
* Note: &nbsp; characters are stripped from the content; make sure that your assertions take this into account.
*
*
* @param string $selector A basic CSS selector, e.g. 'li.jobs h3'
* @param array|string $expectedMatches The content of *all* matched tags as an array
* @throws PHPUnit_Framework_AssertionFailedError
@ -298,15 +325,15 @@ class FunctionalTest extends SapphireTest {
$actuals = array();
if($items) foreach($items as $item) $actuals[] = $item->asXML();
$this->assertTrue(
$expectedMatches == $actuals,
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'"
);
}
/**
* Log in as the given member
* @param $member The ID, fixture codename, or Member object of the member that you want to log in
@ -315,10 +342,10 @@ class FunctionalTest extends SapphireTest {
if(is_object($member)) $memberID = $member->ID;
elseif(is_numeric($member)) $memberID = $member;
else $memberID = $this->idFromFixture('Member', $member);
$this->session()->inst_set('loggedInAs', $memberID);
}
/**
* Use the draft (stage) site for testing.
* This is helpful if you're not testing publication functionality and don't want "stage management" cluttering
@ -333,26 +360,29 @@ class FunctionalTest extends SapphireTest {
}
else {
$this->session()->inst_set('readingMode', 'Stage.Live');
$this->session()->inst_set('unsecuredDraftSite', false);
$this->session()->inst_set('unsecuredDraftSite', false);
}
}
/**
* Return a static variable from this class.
*
* @param string $varName
* @return mixed
*/
public function stat($varName) {
return static::$varName;
}
/**
* @return Boolean
* @return bool
*/
public static function get_disable_themes() {
return static::$disable_themes;
}
/**
* @return Boolean
* @return bool
*/
public static function get_use_draft_site() {
return static::$use_draft_site;

View File

@ -513,10 +513,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$haystack,
$message = '',
$ignoreCase = FALSE,
$checkForObjectIdentity = TRUE
$checkForObjectIdentity = TRUE,
$checkForNonObjectIdentity = false
) {
if ($haystack instanceof DBField) $haystack = (string)$haystack;
parent::assertContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity);
parent::assertContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity, $checkForNonObjectIdentity);
}
public static function assertNotContains(
@ -524,10 +525,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$haystack,
$message = '',
$ignoreCase = FALSE,
$checkForObjectIdentity = TRUE
$checkForObjectIdentity = TRUE,
$checkForNonObjectIdentity = false
) {
if ($haystack instanceof DBField) $haystack = (string)$haystack;
parent::assertNotContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity);
parent::assertNotContains($needle, $haystack, $message, $ignoreCase, $checkForObjectIdentity, $checkForNonObjectIdentity);
}
/**

View File

@ -2,23 +2,35 @@
/**
* Represents a test usage session of a web-app
* It will maintain session-state from request to request
*
*
* @package framework
* @subpackage testing
*/
class TestSession {
private $session;
private $lastResponse;
/**
* @param Controller $controller Necessary to use the mock session
* @var Session
*/
private $session;
/**
* @var SS_HTTPResponse
*/
private $lastResponse;
/**
* Necessary to use the mock session
* created in {@link session} in the normal controller stack,
* e.g. to overwrite Member::currentUser() with custom login data.
*
* @var Controller
*/
protected $controller;
/**
* @var string $lastUrl Fake HTTP Referer Tracking, set in {@link get()} and {@link post()}.
* Fake HTTP Referer Tracking, set in {@link get()} and {@link post()}.
*
* @var string
*/
private $lastUrl;
@ -28,7 +40,7 @@ class TestSession {
$this->controller->setSession($this->session);
$this->controller->pushCurrent();
}
public function __destruct() {
// Shift off anything else that's on the stack. This can happen if something throws
// an exception that causes a premature TestSession::__destruct() call
@ -36,15 +48,21 @@ class TestSession {
if(Controller::has_curr()) $this->controller->popCurrent();
}
/**
* Submit a get request
*
* @uses Director::test()
* @param string $url
* @param Session $session
* @param array $headers
* @param array $cookies
* @return SS_HTTPResponse
*/
public function get($url, $session = null, $headers = null, $cookies = null) {
$headers = (array) $headers;
if($this->lastUrl && !isset($headers['Referer'])) $headers['Referer'] = $this->lastUrl;
$this->lastResponse
$this->lastResponse
= Director::test($url, null, $session ? $session : $this->session, null, null, $headers, $cookies);
$this->lastUrl = $url;
if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING);
@ -53,7 +71,15 @@ class TestSession {
/**
* Submit a post request
*
* @uses Director::test()
* @param string $url
* @param array $data
* @param array $headers
* @param Session $session
* @param string $body
* @param array $cookies
* @return SS_HTTPResponse
*/
public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null) {
$headers = (array) $headers;
@ -64,24 +90,24 @@ class TestSession {
if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING);
return $this->lastResponse;
}
/**
* Submit the form with the given HTML ID, filling it out with the given data.
* Acts on the most recent response.
*
*
* Any data parameters have to be present in the form, with exact form field name
* and values, otherwise they are removed from the submission.
*
*
* Caution: Parameter names have to be formatted
* as they are in the form submission, not as they are interpreted by PHP.
* Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
* Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
*
*
* @see http://www.simpletest.org/en/form_testing_documentation.html
*
* @param String $formID HTML 'id' attribute of a form (loaded through a previous response)
* @param String $button HTML 'name' attribute of the button (NOT the 'id' attribute)
* @param Array $data Map of GET/POST data.
*
* @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
* @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
* @param array $data Map of GET/POST data.
* @return SS_HTTPResponse
*/
public function submitForm($formID, $button = null, $data = array()) {
@ -108,15 +134,17 @@ class TestSession {
$postVars = array();
parse_str($submission->_encode(), $postVars);
return $this->post($url, $postVars);
} else {
user_error("TestSession::submitForm called when there is no form loaded."
. " Visit the page with the form first", E_USER_WARNING);
}
}
/**
* If the last request was a 3xx response, then follow the redirection
*
* @return SS_HTTPResponse The response given, or null if no redirect occurred
*/
public function followRedirection() {
if($this->lastResponse->getHeader('Location')) {
@ -125,25 +153,29 @@ class TestSession {
return $this->get($url);
}
}
/**
* Returns true if the last response was a 3xx redirection
*
* @return bool
*/
public function wasRedirected() {
$code = $this->lastResponse->getStatusCode();
return $code >= 300 && $code < 400;
}
/**
* Get the most recent response, as an SS_HTTPResponse object
* Get the most recent response
*
* @return SS_HTTPResponse
*/
public function lastResponse() {
return $this->lastResponse;
}
/**
* Return the fake HTTP_REFERER; set each time get() or post() is called.
*
*
* @return string
*/
public function lastUrl() {
@ -152,19 +184,27 @@ class TestSession {
/**
* Get the most recent response's content
*
* @return string
*/
public function lastContent() {
if(is_string($this->lastResponse)) return $this->lastResponse;
else return $this->lastResponse->getBody();
}
/**
* Return a CSSContentParser containing the most recent response
*
* @return CSSContentParser
*/
public function cssParser() {
return new CSSContentParser($this->lastContent());
}
/**
* Get the last response as a SimplePage object
*
* @return SimplePage The response if available
*/
public function lastPage() {
require_once("thirdparty/simpletest/http.php");
@ -176,13 +216,15 @@ class TestSession {
$page = &$builder->parse(new TestSession_STResponseWrapper($this->lastResponse));
$builder->free();
unset($builder);
return $page;
}
}
/**
* Get the current session, as a Session object
*
* @return Session
*/
public function session() {
return $this->session;
@ -191,41 +233,66 @@ class TestSession {
/**
* Wrapper around SS_HTTPResponse to make it look like a SimpleHTTPResposne
*
*
* @package framework
* @subpackage testing
*/
class TestSession_STResponseWrapper {
/**
* @var SS_HTTPResponse
*/
private $response;
public function __construct(SS_HTTPResponse $response) {
$this->response = $response;
}
/**
* @return string
*/
public function getContent() {
return $this->response->getBody();
}
/**
* @return string
*/
public function getError() {
return "";
}
/**
* @return null
*/
public function getSent() {
return null;
}
/**
* @return string
*/
public function getHeaders() {
return "";
}
/**
* @return string 'GET'
*/
public function getMethod() {
return "GET";
}
/**
* @return string
*/
public function getUrl() {
return "";
}
/**
* @return null
*/
public function getRequestData() {
return null;
}

View File

@ -81,6 +81,8 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins
## Alpha/beta/release candidate ##
* [3.1.6-rc2](rc/3.1.6-rc2) - 12 August 2014
* [3.1.6-rc1](rc/3.1.6-rc1) - 5 August 2014
* [3.1.5-rc1](rc/3.1.5-rc1) - 7 May 2014
* [3.1.4-rc1](rc/3.1.4-rc1) - 1 April 2014

View File

@ -0,0 +1,59 @@
# 3.1.6-rc1
## Upgrading
* The use of the isDev or isTest query string parameter is now restricted to those logged in as admin,
and requires users to login via the front end form rather than using basic authentication. This
follows the same process as the use of the 'flush' query string parameter, and will redirect
requests containing this argument with a token in the querystring.
Director::isDev, Director::isTest, and Director::isLive no longer have any parameters.
### API Changes
* 2014-07-28 [0e78e3f](https://github.com/silverstripe/sapphire/commit/0e78e3f) Let update interval of tinymce be changed or disabled (Damian Mooyman)
* 2014-07-21 [4453caf](https://github.com/silverstripe/sapphire/commit/4453caf) Let extensions control folder selector in HtmlEditorField_Toolbar (Damian Mooyman)
* 2014-07-05 [3c5e51a](https://github.com/silverstripe/sapphire/commit/3c5e51a) Debug::dump in CLI no longer generates HTML. Uses colours. API Column size is configurable in DebugView (Damian Mooyman)
* 2014-05-22 [ec325a3](https://github.com/silverstripe/sapphire/commit/ec325a3) Fix HTTPS proxy header detection (Ingo Schommer)
### Features and Enhancements
* 2014-07-28 [482c23f](https://github.com/silverstripe/silverstripe-cms/commit/482c23f) Adding CMS sitetree filter to see the current 'live' site (Stig Lindqvist)
* 2014-06-28 [1d86fe4](https://github.com/silverstripe/sapphire/commit/1d86fe4) allow force resampling on images (Stevie Mayhew)
* 2014-07-28 [ac95a87](https://github.com/silverstripe/sapphire/commit/ac95a87) Allow configuring Image backend via yaml. (Jeremy Shipman)
### Bugfixes
* 2014-08-04 [b2dac64](https://github.com/silverstripe/sapphire/commit/b2dac64) Fixed escaping of name/value in options of form fields (Sean Harvey)
* 2014-08-01 [9281089](https://github.com/silverstripe/sapphire/commit/9281089) Return the promise instead of the whole deferred object. (Mateusz Uzdowski)
* 2014-07-31 [d8302a0](https://github.com/silverstripe/sapphire/commit/d8302a0) Add a synthetic event to workaround IE8 issues. (Mateusz Uzdowski)
* 2014-07-30 [31c9fb5](https://github.com/silverstripe/sapphire/commit/31c9fb5) Fix the anchor selector to work for internal pages. (Mateusz Uzdowski)
* 2014-07-29 [baa2b69](https://github.com/silverstripe/sapphire/commit/baa2b69) Fixing incorrect error message on failed UploadField upload (Sean Harvey)
* 2014-07-29 [329dffd](https://github.com/silverstripe/sapphire/commit/329dffd) AssetUploadField hides "generic" file upload messages. (Sean Harvey)
* 2014-07-28 [62ed2d0](https://github.com/silverstripe/sapphire/commit/62ed2d0) Fix periodic tinymce layout refresh (Damian Mooyman)
* 2014-07-24 [3eefd65](https://github.com/silverstripe/silverstripe-cms/commit/3eefd65) Narrowing site tree search to one date shows no pages (Stig Lindqvist)
* 2014-07-20 [333a2aa](https://github.com/silverstripe/sapphire/commit/333a2aa) CMS tree filters doesn't count the correct number of children for deleted pages (Stig Lindqvist)
* 2014-07-17 [ac64d25](https://github.com/silverstripe/sapphire/commit/ac64d25) If user is logged out getHtmlEditorConfigForCMS() gets called on non object (Stig Lindqvist)
* 2014-07-17 [df6a8b6](https://github.com/silverstripe/sapphire/commit/df6a8b6) #3282: Added ability to subselect with in left or inner join (Senorgeno)
* 2014-07-14 [b34aaca](https://github.com/silverstripe/sapphire/commit/b34aaca) Fix several issues around onmatch/onunmatch entwines. (Mateusz Uzdowski)
* 2014-07-03 [c329f07](https://github.com/silverstripe/sapphire/commit/c329f07) Fix incorrect common_languages config (Damian Mooyman)
* 2014-07-01 [d3c7e41](https://github.com/silverstripe/sapphire/commit/d3c7e41) using isDev or isTest query string no longer triggers basic auth (Damian Mooyman)
* 2014-07-01 [a777266](https://github.com/silverstripe/silverstripe-cms/commit/a777266) ensure controller stack is updated when execution halted by an exception. (Will Rossiter)
* 2014-06-28 [2c741fe](https://github.com/silverstripe/sapphire/commit/2c741fe) Add support for compositedbfield within many_many_extraFields (Will Rossiter)
* 2014-06-16 [3d71a22](https://github.com/silverstripe/sapphire/commit/3d71a22) ClassManifest errors if files contain duplicate class names (fixes #3210) (Loz Calver)
* 2014-06-12 [d516063](https://github.com/silverstripe/sapphire/commit/d516063) fix dependency injection stumbling over ViewableData's __isset (Damian Mooyman)
* 2014-06-11 [18b6870](https://github.com/silverstripe/sapphire/commit/18b6870) Sanitise the PHP output. (Mateusz Uzdowski)
* 2014-06-10 [1e19485](https://github.com/silverstripe/silverstripe-cms/commit/1e19485) Ensure that all child pages are deleted (regardless of ShowInMenu status) under enforce_strict_hierarchy. (Rodney Way)
* 2014-05-30 [b8d19ba](https://github.com/silverstripe/silverstripe-cms/commit/b8d19ba) Fix deleted pages redirecting the CMS Update behat tests for Mink 1.6 compatibility (Damian Mooyman)
* 2014-05-22 [f9e7d47](https://github.com/silverstripe/sapphire/commit/f9e7d47) fix listview not working with IE9 (Igor)
* 2014-05-20 [4a34c36](https://github.com/silverstripe/sapphire/commit/4a34c36) Fix access to protected Session::current_session() Fixes #3144 (Damian Mooyman)
* 2014-05-15 [c24a2c2](https://github.com/silverstripe/sapphire/commit/c24a2c2) ArrayList failing to respect the SS_Sortable interface ref: CWPBUG-133 (Damian Mooyman)
* 2014-05-15 [ee6e496](https://github.com/silverstripe/sapphire/commit/ee6e496) Fix grid field showing search without search component added ref: CWPBUG-133 (Damian Mooyman)
* 2014-05-12 [51c3346](https://github.com/silverstripe/sapphire/commit/51c3346) Fix deprecated use of statics in test cases (Damian Mooyman)
* 2014-05-10 [d012b79](https://github.com/silverstripe/sapphire/commit/d012b79) Prevent i18n clearing all SS_Caches (fixes #3122) (Loz Calver)
* 2014-05-02 [807755f](https://github.com/silverstripe/sapphire/commit/807755f) TemplateManifest prevent cache collision (Will Morgan)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.6-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.6-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.6-rc1)

View File

@ -0,0 +1,20 @@
# 3.1.6-rc2
# Overview
Minor documentation and outstanding bugfixes on top of the prior 3.1.6-rc1
## Bugfixes
* 2014-06-01 [e535c35](https://github.com/silverstripe/sapphire/commit/e535c35) New JS sprintf and inject replacement functions (colymba)
* 2014-08-11 [98907fb](https://github.com/silverstripe/sapphire/commit/98907fb) Fix incorrect parsing of HTML content (Damian Mooyman)
* 2014-08-08 [a369094](https://github.com/silverstripe/sapphire/commit/a369094) Fix issue with generating tree data for missing pages (Damian Mooyman)
* 2014-08-06 [53dbbb7](https://github.com/silverstripe/silverstripe-cms/commit/53dbbb7) Fix CMSMain::getList to correctly respect filter result Fixes #1064 CMSSiteTreeFilter refactored to allow SS_List of filtered pages to be returned (Damian Mooyman)
* 2014-08-06 [1c48cb6](https://github.com/silverstripe/silverstripe-cms/commit/1c48cb6) Fix search range for asset filter (Damian Mooyman)
* 2014-06-27 [f19b1ee](https://github.com/silverstripe/sapphire/commit/f19b1ee) declarations matching PHPUnit_Framework_Assert (Michael Parkhill)
## Downloads
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.6-rc2)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.6-rc2)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.6-rc2)

View File

@ -7,11 +7,11 @@ SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
for a more detailed introduction to creating new `ModelAdmin` interfaces, read
the [ModelAdmin referencee](../reference/modeladmin).
the [ModelAdmin reference](../reference/modeladmin).
In this document we'll take the `ProductAdmin` class used in the
[ModelAdmin referencee](../reference/modeladmin#setup) and so how we can change
the menu behaviour by using the static `$menu_title` and `$menu_icon` statics to
[ModelAdmin reference](../reference/modeladmin#setup) and so how we can change
the menu behaviour by using the `$menu_title` and `$menu_icon` statics to
provide a custom title and icon.
### Defining a Custom Icon
@ -48,7 +48,7 @@ For more information on language and translations, please refer to the
## Adding an external link to the menu
On top of your administration windows, the menu can also have external links
(e.g to external reference). In this example, we're going to add a link to
(e.g. to external reference). In this example, we're going to add a link to
Google to the menu.
First, we need to define a `[api:LeftAndMainExtension]` which will contain our
@ -85,7 +85,7 @@ button configuration.
To have the link appear, make sure you add the extension to the `LeftAndMain`
class. For more information about configuring extensions see the
[DataExtension referencee](../reference/dataextension).
[DataExtension reference](../reference/dataextension).
:::php
LeftAndMain::add_extension('CustomLeftAndMain')
@ -93,4 +93,4 @@ class. For more information about configuring extensions see the
## Related
* [How to extend the CMS interface](extend-cms-interface)
* [How to extend the CMS interface](extend-cms-interface)

View File

@ -67,8 +67,8 @@ __Example: using a subclass__
// return either true or false
}
public function getStatusFlags(){
$flags = parent::getStatusFlags();
public function getStatusFlags($cached = true) {
$flags = parent::getStatusFlags($cached);
$flags['scheduledtopublish'] = "Scheduled To Publish";
return $flags;
}
@ -78,4 +78,4 @@ The above subclass of `[api:SiteTree]` will add a new flag for indicating its
__'Scheduled To Publish'__ status. The look of the page node will be changed
from ![Normal Page Node](../_images/page_node_normal.png) to ![Scheduled Page Node](../_images/page_node_scheduled.png). The getStatusFlags has an `updateStatusFlags()`
extension point, so the flags can be modified through `DataExtension` rather than
inheritance as well. Deleting existing flags works by simply unsetting the array key.
inheritance as well. Deleting existing flags works by simply unsetting the array key.

View File

@ -39,6 +39,9 @@ If you already have composer installed you can update it by running:
Composer updates regularly, so you should run this command fairly often. These instructions assume you are running the latest version.
## Installing Composer on Windows WAMP
For those that use WAMP as a development environment, [detailed information is available on installing using Composer.](/windows-wamp#install-wamp)
## Create a new site
Composer can create a new site for you, using the installer as a template (by default composer will download the latest stable version):

View File

@ -1,6 +1,6 @@
# Windows with WAMPServer
# Windows with WAMPServer 2.5+
An easy and reliable approach to getting SilverStripe running on Windows is to use Apache, which can be convieniently
An easy and reliable approach to getting SilverStripe running on Windows is to use Apache, which can be conveniently
done through [WampServer](http://www.wampserver.com/en/). This can be useful if you are deploying on Linux Apache and
want a Microsoft Windows machine with a very similar environment.
@ -9,41 +9,56 @@ Note: Installing on Microsoft's IIS webserver through Microsoft WebPI is likely
## Install WAMP
1. Download WampServer from http://www.wampserver.com/en/download.php
2. Run the installer. By default, it will install to C:\wamp. You can choose your own install path if you wish; the
directories mentioned below will also be different.
3. Once WampServer has been installed and launched, you will see a little half circle gauge in the task bar, next to
the clock. If everything is working, then it will be white. If it's yellow or red, then something is wrong. If you
can't see the gauge, then WampServer hasn't been started and you should start WampServer from the start menu.
4. If something's wrong, this usually means that you have another service on port 80 or port 3306. Here are some
common sources of problems to check. After correcting these issues, left-click the gauge and choose 'restart all
services'. It might a short while to restart, but the gauge should go to white.
* You might have IIS running. Check Start -> Control Panel -> Administrative Tools -> Internet Information
Services. Ensure that any web site services are stopped.
* If you run Skype, visit Select "Tools" -> "Options" in Skype's menu. Find an option "Use port 80 and 443 as
alternatives for incoming connection". Make sure that it is de-selected.
5. Left-click the gauge, then select Apache -> Apache Modules -> Rewrite Module. The gauge will flick to yellow, and
then return to white.
6. Left-click the gauge, then select MySQL -> my.ini. At the very bottom of the file, and add the following to a new
line (without the quotes): "lower_case_table_names = 2". Save the file, close Notepad and left-click the gauge, and
selected 'Restart all services'. This is used to ease the transition between a Windows-based install and a Linux-based
install where case-sensitivity is important.
1. Go to the [WampServer download page](http://www.wampserver.com/en/#download-wrapper).
2. You will first need to download and install the suggested [Visual C++ Redistributable for Visual Studio 2012 Update 4](http://www.microsoft.com/en-us/download/details.aspx?id=30679#) BEFORE installing WampServer.
3. Next, download the WampServer installer and the run the installer. By default, it will install to C:\wamp. You can choose your own install path if you wish; however note we will refer to the c:/wamp in the test of this tutorial.
4. Once WampServer has been installed and launched, you will see a small "W" in the task bar, next to
the clock. If everything is working, then it will be green. If it's orange or red, then something is likely misconfigured. See the Troubleshooting section below. If you can't see the "W", then WampServer hasn't been started and you should start WampServer from the start menu.
5. Left-click the "W", then select Apache -> Apache Modules -> Rewrite Module. The "W" will flick to orange, and
then return to green.
6. Left-click the "W", then select MySQL -> my.ini. At the very bottom of the file, and add the following to a new line without the quotes): "lower_case_table_names = 2". Save the file, close Notepad and left-click the "W", and
select 'Restart all services'. This is used to ease the transition between a Windows-based install and a Linux-based
install where database case-sensitivity is important.
## Install SilverStripe
### Composer
Composer is becoming our preferred way to manager installation and future dependancy management of SilverStripe modules. Getting started with Composer requires:
1. PHP installed on your local environment (which in this context is part of WAMP).
2. The Composer application itself (there is a Windows installer which will ask you to point it to PHP, in this case it should be at C:/wamp/bin/php/phpX.X.X/php.exe).
See the [Composer documentation](https://getcomposer.org/doc/00-intro.md#installation-windows) to get the installer.
3. A command line such as windows command prompt or [gitbash](http://git-scm.com/download/win) (recommended and comes as part of git for windows).
* [Download](http://silverstripe.org/download) the latest SilverStripe installer package
* Unpack the archive into `C:\wamp\www`
* Rename the unpacked directory from `C:\wamp\www\silverstripe-vX.X.X` to `C:\wamp\www\silverstripe`
* Visit `http://localhost/silverstripe` - you will see SilverStripe's installation screen.
* You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two.
* Once the installer has finished, visit `http://localhost/silverstripe`. You should see your new SilverStripe site's
Once you have installed the above, open a command line and use the following command to get a fresh copy of SilverStripe stable code installed into a 'silverstripe' sub-folder (note here we are using gitbash paths).
```bash
$ cd /c/wamp/www
$ composer create-project silverstripe/installer ./silverstripe
```
### Zip download
* [Download](http://silverstripe.org/stable-download) the latest SilverStripe CMS and Framework package
* Unpack the archive into `C:\wamp\www`
* Rename the unpacked directory from `C:\wamp\www\silverstripe-vX.X.X` to `C:\wamp\www\silverstripe`
### Install and configure
* Visit `http://localhost/silverstripe` - you will see SilverStripe's installation screen.
* You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two.
* Once the installer has finished, visit `http://localhost/silverstripe`. You should see your new SilverStripe site's
home page.
## Troubleshooting
1. If there is some misconfiguration, this often indicated you may have another service on port 80 or port 3306. Here are some common sources of problems to check.
Vista's security controls can cause issues. If you have installed on Vista but you're getting errors, there is a chance
that SilverStripe does not have sufficient permissions.
After correcting these issues, left-click the "W" and choose 'restart all services'. It might a short while to restart, but the "W" turn green.
* You might have IIS running. Check Start -> Control Panel -> Administrative Tools -> Internet Information
Services. Ensure that any web site services are stopped.
* If you run Skype, visit Select "Tools" -> "Options" in Skype's menu. Find an option "Use port 80 and 443 as
alternatives for incoming connection". Make sure that it is de-selected.
2. Vista's security controls can cause issues. If you have installed on Vista but you're getting errors, there is a chance that SilverStripe does not have sufficient permissions.
Right clicked on the installation folder and go to Permissions > Security > Users > Advanced and give the user full
control.
control.
3. If you find you are having issues with URL rewriting. Remove the index.php file that is bundled with SilverStripe. As we are using Apache web server's URL rewriting this file is not required (and in fact can result in problems when using apache 2.4+ as in the latest versions of WAMP). The other option is to enable the mod_access_compat module for apache which improves compatibility of newer versions of Apache with SilverStripe.

View File

@ -61,7 +61,7 @@ Report security issues to [security@silverstripe.com](mailto:security@silverstri
## Sharing your Opinion
* [silverstripe.org/forums](http://silverstripe.org/forums): Forums on silverstripe.org
* [silverstripe-dev](http://groups.google.com/group/silverstripe-dev): Core development mailinglist
* [silverstripe-documentation](http://groups.google.com/group/silverstripe-documentation): Documentation team mailing list
* [silverstripe.org/forums](http://silverstripe.org/forums): Forums on silverstripe.org
* [silverstripe-dev](http://groups.google.com/group/silverstripe-dev): Core development mailinglist
* [silverstripe-documentation](http://groups.google.com/group/silverstripe-documentation): Documentation team mailing list
* [silverstripe-translators](http://groups.google.com/group/silverstripe-translators): Translation team mailing list

View File

@ -0,0 +1,189 @@
# Aspects
## Introduction
Aspect oriented programming is the idea that some logic abstractions can be
applied across various type hierarchies "after the fact", altering the
behaviour of the system without altering the code structures that are already
in place.
> In computing, aspect-oriented programming (AOP) is a programming paradigm
> which isolates secondary or supporting functions from the main program's
> business logic. It aims to increase modularity by allowing the separation of
> cross-cutting concerns, forming a basis for aspect-oriented software
> development.
[The wiki article](http://en.wikipedia.org/wiki/Aspect-oriented_programming)
provides a much more in-depth explanation!
In the context of this dependency injector, AOP is achieved thanks to PHP's
__call magic method combined with the Proxy design pattern.
## In practice
* Assume an existing service declaration exists called MyService
* An AopProxyService class instance is created, and the existing MyService object is bound in as a member variable of the AopProxyService class
* Objects are added to the AopProxyService instance's "beforeCall" and "afterCall" lists; each of these implements either the beforeCall or afterCall method
* When client code declares a dependency on MyService, it is actually passed in the AopProxyService instance
* Client code calls a method `myMethod` that it knows exists on MyService - this doesn't exist on AopProxyService, so __call is triggered.
* All classes bound to the beforeCall list are executed; if any explicitly returns 'false', `myMethod` is not executed.
* Otherwise, myMethod is executed
* All classes bound to the afterCall list are executed
## A worked example
To provide some context, imagine a situation where we want to direct all 'write' queries made in the system to a specific
database server, whereas all read queries can be handled by slave servers. A simplified implementation might look
like the following - note that this doesn't cover all cases used by SilverStripe so is not a complete solution, more
just a guide to how it would be used.
```
<?php
/**
* Redirects write queries to a specific database configuration
*
* @author <marcus@silverstripe.com.au>
* @license BSD License http://www.silverstripe.org/bsd-license
*/
class MySQLWriteDbAspect implements BeforeCallAspect {
/**
*
* @var MySQLDatabase
*/
public $writeDb;
public $writeQueries = array('insert','update','delete','replace');
public function beforeCall($proxied, $method, $args, &$alternateReturn) {
if (isset($args[0])) {
$sql = $args[0];
$code = isset($args[1]) ? $args[1] : E_USER_ERROR;
if (in_array(strtolower(substr($sql,0,strpos($sql,' '))), $this->writeQueries)) {
$alternateReturn = $this->writeDb->query($sql, $code);
return false;
}
}
}
}
```
To actually make use of this class, a few different objects need to be configured. First up, define the `writeDb`
object that's made use of above
```
WriteMySQLDatabase:
class: MySQLDatabase
constructor:
- type: MySQLDatabase
server: write.hostname.db
username: user
password: pass
database: write_database
```
This means that whenever something asks the injector for the `WriteMySQLDatabase` object, it'll receive an object of
type `MySQLDatabase`, configured to point at the 'write' database
Next, this should be bound into an instance of the aspect class
```
MySQLWriteDbAspect:
properties:
writeDb: %$WriteMySQLDatabase
```
Next, we need to define the database connection that will be used for all non-write queries
```
ReadMySQLDatabase:
class: MySQLDatabase
constructor:
- type: MySQLDatabase
server: slavecluster.hostname.db
username: user
password: pass
database: read_database
```
The final piece that ties everything together is the AopProxyService instance that will be used as the replacement
object when the framework creates the database connection in DB.php
```
MySQLDatabase:
class: AopProxyService
properties:
proxied: %$ReadMySQLDatabase
beforeCall:
query:
- %$MySQLWriteDbAspect
```
The two important parts here are in the `properties` declared for the object
- **proxied** : This is the 'read' database connectino that all queries should be initially directed through
- **beforeCall** : A hash of method\_name => array containing objects that are to be evaluated _before_ a call to the defined method\_name
Overall configuration for this would look as follows
```
Injector:
ReadMySQLDatabase:
class: MySQLDatabase
constructor:
- type: MySQLDatabase
server: slavecluster.hostname.db
username: user
password: pass
database: read_database
MySQLWriteDbAspect:
properties:
writeDb: %$WriteMySQLDatabase
WriteMySQLDatabase:
class: MySQLDatabase
constructor:
- type: MySQLDatabase
server: write.hostname.db
username: user
password: pass
database: write_database
MySQLDatabase:
class: AopProxyService
properties:
proxied: %$ReadMySQLDatabase
beforeCall:
query:
- %$MySQLWriteDbAspect
```
## Changing what a method returns
One major feature of an aspect is the ability to modify what is returned from the client's call to the proxied method.
As seen in the above example, the `beforeCall` method modifies the byref `&$alternateReturn` variable, and returns
`false` after doing so.
```
$alternateReturn = $this->writeDb->query($sql, $code);
return false;
```
By returning false from the `beforeCall()` method, the wrapping proxy class will _not_ call any additional `beforeCall`
handlers defined for the called method. Assigning the $alternateReturn variable also indicates to return that value
to the caller of the method.
Similarly the `afterCall()` aspect can be used to manipulate the value to be returned to the calling code. All the
`afterCall()` method needs to do is return a non-null value, and that value will be returned to the original calling
code instead of the actual return value of the called method.

View File

@ -23,13 +23,13 @@ The resulting `[api:FieldList]` is the centrepiece of many data administration i
Many customizations of the SilverStripe CMS interface start here,
by adding, removing or configuring fields.
Example getCMSFields implementation
Here is an example getCMSFields implementation:
:::php
class MyDataObject extends DataObject {
$db = array(
'IsActive' => 'Boolean'
);
$db = array(
'IsActive' => 'Boolean'
);
public function getCMSFields() {
return new FieldList(
new CheckboxField('IsActive')
@ -58,13 +58,13 @@ You can then further customize those fields as required.
}
}
The `[ModelAdmin](/reference/modeladmin)` class uses this approach to provide
The [ModelAdmin](/reference/modeladmin) class uses this approach to provide
data management interfaces with very little custom coding.
You can also alter the fields of built-in and module `DataObject` classes through
your own `[DataExtension](/reference/dataextension)`, and a call to `[api:DataExtension->updateCMSFields()]`.
your own [DataExtension](/reference/dataextension), and a call to `DataExtension->updateCMSFields()`.
`[api::DataObject->beforeUpdateCMSFields()]` can also be used to interact with and add to automatically
scaffolded fields prior to being passed to extensions (See `[DataExtension](/reference/dataextension)`).
scaffolded fields prior to being passed to extensions (See [DataExtension](/reference/dataextension)).
### Searchable Fields
@ -120,7 +120,7 @@ assign an array:
}
To include relations (''$has_one'', `$has_many` and `$many_many`) in your search, you can use a dot-notation.
To include relations (`$has_one`, `$has_many` and `$many_many`) in your search, you can use a dot-notation.
:::php
class Team extends DataObject {
@ -247,7 +247,7 @@ Example: Check for CMS access permissions
**Important**: These checks are not enforced on low-level ORM operations
such as `write()` or `delete()`, but rather rely on being checked in the invoking code.
The CMS default sections as well as custom interfaces like
`[ModelAdmin](/reference/modeladmin)` or `[GridField](/reference/grid-field)`
[ModelAdmin](/reference/modeladmin) or [GridField](/reference/grid-field)
already enforce these permissions.
## Indexes

View File

@ -85,8 +85,8 @@ plus some `width` and `height` arguments. We'll add defaults to those in our sho
urlencode($address),
urlencode($address)
);
$width = (isset($args['width']) && $args['width']) ? $args['width'] : 400;
$height = (isset($args['height']) && $args['height']) ? $args['height'] : 300;
$width = (isset($arguments['width']) && $arguments['width']) ? $arguments['width'] : 400;
$height = (isset($arguments['height']) && $arguments['height']) ? $arguments['height'] : 300;
return sprintf(
'<iframe width="%d" height="%d" src="%s" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>',
$width,

View File

@ -140,7 +140,7 @@ to make it easier for CMS authors to identify pages of this type,
when navigating the tree or adding a new page:
:::php
class StaggPage extends Page {
class StaffPage extends Page {
private static $singular_name = 'Staff Directory';
private static $plural_name = 'Staff Directories';
private static $description = 'Two-column layout with a list of staff members';
@ -150,4 +150,4 @@ when navigating the tree or adding a new page:
You can also add custom "badges" to each page in the tree,
which denote status. Built-in examples are "Draft" and "Deleted" flags.
This is detailed in the ["Customize the CMS Tree" howto](/howto/customize-cms-tree).
This is detailed in the ["Customize the CMS Tree" howto](/howto/customize-cms-tree).

View File

@ -316,7 +316,7 @@ that does not exist in a Group.
You can limit the amount of records returned in a DataList by using the
`limit()` method.
:::php
// Returning the first 5 members, sorted alphabetically by Surname
$members = Member::get()->sort('Surname')->limit(5);

View File

@ -375,10 +375,16 @@ format which can be processed more easily by external translation providers (see
alert(ss.i18n._t('MYMODULE.MYENTITY'));
### Advanced Usage with sprintf()
### Advanced Use
The `ss.i18n` object contain a couple functions to help and replace dynamic variable from within a string.
#### Legacy sequential replacement with sprintf()
`sprintf()` will substitute occurencies of `%s` in the main string with each of the following arguments passed to the function. The substitution is done sequentially.
:::js
// MYMODULE.MYENTITY contains "Really delete %s articles by %s authors?"
// MYMODULE.MYENTITY contains "Really delete %s articles by %s?"
alert(ss.i18n.sprintf(
ss.i18n._t('MYMODULE.MYENTITY'),
42,
@ -387,6 +393,19 @@ format which can be processed more easily by external translation providers (see
// Displays: "Really delete 42 articles by Douglas Adams?"
#### Variable injection with inject()
`inject()` will substitute variables in the main string like `{myVar}` by the keys in the object passed as second argument. Each variable can be in any order and appear multiple times.
:::js
// MYMODULE.MYENTITY contains "Really delete {count} articles by {author}?"
alert(ss.i18n.inject(
ss.i18n._t('MYMODULE.MYENTITY'),
{count: 42, author: 'Douglas Adams'}
));
// Displays: "Really delete 42 articles by Douglas Adams?"
## Limitations
* No detecting/conversion of character encodings (we rely fully on UTF-8)

View File

@ -74,7 +74,7 @@ class Upload extends Controller {
public function __construct() {
parent::__construct();
$this->validator = new Upload_Validator();
$this->validator = Injector::inst()->create('Upload_Validator');
$this->replaceFile = self::config()->replaceFile;
}

View File

@ -42,7 +42,7 @@ class CheckboxSetField extends OptionsetField {
* @var array
*/
protected $defaultItems = array();
/**
* @todo Explain different source data that can be used with this field,
* e.g. SQLMap, ArrayList or an array.
@ -50,6 +50,21 @@ class CheckboxSetField extends OptionsetField {
public function Field($properties = array()) {
Requirements::css(FRAMEWORK_DIR . '/css/CheckboxSetField.css');
$properties = array_merge($properties, array(
'Options' => $this->getOptions()
));
return $this->customise($properties)->renderWith(
$this->getTemplates()
);
}
/**
* @return ArrayList
*/
public function getOptions() {
$odd = 0;
$source = $this->source;
$values = $this->value;
$items = array();
@ -99,47 +114,48 @@ class CheckboxSetField extends OptionsetField {
}
}
}
if(is_array($source)) {
unset($source['']);
}
$odd = 0;
$options = array();
if ($source == null) $source = array();
if($source) {
foreach($source as $value => $item) {
if($item instanceof DataObject) {
$value = $item->ID;
$title = $item->Title;
} else {
$title = $item;
}
$itemID = $this->ID() . '_' . preg_replace('/[^a-zA-Z0-9]/', '', $value);
$odd = ($odd + 1) % 2;
$extraClass = $odd ? 'odd' : 'even';
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $value);
$options[] = new ArrayData(array(
'ID' => $itemID,
'Class' => $extraClass,
'Name' => "{$this->name}[{$value}]",
'Value' => $value,
'Title' => $title,
'isChecked' => in_array($value, $items) || in_array($value, $this->defaultItems),
'isDisabled' => $this->disabled || in_array($value, $this->disabledItems)
));
}
if ($source == null) {
$source = array();
}
$properties = array_merge($properties, array('Options' => new ArrayList($options)));
foreach($source as $value => $item) {
if($item instanceof DataObject) {
$value = $item->ID;
$title = $item->Title;
} else {
$title = $item;
}
return $this->customise($properties)->renderWith($this->getTemplates());
$itemID = $this->ID() . '_' . preg_replace('/[^a-zA-Z0-9]/', '', $value);
$odd = ($odd + 1) % 2;
$extraClass = $odd ? 'odd' : 'even';
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $value);
$options[] = new ArrayData(array(
'ID' => $itemID,
'Class' => $extraClass,
'Name' => "{$this->name}[{$value}]",
'Value' => $value,
'Title' => $title,
'isChecked' => in_array($value, $items) || in_array($value, $this->defaultItems),
'isDisabled' => $this->disabled || in_array($value, $this->disabledItems)
));
}
$options = new ArrayList($options);
$this->extend('updateGetOptions', $options);
return $options;
}
/**
* Default selections, regardless of the {@link setValue()} settings.
* Note: Items marked as disabled through {@link setDisabledItems()} can still be

View File

@ -73,6 +73,7 @@ class HtmlEditorConfig {
'editor_selector' => "htmleditor",
'width' => "100%",
'auto_resize' => false,
'update_interval' => 5000, // Ensure update of this data every 5 seconds to the underlying textarea
'theme' => "advanced",
'theme_advanced_layout_manager' => "SimpleLayout",

View File

@ -190,7 +190,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
private static $allowed_actions = array(
'LinkForm',
'MediaForm',
'viewfile'
'viewfile',
'getanchors'
);
/**
@ -312,6 +313,17 @@ class HtmlEditorField_Toolbar extends RequestHandler {
return $form;
}
/**
* Get the folder ID to filter files by for the "from cms" tab
*
* @return int
*/
protected function getAttachParentID() {
$parentID = $this->controller->getRequest()->requestVar('ParentID');
$this->extend('updateAttachParentID', $parentID);
return $parentID;
}
/**
* Return a {@link Form} instance allowing a user to
* add images and flash objects to the TinyMCE content editor.
@ -321,7 +333,7 @@ class HtmlEditorField_Toolbar extends RequestHandler {
public function MediaForm() {
// TODO Handle through GridState within field - currently this state set too late to be useful here (during
// request handling)
$parentID = $this->controller->getRequest()->requestVar('ParentID');
$parentID = $this->getAttachParentID();
$fileFieldConfig = GridFieldConfig::create()->addComponents(
new GridFieldFilterHeader(),
@ -349,7 +361,9 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$fromCMS = new CompositeField(
new LiteralField('headerSelect',
'<h4>'.sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.FindInFolder', 'Find in Folder')).'</h4>'),
$select = TreeDropdownField::create('ParentID', "", 'Folder')->addExtraClass('noborder'),
$select = TreeDropdownField::create('ParentID', "", 'Folder')
->addExtraClass('noborder')
->setValue($parentID),
$fileField
);
@ -508,6 +522,41 @@ class HtmlEditorField_Toolbar extends RequestHandler {
))->renderWith($this->templateViewFile);
}
/**
* Find all anchors available on the given page.
*
* @return array
*/
public function getanchors() {
$id = (int)$this->request->getVar('PageID');
$anchors = array();
if (($page = Page::get()->byID($id)) && !empty($page)) {
if (!$page->canView()) {
throw new SS_HTTPResponse_Exception(
_t(
'HtmlEditorField.ANCHORSCANNOTACCESSPAGE',
'You are not permitted to access the content of the target page.'
),
403
);
}
// Similar to the regex found in HtmlEditorField.js / getAnchors method.
if (preg_match_all("/name=\"([^\"]+?)\"|name='([^']+?)'/im", $page->Content, $matches)) {
$anchors = $matches[1];
}
} else {
throw new SS_HTTPResponse_Exception(
_t('HtmlEditorField.ANCHORSPAGENOTFOUND', 'Target page not found.'),
404
);
}
return json_encode($anchors);
}
/**
* Similar to {@link File->getCMSFields()}, but only returns fields
* for manipulating the instance of the file as inserted into the HTML content,

View File

@ -340,7 +340,7 @@ class GridField extends FormField {
if($total > 0) {
$rows = array();
foreach($list as $idx => $record) {
if(!$record->canView()) {
if($record->hasMethod('canView') && !$record->canView()) {
continue;
}
$rowContent = '';

View File

@ -5,18 +5,18 @@ require_once 'i18nSSLegacyAdapter.php';
/**
* Base-class for storage and retrieval of translated entities.
*
*
* Please see the 'translatable' module for managing translations of database-content.
*
*
* <b>Usage</b>
*
*
* PHP:
* <code>
* _t('MyNamespace.MYENTITY', 'My default natural language value');
* _t('MyNamespace.MYENTITY', 'My default natural language value', 'My explanatory context');
* sprintf(_t('MyNamespace.MYENTITY', 'Counting %s things'), 42);
* </code>
*
*
* Templates:
* <code>
* <% _t('MyNamespace.MYENTITY', 'My default natural language value') %>
@ -27,33 +27,33 @@ require_once 'i18nSSLegacyAdapter.php';
* <code>
* ss.i18n._t('MyEntity.MyNamespace','My default natural language value');
* </code>
*
*
* File-based i18n-translations always have a "locale" (e.g. 'en_US').
* Common language names (e.g. 'en') are mainly used in the 'translatable' module
* database-entities.
*
*
* <b>Text Collection</b>
*
*
* Features a "textcollector-mode" that parses all files with a certain extension
* (currently *.php and *.ss) for new translatable strings. Textcollector will write
* updated string-tables to their respective folders inside the module, and automatically
* namespace entities to the classes/templates they are found in (e.g. $lang['en_US']['AssetAdmin']['UPLOADFILES']).
*
*
* Caution: Does not apply any character-set conversion, it is assumed that all content
* is stored and represented in UTF-8 (Unicode). Please make sure your files are created with the correct
* character-set, and your HTML-templates render UTF-8.
*
*
* Caution: The language file has to be stored in the same module path as the "filename namespaces"
* on the entities. So an entity stored in $lang['en_US']['AssetAdmin']['DETAILSTAB'] has to
* in the language file cms/lang/en_US.php, as the referenced file (AssetAdmin.php) is stored
* in the "cms" module.
*
*
* <b>Locales</b>
*
* For the i18n class, a "locale" consists of a language code plus a region code separated by an underscore,
* For the i18n class, a "locale" consists of a language code plus a region code separated by an underscore,
* for example "de_AT" for German language ("de") in the region Austria ("AT").
* See http://www.w3.org/International/articles/language-tags/ for a detailed description.
*
*
* @see http://doc.silverstripe.org/i18n
* @see http://www.w3.org/TR/i18n-html-tech-lang
*
@ -62,24 +62,24 @@ require_once 'i18nSSLegacyAdapter.php';
* @subpackage misc
*/
class i18n extends Object implements TemplateGlobalProvider {
/**
* This static variable is used to store the current defined locale.
*/
protected static $current_locale = '';
/**
* @config
* @var string
*/
private static $default_locale = 'en_US';
/**
* @config
* @var boolean
*/
private static $js_i18n = true;
/**
* @config
* @var string
@ -96,7 +96,7 @@ class i18n extends Object implements TemplateGlobalProvider {
* @var array Array of priority keys to instances of Zend_Translate, mapped by name.
*/
protected static $translators;
/**
* Use javascript i18n through the ss.i18n class (enabled by default).
* If set to TRUE, includes javascript requirements for the base library
@ -108,8 +108,8 @@ class i18n extends Object implements TemplateGlobalProvider {
*
* Caution: This flag gets overwritten in {@link LeftAndMain::init()} to enforce javascript
* i18n for the CMS interfaces.
*
* @see Requirements::process_i18n_javascript()
*
* @see Requirements::process_i18n_javascript()
*
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead
* @param bool $bool
@ -118,7 +118,7 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead');
Config::inst()->update('i18n', 'js_i18n', $bool);
}
/**
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead
* @return bool
@ -127,7 +127,7 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead');
return Config::inst()->get('i18n', 'js_i18n');
}
/**
* @deprecated 3.2 Use the "i18n.date_format" config setting instead
* @param string ISO date format
@ -136,7 +136,7 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead');
Config::inst()->update('i18n', 'date_format', $format);
}
/**
* @return string ISO date format
*/
@ -144,7 +144,7 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead');
return Config::inst()->get('i18n', 'date_format');
}
/**
* @deprecated 3.2 Use the "i18n.time_format" config setting instead
* @param string ISO time format
@ -153,7 +153,7 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead');
Config::inst()->update('i18n', 'time_format', $format);
}
/**
* @return string ISO time format
*/
@ -161,7 +161,7 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead');
return Config::inst()->get('i18n', 'time_format');
}
/**
* An exhaustive list of possible locales (code => language and country)
*
@ -169,487 +169,487 @@ class i18n extends Object implements TemplateGlobalProvider {
* @var array
*/
private static $all_locales = array (
'aa_DJ' => 'Afar (Djibouti)',
'ab_GE' => 'Abkhazian (Georgia)',
'abr_GH' => 'Abron (Ghana)',
'ace_ID' => 'Achinese (Indonesia)',
'ady_RU' => 'Adyghe (Russia)',
'af_ZA' => 'Afrikaans (South Africa)',
'ak_GH' => 'Akan (Ghana)',
'am_ET' => 'Amharic (Ethiopia)',
'ar_AE' => 'Arabic (United Arab Emirates)',
'ar_BH' => 'Arabic (Bahrain)',
'ar_DZ' => 'Arabic (Algeria)',
'ar_EG' => 'Arabic (Egypt)',
'ar_EH' => 'Arabic (Western Sahara)',
'ar_IQ' => 'Arabic (Iraq)',
'ar_JO' => 'Arabic (Jordan)',
'ar_KW' => 'Arabic (Kuwait)',
'ar_LB' => 'Arabic (Lebanon)',
'ar_LY' => 'Arabic (Libya)',
'ar_MA' => 'Arabic (Morocco)',
'ar_MR' => 'Arabic (Mauritania)',
'ar_OM' => 'Arabic (Oman)',
'ar_PS' => 'Arabic (Palestinian Territory)',
'ar_QA' => 'Arabic (Qatar)',
'ar_SA' => 'Arabic (Saudi Arabia)',
'ar_SD' => 'Arabic (Sudan)',
'ar_SY' => 'Arabic (Syria)',
'ar_TD' => 'Arabic (Chad)',
'ar_TN' => 'Arabic (Tunisia)',
'ar_YE' => 'Arabic (Yemen)',
'as_IN' => 'Assamese (India)',
'ast_ES' => 'Asturian (Spain)',
'auv_FR' => 'Auvergnat (France)',
'av_RU' => 'Avaric (Russia)',
'awa_IN' => 'Awadhi (India)',
'ay_BO' => 'Aymara (Bolivia)',
'ay_PE' => 'Aymara (Peru)',
'az_AZ' => 'Azerbaijani (Azerbaijan)',
'az_IR' => 'Azerbaijani (Iran)',
'ba_RU' => 'Bashkir (Russia)',
'ban_ID' => 'Balinese (Indonesia)',
'bcc_PK' => 'Balochi, Southern (Pakistan)',
'bcl_PH' => 'Bicolano, Central (Philippines)',
'be_BY' => 'Belarusian (Belarus)',
'bew_ID' => 'Betawi (Indonesia)',
'bg_BG' => 'Bulgarian (Bulgaria)',
'bgc_IN' => 'Haryanvi (India)',
'bgn_PK' => 'Balochi, Western (Pakistan)',
'bgp_PK' => 'Balochi, Easter (Pakistan)',
'bhb_IN' => 'Bhili (India)',
'bhi_IN' => 'Bhilali (India)',
'bhk_PH' => 'Bicolano, Albay (Philippines)',
'bho_IN' => 'Bhojpuri (India)',
'bho_MU' => 'Bhojpuri (Mauritius)',
'bho_NP' => 'Bhojpuri (Nepal)',
'bi_VU' => 'Bislama (Vanuatu)',
'bjj_IN' => 'Kanauji (India)',
'bjn_ID' => 'Banjar (Indonesia)',
'bm_ML' => 'Bambara (Mali)',
'bn_BD' => 'Bengali (Bangladesh)',
'bn_IN' => 'Bengali (India)',
'bo_CN' => 'Tibetan (China)',
'bqi_IR' => 'Bakhtiari (Iran)',
'brh_PK' => 'Brahui (Pakistan)',
'bs_BA' => 'Bosnian (Bosnia and Herzegovina)',
'btk_ID' => 'Batak (Indonesia)',
'buc_YT' => 'Bushi (Mayotte)',
'bug_ID' => 'Buginese (Indonesia)',
'ca_AD' => 'Catalan (Andorra)',
'ca_ES' => 'Catalan (Spain)',
'ce_RU' => 'Chechen (Russia)',
'ceb_PH' => 'Cebuano (Philippines)',
'cgg_UG' => 'Chiga (Uganda)',
'ch_GU' => 'Chamorro (Guam)',
'chk_FM' => 'Chuukese (Micronesia)',
'crk_CA' => 'Cree, Plains (Canada)',
'cs_CZ' => 'Czech (Czech Republic)',
'cwd_CA' => 'Cree, Woods (Canada)',
'cy_GB' => 'Welsh (United Kingdom)',
'da_DK' => 'Danish (Denmark)',
'da_GL' => 'Danish (Greenland)',
'dcc_IN' => 'Deccan (India)',
'de_AT' => 'German (Austria)',
'de_BE' => 'German (Belgium)',
'de_CH' => 'German (Switzerland)',
'de_DE' => 'German (Germany)',
'de_LI' => 'German (Liechtenstein)',
'de_LU' => 'German (Luxembourg)',
'dgo_IN' => 'Dogri (India)',
'dhd_IN' => 'Dhundari (India)',
'diq_TR' => 'Dimli (Turkey)',
'dje_NE' => 'Zarma (Niger)',
'dv_MV' => 'Divehi (Maldives)',
'dz_BT' => 'Dzongkha (Bhutan)',
'ee_GH' => 'Ewe (Ghana)',
'el_CY' => 'Greek (Cyprus)',
'el_GR' => 'Greek (Greece)',
'en_AS' => 'English (American Samoa)',
'en_AU' => 'English (Australia)',
'en_BM' => 'English (Bermuda)',
'en_BS' => 'English (Bahamas)',
'en_CA' => 'English (Canada)',
'en_DE' => 'English (Germany)',
'en_ES' => 'English (Spain)',
'en_FR' => 'English (France)',
'en_GB' => 'English (United Kingdom)',
'en_HK' => 'English (Hong Kong SAR China)',
'en_IE' => 'English (Ireland)',
'en_IN' => 'English (India)',
'en_IT' => 'English (Italy)',
'en_JM' => 'English (Jamaica)',
'en_KE' => 'English (Kenya)',
'en_LR' => 'English (Liberia)',
'en_MM' => 'English (Myanmar)',
'en_MW' => 'English (Malawi)',
'en_MY' => 'English (Malaysia)',
'en_NL' => 'English (Netherlands)',
'en_NZ' => 'English (New Zealand)',
'en_PH' => 'English (Philippines)',
'en_SG' => 'English (Singapore)',
'en_TT' => 'English (Trinidad and Tobago)',
'en_US' => 'English (United States)',
'en_ZA' => 'English (South Africa)',
'eo_XX' => 'Esperanto',
'es_419' => 'Spanish (Latin America)',
'es_AR' => 'Spanish (Argentina)',
'es_BO' => 'Spanish (Bolivia)',
'es_CL' => 'Spanish (Chile)',
'es_CO' => 'Spanish (Colombia)',
'es_CR' => 'Spanish (Costa Rica)',
'es_CU' => 'Spanish (Cuba)',
'es_DO' => 'Spanish (Dominican Republic)',
'es_EC' => 'Spanish (Ecuador)',
'es_ES' => 'Spanish (Spain)',
'es_GQ' => 'Spanish (Equatorial Guinea)',
'es_GT' => 'Spanish (Guatemala)',
'es_HN' => 'Spanish (Honduras)',
'es_MX' => 'Spanish (Mexico)',
'es_NI' => 'Spanish (Nicaragua)',
'es_PA' => 'Spanish (Panama)',
'es_PE' => 'Spanish (Peru)',
'es_PH' => 'Spanish (Philippines)',
'es_PR' => 'Spanish (Puerto Rico)',
'es_PY' => 'Spanish (Paraguay)',
'es_SV' => 'Spanish (El Salvador)',
'es_US' => 'Spanish (United States)',
'es_UY' => 'Spanish (Uruguay)',
'es_VE' => 'Spanish (Venezuela)',
'et_EE' => 'Estonian (Estonia)',
'eu_ES' => 'Basque (Spain)',
'fa_AF' => 'Persian (Afghanistan)',
'fa_IR' => 'Persian (Iran)',
'fa_PK' => 'Persian (Pakistan)',
'fan_GQ' => 'Fang (Equatorial Guinea)',
'fi_FI' => 'Finnish (Finland)',
'fi_SE' => 'Finnish (Sweden)',
'fil_PH' => 'Filipino (Philippines)',
'fj_FJ' => 'Fijian (Fiji)',
'fo_FO' => 'Faroese (Faroe Islands)',
'fon_BJ' => 'Fon (Benin)',
'fr_002' => 'French (Africa)',
'fr_BE' => 'French (Belgium)',
'fr_CA' => 'French (Canada)',
'fr_CH' => 'French (Switzerland)',
'fr_DZ' => 'French (Algeria)',
'fr_FR' => 'French (France)',
'fr_GF' => 'French (French Guiana)',
'fr_GP' => 'French (Guadeloupe)',
'fr_HT' => 'French (Haiti)',
'fr_KM' => 'French (Comoros)',
'fr_MA' => 'French (Morocco)',
'fr_MQ' => 'French (Martinique)',
'fr_MU' => 'French (Mauritius)',
'fr_NC' => 'French (New Caledonia)',
'fr_PF' => 'French (French Polynesia)',
'fr_PM' => 'French (Saint Pierre and Miquelon)',
'fr_RE' => 'French (Reunion)',
'fr_SC' => 'French (Seychelles)',
'fr_SN' => 'French (Senegal)',
'fr_US' => 'French (United States)',
'fuv_NG' => 'Fulfulde (Nigeria)',
'ga_GB' => 'Irish (United Kingdom)',
'ga_IE' => 'Irish (Ireland)',
'gaa_GH' => 'Ga (Ghana)',
'gbm_IN' => 'Garhwali (India)',
'gcr_GF' => 'Guianese Creole French (French Guiana)',
'gd_GB' => 'Scottish Gaelic (United Kingdom)',
'gil_KI' => 'Gilbertese (Kiribati)',
'gl_ES' => 'Galician (Spain)',
'glk_IR' => 'Gilaki (Iran)',
'gn_PY' => 'Guarani (Paraguay)',
'gno_IN' => 'Gondi, Northern (India)',
'gsw_CH' => 'Swiss German (Switzerland)',
'gsw_LI' => 'Swiss German (Liechtenstein)',
'gu_IN' => 'Gujarati (India)',
'guz_KE' => 'Gusii (Kenya)',
'ha_NE' => 'Hausa (Niger)',
'ha_NG' => 'Hausa (Nigeria)',
'haw_US' => 'Hawaiian (United States)',
'haz_AF' => 'Hazaragi (Afghanistan)',
'he_IL' => 'Hebrew (Israel)',
'hi_IN' => 'Hindi (India)',
'hil_PH' => 'Hiligaynon (Philippines)',
'hne_IN' => 'Chhattisgarhi (India)',
'hno_PK' => 'Hindko, Northern (Pakistan)',
'hoc_IN' => 'Ho (India)',
'hr_AT' => 'Croatian (Austria)',
'hr_BA' => 'Croatian (Bosnia and Herzegovina)',
'hr_HR' => 'Croatian (Croatia)',
'ht_HT' => 'Haitian (Haiti)',
'hu_AT' => 'Hungarian (Austria)',
'hu_HU' => 'Hungarian (Hungary)',
'hu_RO' => 'Hungarian (Romania)',
'hu_RS' => 'Hungarian (Serbia)',
'hy_AM' => 'Armenian (Armenia)',
'id_ID' => 'Indonesian (Indonesia)',
'ig_NG' => 'Igbo (Nigeria)',
'ilo_PH' => 'Iloko (Philippines)',
'inh_RU' => 'Ingush (Russia)',
'is_IS' => 'Icelandic (Iceland)',
'it_CH' => 'Italian (Switzerland)',
'it_FR' => 'Italian (France)',
'it_HR' => 'Italian (Croatia)',
'it_IT' => 'Italian (Italy)',
'it_SM' => 'Italian (San Marino)',
'it_US' => 'Italian (United States)',
'iu_CA' => 'Inuktitut (Canada)',
'ja_JP' => 'Japanese (Japan)',
'jv_ID' => 'Javanese (Indonesia)',
'ka_GE' => 'Georgian (Georgia)',
'kam_KE' => 'Kamba (Kenya)',
'kbd_RU' => 'Kabardian (Russia)',
'kfy_IN' => 'Kumauni (India)',
'kha_IN' => 'Khasi (India)',
'khn_IN' => 'Khandesi (India)',
'ki_KE' => 'Kikuyu (Kenya)',
'kj_NA' => 'Kuanyama (Namibia)',
'kk_CN' => 'Kazakh (China)',
'kk_KZ' => 'Kazakh (Kazakhstan)',
'kl_DK' => 'Kalaallisut (Denmark)',
'kl_GL' => 'Kalaallisut (Greenland)',
'kln_KE' => 'Kalenjin (Kenya)',
'km_KH' => 'Khmer (Cambodia)',
'kn_IN' => 'Kannada (India)',
'ko_KR' => 'Korean (Korea)',
'koi_RU' => 'Komi-Permyak (Russia)',
'kok_IN' => 'Konkani (India)',
'kos_FM' => 'Kosraean (Micronesia)',
'kpv_RU' => 'Komi-Zyrian (Russia)',
'krc_RU' => 'Karachay-Balkar (Russia)',
'kru_IN' => 'Kurukh (India)',
'ks_IN' => 'Kashmiri (India)',
'ku_IQ' => 'Kurdish (Iraq)',
'ku_IR' => 'Kurdish (Iran)',
'ku_SY' => 'Kurdish (Syria)',
'ku_TR' => 'Kurdish (Turkey)',
'kum_RU' => 'Kumyk (Russia)',
'kxm_TH' => 'Khmer, Northern (Thailand)',
'ky_KG' => 'Kirghiz (Kyrgyzstan)',
'la_VA' => 'Latin (Vatican)',
'lah_PK' => 'Lahnda (Pakistan)',
'lb_LU' => 'Luxembourgish (Luxembourg)',
'lbe_RU' => 'Lak (Russia)',
'lc_XX' => 'LOLCAT',
'lez_RU' => 'Lezghian (Russia)',
'lg_UG' => 'Ganda (Uganda)',
'lij_IT' => 'Ligurian (Italy)',
'lij_MC' => 'Ligurian (Monaco)',
'ljp_ID' => 'Lampung (Indonesia)',
'lmn_IN' => 'Lambadi (India)',
'ln_CD' => 'Lingala (Congo - Kinshasa)',
'ln_CG' => 'Lingala (Congo - Brazzaville)',
'lo_LA' => 'Lao (Laos)',
'lrc_IR' => 'Luri, Northern (Iran)',
'lt_LT' => 'Lithuanian (Lithuania)',
'luo_KE' => 'Luo (Kenya)',
'luy_KE' => 'Luyia (Kenya)',
'lv_LV' => 'Latvian (Latvia)',
'mad_ID' => 'Madurese (Indonesia)',
'mai_IN' => 'Maithili (India)',
'mai_NP' => 'Maithili (Nepal)',
'mak_ID' => 'Makasar (Indonesia)',
'mdf_RU' => 'Moksha (Russia)',
'mdh_PH' => 'Maguindanao (Philippines)',
'mer_KE' => 'Meru (Kenya)',
'mfa_TH' => 'Malay, Pattani (Thailand)',
'mfe_MU' => 'Morisyen (Mauritius)',
'mg_MG' => 'Malagasy (Madagascar)',
'mh_MH' => 'Marshallese (Marshall Islands)',
'mi_NZ' => 'Māori (New Zealand)',
'min_ID' => 'Minangkabau (Indonesia)',
'mk_MK' => 'Macedonian (Macedonia)',
'ml_IN' => 'Malayalam (India)',
'mn_CN' => 'Mongolian (China)',
'mn_MN' => 'Mongolian (Mongolia)',
'mni_IN' => 'Manipuri (India)',
'mr_IN' => 'Marathi (India)',
'ms_BN' => 'Malay (Brunei)',
'ms_CC' => 'Malay (Cocos Islands)',
'ms_ID' => 'Malay (Indonesia)',
'ms_MY' => 'Malay (Malaysia)',
'ms_SG' => 'Malay (Singapore)',
'mt_MT' => 'Maltese (Malta)',
'mtr_IN' => 'Mewari (India)',
'mup_IN' => 'Malvi (India)',
'muw_IN' => 'Mundari (India)',
'my_MM' => 'Burmese (Myanmar)',
'myv_RU' => 'Erzya (Russia)',
'na_NR' => 'Nauru (Nauru)',
'nb_NO' => 'Norwegian Bokmal (Norway)',
'nb_SJ' => 'Norwegian Bokmal (Svalbard and Jan Mayen)',
'nd_ZW' => 'North Ndebele (Zimbabwe)',
'ndc_MZ' => 'Ndau (Mozambique)',
'ne_IN' => 'Nepali (India)',
'ne_NP' => 'Nepali (Nepal)',
'ng_NA' => 'Ndonga (Namibia)',
'ngl_MZ' => 'Lomwe (Mozambique)',
'niu_NU' => 'Niuean (Niue)',
'nl_AN' => 'Dutch (Netherlands Antilles)',
'nl_AW' => 'Dutch (Aruba)',
'nl_BE' => 'Dutch (Belgium)',
'nl_NL' => 'Dutch (Netherlands)',
'nl_SR' => 'Dutch (Suriname)',
'nn_NO' => 'Norwegian Nynorsk (Norway)',
'nb_NO' => 'Norwegian',
'nod_TH' => 'Thai, Northern (Thailand)',
'noe_IN' => 'Nimadi (India)',
'nso_ZA' => 'Northern Sotho (South Africa)',
'ny_MW' => 'Nyanja (Malawi)',
'ny_ZM' => 'Nyanja (Zambia)',
'nyn_UG' => 'Nyankole (Uganda)',
'om_ET' => 'Oromo (Ethiopia)',
'or_IN' => 'Oriya (India)',
'pa_IN' => 'Punjabi (India)',
'pag_PH' => 'Pangasinan (Philippines)',
'pap_AN' => 'Papiamento (Netherlands Antilles)',
'pap_AW' => 'Papiamento (Aruba)',
'pau_PW' => 'Palauan (Palau)',
'pl_PL' => 'Polish (Poland)',
'pl_UA' => 'Polish (Ukraine)',
'pon_FM' => 'Pohnpeian (Micronesia)',
'ps_AF' => 'Pashto (Afghanistan)',
'ps_PK' => 'Pashto (Pakistan)',
'pt_AO' => 'Portuguese (Angola)',
'pt_BR' => 'Portuguese (Brazil)',
'pt_CV' => 'Portuguese (Cape Verde)',
'pt_GW' => 'Portuguese (Guinea-Bissau)',
'pt_MZ' => 'Portuguese (Mozambique)',
'pt_PT' => 'Portuguese (Portugal)',
'pt_ST' => 'Portuguese (Sao Tome and Principe)',
'pt_TL' => 'Portuguese (East Timor)',
'qu_BO' => 'Quechua (Bolivia)',
'qu_PE' => 'Quechua (Peru)',
'rcf_RE' => 'R<>union Creole French (Reunion)',
'rej_ID' => 'Rejang (Indonesia)',
'rif_MA' => 'Tarifit (Morocco)',
'rjb_IN' => 'Rajbanshi (India)',
'rm_CH' => 'Rhaeto-Romance (Switzerland)',
'rmt_IR' => 'Domari (Iran)',
'rn_BI' => 'Rundi (Burundi)',
'ro_MD' => 'Romanian (Moldova)',
'ro_RO' => 'Romanian (Romania)',
'ro_RS' => 'Romanian (Serbia)',
'ru_BY' => 'Russian (Belarus)',
'ru_KG' => 'Russian (Kyrgyzstan)',
'ru_KZ' => 'Russian (Kazakhstan)',
'ru_RU' => 'Russian (Russia)',
'ru_SJ' => 'Russian (Svalbard and Jan Mayen)',
'ru_UA' => 'Russian (Ukraine)',
'rw_RW' => 'Kinyarwanda (Rwanda)',
'sa_IN' => 'Sanskrit (India)',
'sah_RU' => 'Yakut (Russia)',
'sas_ID' => 'Sasak (Indonesia)',
'sat_IN' => 'Santali (India)',
'sck_IN' => 'Sadri (India)',
'sco_GB' => 'Scots (United Kingdom)',
'sco_SCO' => 'Scots',
'sd_IN' => 'Sindhi (India)',
'sd_PK' => 'Sindhi (Pakistan)',
'se_NO' => 'Northern Sami (Norway)',
'sg_CF' => 'Sango (Central African Republic)',
'si_LK' => 'Sinhalese (Sri Lanka)',
'sid_ET' => 'Sidamo (Ethiopia)',
'sk_RS' => 'Slovak (Serbia)',
'sk_SK' => 'Slovak (Slovakia)',
'sl_AT' => 'Slovenian (Austria)',
'sl_SI' => 'Slovenian (Slovenia)',
'sm_AS' => 'Samoan (American Samoa)',
'sm_WS' => 'Samoan (Samoa)',
'sn_ZW' => 'Shona (Zimbabwe)',
'so_DJ' => 'Somali (Djibouti)',
'so_ET' => 'Somali (Ethiopia)',
'so_SO' => 'Somali (Somalia)',
'sou_TH' => 'Thai, Southern (Thailand)',
'sq_AL' => 'Albanian (Albania)',
'sr_BA' => 'Serbian (Bosnia and Herzegovina)',
'sr_ME' => 'Serbian (Montenegro)',
'sr_RS' => 'Serbian (Serbia)',
'ss_SZ' => 'Swati (Swaziland)',
'ss_ZA' => 'Swati (South Africa)',
'st_LS' => 'Southern Sotho (Lesotho)',
'st_ZA' => 'Southern Sotho (South Africa)',
'su_ID' => 'Sundanese (Indonesia)',
'sv_AX' => 'Swedish (Aland Islands)',
'sv_FI' => 'Swedish (Finland)',
'sv_SE' => 'Swedish (Sweden)',
'sw_KE' => 'Swahili (Kenya)',
'sw_SO' => 'Swahili (Somalia)',
'sw_TZ' => 'Swahili (Tanzania)',
'sw_UG' => 'Swahili (Uganda)',
'swb_KM' => 'Comorian (Comoros)',
'swb_YT' => 'Comorian (Mayotte)',
'swv_IN' => 'Shekhawati (India)',
'ta_IN' => 'Tamil (India)',
'ta_LK' => 'Tamil (Sri Lanka)',
'ta_MY' => 'Tamil (Malaysia)',
'ta_SG' => 'Tamil (Singapore)',
'tcy_IN' => 'Tulu (India)',
'te_IN' => 'Telugu (India)',
'tet_TL' => 'Tetum (East Timor)',
'tg_TJ' => 'Tajik (Tajikistan)',
'th_TH' => 'Thai (Thailand)',
'ti_ER' => 'Tigrinya (Eritrea)',
'ti_ET' => 'Tigrinya (Ethiopia)',
'tk_IR' => 'Turkmen (Iran)',
'tk_TM' => 'Turkmen (Turkmenistan)',
'tkl_TK' => 'Tokelau (Tokelau)',
'tl_PH' => 'Tagalog (Philippines)',
'tl_US' => 'Tagalog (United States)',
'tn_BW' => 'Tswana (Botswana)',
'tn_ZA' => 'Tswana (South Africa)',
'to_TO' => 'Tonga (Tonga)',
'tr_CY' => 'Turkish (Cyprus)',
'tr_DE' => 'Turkish (Germany)',
'tr_MK' => 'Turkish (Macedonia)',
'tr_TR' => 'Turkish (Turkey)',
'ts_MZ' => 'Tsonga (Mozambique)',
'ts_ZA' => 'Tsonga (South Africa)',
'tsg_PH' => 'Tausug (Philippines)',
'tt_RU' => 'Tatar (Russia)',
'tts_TH' => 'Thai, Northeastern (Thailand)',
'tvl_TV' => 'Tuvalu (Tuvalu)',
'tw_GH' => 'Twi (Ghana)',
'ty_PF' => 'Tahitian (French Polynesia)',
'tyv_RU' => 'Tuvinian (Russia)',
'tzm_MA' => 'Tamazight, Central Atlas (Morocco)',
'udm_RU' => 'Udmurt (Russia)',
'ug_CN' => 'Uighur (China)',
'uk_UA' => 'Ukrainian (Ukraine)',
'uli_FM' => 'Ulithian (Micronesia)',
'ur_IN' => 'Urdu (India)',
'ur_PK' => 'Urdu (Pakistan)',
'uz_AF' => 'Uzbek (Afghanistan)',
'uz_UZ' => 'Uzbek (Uzbekistan)',
've_ZA' => 'Venda (South Africa)',
'vi_US' => 'Vietnamese (United States)',
'vi_VN' => 'Vietnamese (Vietnam)',
'vmw_MZ' => 'Waddar (Mozambique)',
'wal_ET' => 'Walamo (Ethiopia)',
'war_PH' => 'Waray (Philippines)',
'wbq_IN' => 'Waddar (India)',
'wbr_IN' => 'Wagdi (India)',
'wo_MR' => 'Wolof (Mauritania)',
'wo_SN' => 'Wolof (Senegal)',
'wtm_IN' => 'Mewati (India)',
'xh_ZA' => 'Xhosa (South Africa)',
'xnr_IN' => 'Kangri (India)',
'xog_UG' => 'Soga (Uganda)',
'yap_FM' => 'Yapese (Micronesia)',
'yo_NG' => 'Yoruba (Nigeria)',
'za_CN' => 'Zhuang (China)',
'zh_CN' => 'Chinese (China)',
'zh_HK' => 'Chinese (Hong Kong SAR China)',
'zh_MO' => 'Chinese (Macao SAR China)',
'zh_SG' => 'Chinese (Singapore)',
'zh_TW' => 'Chinese (Taiwan)',
'zh_US' => 'Chinese (United States)',
'zh_cmn' => 'Chinese (Mandarin)',
'zh_yue' => 'Chinese (Cantonese)',
'zu_ZA' => 'Zulu (South Africa)'
'aa_DJ' => 'Afar (Djibouti)',
'ab_GE' => 'Abkhazian (Georgia)',
'abr_GH' => 'Abron (Ghana)',
'ace_ID' => 'Achinese (Indonesia)',
'ady_RU' => 'Adyghe (Russia)',
'af_ZA' => 'Afrikaans (South Africa)',
'ak_GH' => 'Akan (Ghana)',
'am_ET' => 'Amharic (Ethiopia)',
'ar_AE' => 'Arabic (United Arab Emirates)',
'ar_BH' => 'Arabic (Bahrain)',
'ar_DZ' => 'Arabic (Algeria)',
'ar_EG' => 'Arabic (Egypt)',
'ar_EH' => 'Arabic (Western Sahara)',
'ar_IQ' => 'Arabic (Iraq)',
'ar_JO' => 'Arabic (Jordan)',
'ar_KW' => 'Arabic (Kuwait)',
'ar_LB' => 'Arabic (Lebanon)',
'ar_LY' => 'Arabic (Libya)',
'ar_MA' => 'Arabic (Morocco)',
'ar_MR' => 'Arabic (Mauritania)',
'ar_OM' => 'Arabic (Oman)',
'ar_PS' => 'Arabic (Palestinian Territory)',
'ar_QA' => 'Arabic (Qatar)',
'ar_SA' => 'Arabic (Saudi Arabia)',
'ar_SD' => 'Arabic (Sudan)',
'ar_SY' => 'Arabic (Syria)',
'ar_TD' => 'Arabic (Chad)',
'ar_TN' => 'Arabic (Tunisia)',
'ar_YE' => 'Arabic (Yemen)',
'as_IN' => 'Assamese (India)',
'ast_ES' => 'Asturian (Spain)',
'auv_FR' => 'Auvergnat (France)',
'av_RU' => 'Avaric (Russia)',
'awa_IN' => 'Awadhi (India)',
'ay_BO' => 'Aymara (Bolivia)',
'ay_PE' => 'Aymara (Peru)',
'az_AZ' => 'Azerbaijani (Azerbaijan)',
'az_IR' => 'Azerbaijani (Iran)',
'ba_RU' => 'Bashkir (Russia)',
'ban_ID' => 'Balinese (Indonesia)',
'bcc_PK' => 'Balochi, Southern (Pakistan)',
'bcl_PH' => 'Bicolano, Central (Philippines)',
'be_BY' => 'Belarusian (Belarus)',
'bew_ID' => 'Betawi (Indonesia)',
'bg_BG' => 'Bulgarian (Bulgaria)',
'bgc_IN' => 'Haryanvi (India)',
'bgn_PK' => 'Balochi, Western (Pakistan)',
'bgp_PK' => 'Balochi, Easter (Pakistan)',
'bhb_IN' => 'Bhili (India)',
'bhi_IN' => 'Bhilali (India)',
'bhk_PH' => 'Bicolano, Albay (Philippines)',
'bho_IN' => 'Bhojpuri (India)',
'bho_MU' => 'Bhojpuri (Mauritius)',
'bho_NP' => 'Bhojpuri (Nepal)',
'bi_VU' => 'Bislama (Vanuatu)',
'bjj_IN' => 'Kanauji (India)',
'bjn_ID' => 'Banjar (Indonesia)',
'bm_ML' => 'Bambara (Mali)',
'bn_BD' => 'Bengali (Bangladesh)',
'bn_IN' => 'Bengali (India)',
'bo_CN' => 'Tibetan (China)',
'bqi_IR' => 'Bakhtiari (Iran)',
'brh_PK' => 'Brahui (Pakistan)',
'bs_BA' => 'Bosnian (Bosnia and Herzegovina)',
'btk_ID' => 'Batak (Indonesia)',
'buc_YT' => 'Bushi (Mayotte)',
'bug_ID' => 'Buginese (Indonesia)',
'ca_AD' => 'Catalan (Andorra)',
'ca_ES' => 'Catalan (Spain)',
'ce_RU' => 'Chechen (Russia)',
'ceb_PH' => 'Cebuano (Philippines)',
'cgg_UG' => 'Chiga (Uganda)',
'ch_GU' => 'Chamorro (Guam)',
'chk_FM' => 'Chuukese (Micronesia)',
'crk_CA' => 'Cree, Plains (Canada)',
'cs_CZ' => 'Czech (Czech Republic)',
'cwd_CA' => 'Cree, Woods (Canada)',
'cy_GB' => 'Welsh (United Kingdom)',
'da_DK' => 'Danish (Denmark)',
'da_GL' => 'Danish (Greenland)',
'dcc_IN' => 'Deccan (India)',
'de_AT' => 'German (Austria)',
'de_BE' => 'German (Belgium)',
'de_CH' => 'German (Switzerland)',
'de_DE' => 'German (Germany)',
'de_LI' => 'German (Liechtenstein)',
'de_LU' => 'German (Luxembourg)',
'dgo_IN' => 'Dogri (India)',
'dhd_IN' => 'Dhundari (India)',
'diq_TR' => 'Dimli (Turkey)',
'dje_NE' => 'Zarma (Niger)',
'dv_MV' => 'Divehi (Maldives)',
'dz_BT' => 'Dzongkha (Bhutan)',
'ee_GH' => 'Ewe (Ghana)',
'el_CY' => 'Greek (Cyprus)',
'el_GR' => 'Greek (Greece)',
'en_AS' => 'English (American Samoa)',
'en_AU' => 'English (Australia)',
'en_BM' => 'English (Bermuda)',
'en_BS' => 'English (Bahamas)',
'en_CA' => 'English (Canada)',
'en_DE' => 'English (Germany)',
'en_ES' => 'English (Spain)',
'en_FR' => 'English (France)',
'en_GB' => 'English (United Kingdom)',
'en_HK' => 'English (Hong Kong SAR China)',
'en_IE' => 'English (Ireland)',
'en_IN' => 'English (India)',
'en_IT' => 'English (Italy)',
'en_JM' => 'English (Jamaica)',
'en_KE' => 'English (Kenya)',
'en_LR' => 'English (Liberia)',
'en_MM' => 'English (Myanmar)',
'en_MW' => 'English (Malawi)',
'en_MY' => 'English (Malaysia)',
'en_NL' => 'English (Netherlands)',
'en_NZ' => 'English (New Zealand)',
'en_PH' => 'English (Philippines)',
'en_SG' => 'English (Singapore)',
'en_TT' => 'English (Trinidad and Tobago)',
'en_US' => 'English (United States)',
'en_ZA' => 'English (South Africa)',
'eo_XX' => 'Esperanto',
'es_419' => 'Spanish (Latin America)',
'es_AR' => 'Spanish (Argentina)',
'es_BO' => 'Spanish (Bolivia)',
'es_CL' => 'Spanish (Chile)',
'es_CO' => 'Spanish (Colombia)',
'es_CR' => 'Spanish (Costa Rica)',
'es_CU' => 'Spanish (Cuba)',
'es_DO' => 'Spanish (Dominican Republic)',
'es_EC' => 'Spanish (Ecuador)',
'es_ES' => 'Spanish (Spain)',
'es_GQ' => 'Spanish (Equatorial Guinea)',
'es_GT' => 'Spanish (Guatemala)',
'es_HN' => 'Spanish (Honduras)',
'es_MX' => 'Spanish (Mexico)',
'es_NI' => 'Spanish (Nicaragua)',
'es_PA' => 'Spanish (Panama)',
'es_PE' => 'Spanish (Peru)',
'es_PH' => 'Spanish (Philippines)',
'es_PR' => 'Spanish (Puerto Rico)',
'es_PY' => 'Spanish (Paraguay)',
'es_SV' => 'Spanish (El Salvador)',
'es_US' => 'Spanish (United States)',
'es_UY' => 'Spanish (Uruguay)',
'es_VE' => 'Spanish (Venezuela)',
'et_EE' => 'Estonian (Estonia)',
'eu_ES' => 'Basque (Spain)',
'fa_AF' => 'Persian (Afghanistan)',
'fa_IR' => 'Persian (Iran)',
'fa_PK' => 'Persian (Pakistan)',
'fan_GQ' => 'Fang (Equatorial Guinea)',
'fi_FI' => 'Finnish (Finland)',
'fi_SE' => 'Finnish (Sweden)',
'fil_PH' => 'Filipino (Philippines)',
'fj_FJ' => 'Fijian (Fiji)',
'fo_FO' => 'Faroese (Faroe Islands)',
'fon_BJ' => 'Fon (Benin)',
'fr_002' => 'French (Africa)',
'fr_BE' => 'French (Belgium)',
'fr_CA' => 'French (Canada)',
'fr_CH' => 'French (Switzerland)',
'fr_DZ' => 'French (Algeria)',
'fr_FR' => 'French (France)',
'fr_GF' => 'French (French Guiana)',
'fr_GP' => 'French (Guadeloupe)',
'fr_HT' => 'French (Haiti)',
'fr_KM' => 'French (Comoros)',
'fr_MA' => 'French (Morocco)',
'fr_MQ' => 'French (Martinique)',
'fr_MU' => 'French (Mauritius)',
'fr_NC' => 'French (New Caledonia)',
'fr_PF' => 'French (French Polynesia)',
'fr_PM' => 'French (Saint Pierre and Miquelon)',
'fr_RE' => 'French (Reunion)',
'fr_SC' => 'French (Seychelles)',
'fr_SN' => 'French (Senegal)',
'fr_US' => 'French (United States)',
'fuv_NG' => 'Fulfulde (Nigeria)',
'ga_GB' => 'Irish (United Kingdom)',
'ga_IE' => 'Irish (Ireland)',
'gaa_GH' => 'Ga (Ghana)',
'gbm_IN' => 'Garhwali (India)',
'gcr_GF' => 'Guianese Creole French (French Guiana)',
'gd_GB' => 'Scottish Gaelic (United Kingdom)',
'gil_KI' => 'Gilbertese (Kiribati)',
'gl_ES' => 'Galician (Spain)',
'glk_IR' => 'Gilaki (Iran)',
'gn_PY' => 'Guarani (Paraguay)',
'gno_IN' => 'Gondi, Northern (India)',
'gsw_CH' => 'Swiss German (Switzerland)',
'gsw_LI' => 'Swiss German (Liechtenstein)',
'gu_IN' => 'Gujarati (India)',
'guz_KE' => 'Gusii (Kenya)',
'ha_NE' => 'Hausa (Niger)',
'ha_NG' => 'Hausa (Nigeria)',
'haw_US' => 'Hawaiian (United States)',
'haz_AF' => 'Hazaragi (Afghanistan)',
'he_IL' => 'Hebrew (Israel)',
'hi_IN' => 'Hindi (India)',
'hil_PH' => 'Hiligaynon (Philippines)',
'hne_IN' => 'Chhattisgarhi (India)',
'hno_PK' => 'Hindko, Northern (Pakistan)',
'hoc_IN' => 'Ho (India)',
'hr_AT' => 'Croatian (Austria)',
'hr_BA' => 'Croatian (Bosnia and Herzegovina)',
'hr_HR' => 'Croatian (Croatia)',
'ht_HT' => 'Haitian (Haiti)',
'hu_AT' => 'Hungarian (Austria)',
'hu_HU' => 'Hungarian (Hungary)',
'hu_RO' => 'Hungarian (Romania)',
'hu_RS' => 'Hungarian (Serbia)',
'hy_AM' => 'Armenian (Armenia)',
'id_ID' => 'Indonesian (Indonesia)',
'ig_NG' => 'Igbo (Nigeria)',
'ilo_PH' => 'Iloko (Philippines)',
'inh_RU' => 'Ingush (Russia)',
'is_IS' => 'Icelandic (Iceland)',
'it_CH' => 'Italian (Switzerland)',
'it_FR' => 'Italian (France)',
'it_HR' => 'Italian (Croatia)',
'it_IT' => 'Italian (Italy)',
'it_SM' => 'Italian (San Marino)',
'it_US' => 'Italian (United States)',
'iu_CA' => 'Inuktitut (Canada)',
'ja_JP' => 'Japanese (Japan)',
'jv_ID' => 'Javanese (Indonesia)',
'ka_GE' => 'Georgian (Georgia)',
'kam_KE' => 'Kamba (Kenya)',
'kbd_RU' => 'Kabardian (Russia)',
'kfy_IN' => 'Kumauni (India)',
'kha_IN' => 'Khasi (India)',
'khn_IN' => 'Khandesi (India)',
'ki_KE' => 'Kikuyu (Kenya)',
'kj_NA' => 'Kuanyama (Namibia)',
'kk_CN' => 'Kazakh (China)',
'kk_KZ' => 'Kazakh (Kazakhstan)',
'kl_DK' => 'Kalaallisut (Denmark)',
'kl_GL' => 'Kalaallisut (Greenland)',
'kln_KE' => 'Kalenjin (Kenya)',
'km_KH' => 'Khmer (Cambodia)',
'kn_IN' => 'Kannada (India)',
'ko_KR' => 'Korean (Korea)',
'koi_RU' => 'Komi-Permyak (Russia)',
'kok_IN' => 'Konkani (India)',
'kos_FM' => 'Kosraean (Micronesia)',
'kpv_RU' => 'Komi-Zyrian (Russia)',
'krc_RU' => 'Karachay-Balkar (Russia)',
'kru_IN' => 'Kurukh (India)',
'ks_IN' => 'Kashmiri (India)',
'ku_IQ' => 'Kurdish (Iraq)',
'ku_IR' => 'Kurdish (Iran)',
'ku_SY' => 'Kurdish (Syria)',
'ku_TR' => 'Kurdish (Turkey)',
'kum_RU' => 'Kumyk (Russia)',
'kxm_TH' => 'Khmer, Northern (Thailand)',
'ky_KG' => 'Kirghiz (Kyrgyzstan)',
'la_VA' => 'Latin (Vatican)',
'lah_PK' => 'Lahnda (Pakistan)',
'lb_LU' => 'Luxembourgish (Luxembourg)',
'lbe_RU' => 'Lak (Russia)',
'lc_XX' => 'LOLCAT',
'lez_RU' => 'Lezghian (Russia)',
'lg_UG' => 'Ganda (Uganda)',
'lij_IT' => 'Ligurian (Italy)',
'lij_MC' => 'Ligurian (Monaco)',
'ljp_ID' => 'Lampung (Indonesia)',
'lmn_IN' => 'Lambadi (India)',
'ln_CD' => 'Lingala (Congo - Kinshasa)',
'ln_CG' => 'Lingala (Congo - Brazzaville)',
'lo_LA' => 'Lao (Laos)',
'lrc_IR' => 'Luri, Northern (Iran)',
'lt_LT' => 'Lithuanian (Lithuania)',
'luo_KE' => 'Luo (Kenya)',
'luy_KE' => 'Luyia (Kenya)',
'lv_LV' => 'Latvian (Latvia)',
'mad_ID' => 'Madurese (Indonesia)',
'mai_IN' => 'Maithili (India)',
'mai_NP' => 'Maithili (Nepal)',
'mak_ID' => 'Makasar (Indonesia)',
'mdf_RU' => 'Moksha (Russia)',
'mdh_PH' => 'Maguindanao (Philippines)',
'mer_KE' => 'Meru (Kenya)',
'mfa_TH' => 'Malay, Pattani (Thailand)',
'mfe_MU' => 'Morisyen (Mauritius)',
'mg_MG' => 'Malagasy (Madagascar)',
'mh_MH' => 'Marshallese (Marshall Islands)',
'mi_NZ' => 'te reo Māori (New Zealand)',
'min_ID' => 'Minangkabau (Indonesia)',
'mk_MK' => 'Macedonian (Macedonia)',
'ml_IN' => 'Malayalam (India)',
'mn_CN' => 'Mongolian (China)',
'mn_MN' => 'Mongolian (Mongolia)',
'mni_IN' => 'Manipuri (India)',
'mr_IN' => 'Marathi (India)',
'ms_BN' => 'Malay (Brunei)',
'ms_CC' => 'Malay (Cocos Islands)',
'ms_ID' => 'Malay (Indonesia)',
'ms_MY' => 'Malay (Malaysia)',
'ms_SG' => 'Malay (Singapore)',
'mt_MT' => 'Maltese (Malta)',
'mtr_IN' => 'Mewari (India)',
'mup_IN' => 'Malvi (India)',
'muw_IN' => 'Mundari (India)',
'my_MM' => 'Burmese (Myanmar)',
'myv_RU' => 'Erzya (Russia)',
'na_NR' => 'Nauru (Nauru)',
'nb_NO' => 'Norwegian Bokmal (Norway)',
'nb_SJ' => 'Norwegian Bokmal (Svalbard and Jan Mayen)',
'nd_ZW' => 'North Ndebele (Zimbabwe)',
'ndc_MZ' => 'Ndau (Mozambique)',
'ne_IN' => 'Nepali (India)',
'ne_NP' => 'Nepali (Nepal)',
'ng_NA' => 'Ndonga (Namibia)',
'ngl_MZ' => 'Lomwe (Mozambique)',
'niu_NU' => 'Niuean (Niue)',
'nl_AN' => 'Dutch (Netherlands Antilles)',
'nl_AW' => 'Dutch (Aruba)',
'nl_BE' => 'Dutch (Belgium)',
'nl_NL' => 'Dutch (Netherlands)',
'nl_SR' => 'Dutch (Suriname)',
'nn_NO' => 'Norwegian Nynorsk (Norway)',
'nb_NO' => 'Norwegian',
'nod_TH' => 'Thai, Northern (Thailand)',
'noe_IN' => 'Nimadi (India)',
'nso_ZA' => 'Northern Sotho (South Africa)',
'ny_MW' => 'Nyanja (Malawi)',
'ny_ZM' => 'Nyanja (Zambia)',
'nyn_UG' => 'Nyankole (Uganda)',
'om_ET' => 'Oromo (Ethiopia)',
'or_IN' => 'Oriya (India)',
'pa_IN' => 'Punjabi (India)',
'pag_PH' => 'Pangasinan (Philippines)',
'pap_AN' => 'Papiamento (Netherlands Antilles)',
'pap_AW' => 'Papiamento (Aruba)',
'pau_PW' => 'Palauan (Palau)',
'pl_PL' => 'Polish (Poland)',
'pl_UA' => 'Polish (Ukraine)',
'pon_FM' => 'Pohnpeian (Micronesia)',
'ps_AF' => 'Pashto (Afghanistan)',
'ps_PK' => 'Pashto (Pakistan)',
'pt_AO' => 'Portuguese (Angola)',
'pt_BR' => 'Portuguese (Brazil)',
'pt_CV' => 'Portuguese (Cape Verde)',
'pt_GW' => 'Portuguese (Guinea-Bissau)',
'pt_MZ' => 'Portuguese (Mozambique)',
'pt_PT' => 'Portuguese (Portugal)',
'pt_ST' => 'Portuguese (Sao Tome and Principe)',
'pt_TL' => 'Portuguese (East Timor)',
'qu_BO' => 'Quechua (Bolivia)',
'qu_PE' => 'Quechua (Peru)',
'rcf_RE' => 'R<>union Creole French (Reunion)',
'rej_ID' => 'Rejang (Indonesia)',
'rif_MA' => 'Tarifit (Morocco)',
'rjb_IN' => 'Rajbanshi (India)',
'rm_CH' => 'Rhaeto-Romance (Switzerland)',
'rmt_IR' => 'Domari (Iran)',
'rn_BI' => 'Rundi (Burundi)',
'ro_MD' => 'Romanian (Moldova)',
'ro_RO' => 'Romanian (Romania)',
'ro_RS' => 'Romanian (Serbia)',
'ru_BY' => 'Russian (Belarus)',
'ru_KG' => 'Russian (Kyrgyzstan)',
'ru_KZ' => 'Russian (Kazakhstan)',
'ru_RU' => 'Russian (Russia)',
'ru_SJ' => 'Russian (Svalbard and Jan Mayen)',
'ru_UA' => 'Russian (Ukraine)',
'rw_RW' => 'Kinyarwanda (Rwanda)',
'sa_IN' => 'Sanskrit (India)',
'sah_RU' => 'Yakut (Russia)',
'sas_ID' => 'Sasak (Indonesia)',
'sat_IN' => 'Santali (India)',
'sck_IN' => 'Sadri (India)',
'sco_GB' => 'Scots (United Kingdom)',
'sco_SCO' => 'Scots',
'sd_IN' => 'Sindhi (India)',
'sd_PK' => 'Sindhi (Pakistan)',
'se_NO' => 'Northern Sami (Norway)',
'sg_CF' => 'Sango (Central African Republic)',
'si_LK' => 'Sinhalese (Sri Lanka)',
'sid_ET' => 'Sidamo (Ethiopia)',
'sk_RS' => 'Slovak (Serbia)',
'sk_SK' => 'Slovak (Slovakia)',
'sl_AT' => 'Slovenian (Austria)',
'sl_SI' => 'Slovenian (Slovenia)',
'sm_AS' => 'Samoan (American Samoa)',
'sm_WS' => 'Samoan (Samoa)',
'sn_ZW' => 'Shona (Zimbabwe)',
'so_DJ' => 'Somali (Djibouti)',
'so_ET' => 'Somali (Ethiopia)',
'so_SO' => 'Somali (Somalia)',
'sou_TH' => 'Thai, Southern (Thailand)',
'sq_AL' => 'Albanian (Albania)',
'sr_BA' => 'Serbian (Bosnia and Herzegovina)',
'sr_ME' => 'Serbian (Montenegro)',
'sr_RS' => 'Serbian (Serbia)',
'ss_SZ' => 'Swati (Swaziland)',
'ss_ZA' => 'Swati (South Africa)',
'st_LS' => 'Southern Sotho (Lesotho)',
'st_ZA' => 'Southern Sotho (South Africa)',
'su_ID' => 'Sundanese (Indonesia)',
'sv_AX' => 'Swedish (Aland Islands)',
'sv_FI' => 'Swedish (Finland)',
'sv_SE' => 'Swedish (Sweden)',
'sw_KE' => 'Swahili (Kenya)',
'sw_SO' => 'Swahili (Somalia)',
'sw_TZ' => 'Swahili (Tanzania)',
'sw_UG' => 'Swahili (Uganda)',
'swb_KM' => 'Comorian (Comoros)',
'swb_YT' => 'Comorian (Mayotte)',
'swv_IN' => 'Shekhawati (India)',
'ta_IN' => 'Tamil (India)',
'ta_LK' => 'Tamil (Sri Lanka)',
'ta_MY' => 'Tamil (Malaysia)',
'ta_SG' => 'Tamil (Singapore)',
'tcy_IN' => 'Tulu (India)',
'te_IN' => 'Telugu (India)',
'tet_TL' => 'Tetum (East Timor)',
'tg_TJ' => 'Tajik (Tajikistan)',
'th_TH' => 'Thai (Thailand)',
'ti_ER' => 'Tigrinya (Eritrea)',
'ti_ET' => 'Tigrinya (Ethiopia)',
'tk_IR' => 'Turkmen (Iran)',
'tk_TM' => 'Turkmen (Turkmenistan)',
'tkl_TK' => 'Tokelau (Tokelau)',
'tl_PH' => 'Tagalog (Philippines)',
'tl_US' => 'Tagalog (United States)',
'tn_BW' => 'Tswana (Botswana)',
'tn_ZA' => 'Tswana (South Africa)',
'to_TO' => 'Tonga (Tonga)',
'tr_CY' => 'Turkish (Cyprus)',
'tr_DE' => 'Turkish (Germany)',
'tr_MK' => 'Turkish (Macedonia)',
'tr_TR' => 'Turkish (Turkey)',
'ts_MZ' => 'Tsonga (Mozambique)',
'ts_ZA' => 'Tsonga (South Africa)',
'tsg_PH' => 'Tausug (Philippines)',
'tt_RU' => 'Tatar (Russia)',
'tts_TH' => 'Thai, Northeastern (Thailand)',
'tvl_TV' => 'Tuvalu (Tuvalu)',
'tw_GH' => 'Twi (Ghana)',
'ty_PF' => 'Tahitian (French Polynesia)',
'tyv_RU' => 'Tuvinian (Russia)',
'tzm_MA' => 'Tamazight, Central Atlas (Morocco)',
'udm_RU' => 'Udmurt (Russia)',
'ug_CN' => 'Uighur (China)',
'uk_UA' => 'Ukrainian (Ukraine)',
'uli_FM' => 'Ulithian (Micronesia)',
'ur_IN' => 'Urdu (India)',
'ur_PK' => 'Urdu (Pakistan)',
'uz_AF' => 'Uzbek (Afghanistan)',
'uz_UZ' => 'Uzbek (Uzbekistan)',
've_ZA' => 'Venda (South Africa)',
'vi_US' => 'Vietnamese (United States)',
'vi_VN' => 'Vietnamese (Vietnam)',
'vmw_MZ' => 'Waddar (Mozambique)',
'wal_ET' => 'Walamo (Ethiopia)',
'war_PH' => 'Waray (Philippines)',
'wbq_IN' => 'Waddar (India)',
'wbr_IN' => 'Wagdi (India)',
'wo_MR' => 'Wolof (Mauritania)',
'wo_SN' => 'Wolof (Senegal)',
'wtm_IN' => 'Mewati (India)',
'xh_ZA' => 'Xhosa (South Africa)',
'xnr_IN' => 'Kangri (India)',
'xog_UG' => 'Soga (Uganda)',
'yap_FM' => 'Yapese (Micronesia)',
'yo_NG' => 'Yoruba (Nigeria)',
'za_CN' => 'Zhuang (China)',
'zh_CN' => 'Chinese (China)',
'zh_HK' => 'Chinese (Hong Kong SAR China)',
'zh_MO' => 'Chinese (Macao SAR China)',
'zh_SG' => 'Chinese (Singapore)',
'zh_TW' => 'Chinese (Taiwan)',
'zh_US' => 'Chinese (United States)',
'zh_cmn' => 'Chinese (Mandarin)',
'zh_yue' => 'Chinese (Cantonese)',
'zu_ZA' => 'Zulu (South Africa)'
);
/**
* @config
* @var array $common_languages A list of commonly used languages, in the form
@ -657,19 +657,19 @@ class i18n extends Object implements TemplateGlobalProvider {
*/
private static $common_languages = array(
'af' => array(
'name' => 'Afrikaans',
'name' => 'Afrikaans',
'native' => 'Afrikaans'
),
'sq' => array(
'name' => 'Albanian',
'name' => 'Albanian',
'native' => 'shqip'
),
'ar' => array(
'name' => 'Arabic',
'name' => 'Arabic',
'native' => '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'
),
'eu' => array(
'name' => 'Basque',
'name' => 'Basque',
'native' => 'euskera'
),
'be' => array(
@ -678,27 +678,27 @@ class i18n extends Object implements TemplateGlobalProvider {
'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'
),
'bn' => array(
'name' => 'Bengali',
'name' => 'Bengali',
'native' => '&#2476;&#2494;&#2434;&#2482;&#2494;'
),
'bg' => array(
'name' => 'Bulgarian',
'name' => 'Bulgarian',
'native' => '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'
),
'ca' => array(
'name' => 'Catalan',
'name' => 'Catalan',
'native' => 'catal&agrave;'
),
'zh_yue' => array(
'name' => 'Chinese (Cantonese)',
'name' => 'Chinese (Cantonese)',
'native' => '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'
),
'zh_cmn' => array(
'name' => 'Chinese (Mandarin)',
'name' => 'Chinese (Mandarin)',
'native' => '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'
),
'hr' => array(
'name' => 'Croatian',
'name' => 'Croatian',
'native' => 'Hrvatski'
),
'zh' => array(
@ -706,299 +706,299 @@ class i18n extends Object implements TemplateGlobalProvider {
'native' => '&#20013;&#22269;&#30340;'
),
'cs' => array(
'name' => 'Czech',
'name' => 'Czech',
'native' => '&#x010D;e&#353;tina'
),
'cy' => array(
'name' => 'Welsh',
'name' => 'Welsh',
'native' => 'Welsh/Cymraeg'
),
'da' => array(
'name' => 'Danish',
'name' => 'Danish',
'native' => 'dansk'
),
'nl' => array(
'name' => 'Dutch',
'name' => 'Dutch',
'native' => 'Nederlands'
),
'en' => array(
'name' => 'English',
'name' => 'English',
'native' => 'English'
),
'eo' => array(
'name' => 'Esperanto',
'name' => 'Esperanto',
'native' => 'Esperanto'
),
'et' => array(
'name' => 'Estonian',
'name' => 'Estonian',
'native' => 'eesti keel'
),
'fo' => array(
'name' => 'Faroese',
'name' => 'Faroese',
'native' => 'F&oslash;royska'
),
'fi' => array(
'name' => 'Finnish',
'name' => 'Finnish',
'native' => 'suomi'
),
'fr' => array(
'name' => 'French',
'name' => 'French',
'native' => 'fran&ccedil;ais'
),
'gd' => array(
'name' => 'Gaelic',
'name' => 'Gaelic',
'native' => 'Gaeilge'
),
'gl' => array(
'name' => 'Galician',
'name' => 'Galician',
'native' => 'Galego'
),
'de' => array(
'name' => 'German',
'name' => 'German',
'native' => 'Deutsch'
),
'el' => array(
'name' => 'Greek',
'name' => 'Greek',
'native' => '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'
),
'gu' => array(
'name' => 'Gujarati',
'name' => 'Gujarati',
'native' => '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'
),
'ha' => array(
'name' => 'Hausa',
'name' => 'Hausa',
'native' => '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'
),
'he' => array(
'name' => 'Hebrew',
'name' => 'Hebrew',
'native' => '&#1506;&#1489;&#1512;&#1497;&#1514;'
),
'hi' => array(
'name' => 'Hindi',
'name' => 'Hindi',
'native' => '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'
),
'hu' => array(
'name' => 'Hungarian',
'name' => 'Hungarian',
'native' => 'magyar'
),
'is' => array(
'name' => 'Icelandic',
'name' => 'Icelandic',
'native' => '&Iacute;slenska'
),
'io' => array(
'name' => 'Ido',
'name' => 'Ido',
'native' => 'Ido'
),
'id' => array(
'name' => 'Indonesian',
'name' => 'Indonesian',
'native' => 'Bahasa Indonesia'
),
'ga' => array(
'name' => 'Irish',
'name' => 'Irish',
'native' => 'Irish'
),
'it' => array(
'name' => 'Italian',
'name' => 'Italian',
'native' => 'italiano'
),
'ja' => array(
'name' => 'Japanese',
'name' => 'Japanese',
'native' => '&#26085;&#26412;&#35486;'
),
'jv' => array(
'name' => 'Javanese',
'name' => 'Javanese',
'native' => 'basa Jawa'
),
'ko' => array(
'name' => 'Korean',
'name' => 'Korean',
'native' => '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'
),
'ku' => array(
'name' => 'Kurdish',
'name' => 'Kurdish',
'native' => 'Kurd&iacute;'
),
'lv' => array(
'name' => 'Latvian',
'name' => 'Latvian',
'native' => 'latvie&#353;u'
),
'lt' => array(
'name' => 'Lithuanian',
'name' => 'Lithuanian',
'native' => 'lietuvi&#353;kai'
),
'lmo' => array(
'name' => 'Lombard',
'name' => 'Lombard',
'native' => 'Lombardo'
),
'mk' => array(
'name' => 'Macedonian',
'name' => 'Macedonian',
'native' => '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'
),
'mi' => array(
'name' => 'Māori',
'native' => 'Māori'
'name' => 'te reo Māori',
'native' => 'te reo Māori'
),
'ms' => array(
'name' => 'Malay',
'name' => 'Malay',
'native' => 'Bahasa melayu'
),
'mt' => array(
'name' => 'Maltese',
'name' => 'Maltese',
'native' => 'Malti'
),
'mr' => array(
'name' => 'Marathi',
'name' => 'Marathi',
'native' => '&#2350;&#2352;&#2366;&#2336;&#2368;'
),
'ne' => array(
'name' => 'Nepali',
'name' => 'Nepali',
'native' => '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'
),
'nb' => array(
'name' => 'Norwegian',
'name' => 'Norwegian',
'native' => 'Norsk'
),
'om' => array(
'name' => 'Oromo',
'name' => 'Oromo',
'native' => 'Afaan Oromo'
),
'fa' => array(
'name' => 'Persian',
'name' => 'Persian',
'native' => '&#1601;&#1575;&#1585;&#1587;&#1609;'
),
'pl' => array(
'name' => 'Polish',
'name' => 'Polish',
'native' => 'polski'
),
'pt_PT' => array(
'name' => 'Portuguese (Portugal)',
'name' => 'Portuguese (Portugal)',
'native' => 'portugu&ecirc;s (Portugal)'
),
'pt_BR' => array(
'name' => 'Portuguese (Brazil)',
'name' => 'Portuguese (Brazil)',
'native' => 'portugu&ecirc;s (Brazil)'
),
'pa' => array(
'name' => 'Punjabi',
'name' => 'Punjabi',
'native' => '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'
),
'qu' => array(
'name' => 'Quechua',
'name' => 'Quechua',
'native' => 'Quechua'
),
'rm' => array(
'name' => 'Romansh',
'name' => 'Romansh',
'native' => 'rumantsch'
),
'ro' => array(
'name' => 'Romanian',
'name' => 'Romanian',
'native' => 'rom&acirc;n'
),
'ru' => array(
'name' => 'Russian',
'name' => 'Russian',
'native' => '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'
),
'sco' => array(
'name' => 'Scots',
'name' => 'Scots',
'native' => 'Scoats leid, Lallans'
),
'sr' => array(
'name' => 'Serbian',
'name' => 'Serbian',
'native' => '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'
),
'sk' => array(
'name' => 'Slovak',
'name' => 'Slovak',
'native' => 'sloven&#269;ina'
),
'sl' => array(
'name' => 'Slovenian',
'name' => 'Slovenian',
'native' => 'sloven&#353;&#269;ina'
),
'es' => array(
'name' => 'Spanish',
'name' => 'Spanish',
'native' => 'espa&ntilde;ol'
),
'sv' => array(
'name' => 'Swedish',
'name' => 'Swedish',
'native' => 'Svenska'
),
'tl' => array(
'name' => 'Tagalog',
'name' => 'Tagalog',
'native' => 'Tagalog'
),
'ta' => array(
'name' => 'Tamil',
'name' => 'Tamil',
'native' => '&#2980;&#2990;&#3007;&#2996;&#3021;'
),
'te' => array(
'name' => 'Telugu',
'name' => 'Telugu',
'native' => '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'
),
'to' => array(
'name' => 'Tonga',
'name' => 'Tonga',
'native' => 'chiTonga'
),
'ts' => array(
'name' => 'Tsonga',
'name' => 'Tsonga',
'native' => 'xiTshonga'
),
'tn' => array(
'name' => 'Tswana',
'name' => 'Tswana',
'native' => 'seTswana'
),
'tr' => array(
'name' => 'Turkish',
'name' => 'Turkish',
'native' => 'T&uuml;rk&ccedil;e'
),
'tk' => array(
'name' => 'Turkmen',
'name' => 'Turkmen',
'native' => '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'
),
'tw' => array(
'name' => 'Twi',
'name' => 'Twi',
'native' => 'twi'
),
'uk' => array(
'name' => 'Ukrainian',
'name' => 'Ukrainian',
'native' => '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'
),
'ur' => array(
'name' => 'Urdu',
'name' => 'Urdu',
'native' => '&#1575;&#1585;&#1583;&#1608;'
),
'uz' => array(
'name' => 'Uzbek',
'name' => 'Uzbek',
'native' => '&#1118;&#1079;&#1073;&#1077;&#1082;'
),
've' => array(
'name' => 'Venda',
'name' => 'Venda',
'native' => 'tshiVen&#x1E13;a'
),
'vi' => array(
'name' => 'Vietnamese',
'name' => 'Vietnamese',
'native' => 'ti&#7871;ng vi&#7879;t'
),
'wa' => array(
'name' => 'Walloon',
'name' => 'Walloon',
'native' => 'walon'
),
'wo' => array(
'name' => 'Wolof',
'name' => 'Wolof',
'native' => 'Wollof'
),
'xh' => array(
'name' => 'Xhosa',
'name' => 'Xhosa',
'native' => 'isiXhosa'
),
'yi' => array(
'name' => 'Yiddish',
'name' => 'Yiddish',
'native' => '&#1522;&#1460;&#1491;&#1497;&#1513;'
),
'zu' => array(
'name' => 'Zulu',
'name' => 'Zulu',
'native' => 'isiZulu'
)
);
/**
* @config
* @var array $common_locales
@ -1184,8 +1184,8 @@ class i18n extends Object implements TemplateGlobalProvider {
'native' => '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'
),
'mi_NZ' => array(
'name' => 'Māori',
'native' => 'Māori'
'name' => 'te reo Māori',
'native' => 'te reo Māori'
),
'ms_MY' => array(
'name' => 'Malay',
@ -1340,7 +1340,7 @@ class i18n extends Object implements TemplateGlobalProvider {
'native' => 'isiZulu'
),
);
/**
* @config
* @var array
@ -1508,7 +1508,7 @@ class i18n extends Object implements TemplateGlobalProvider {
'zh_US' => 'zn-cn',
);
/**
* @config
* @var array $likely_subtags Provides you "likely locales"
@ -1978,7 +1978,7 @@ class i18n extends Object implements TemplateGlobalProvider {
'zh_TW' => 'zh_TW',
'zu' => 'zu_ZA',
);
/**
* This is the main translator function. Returns the string defined by $class and $entity according to the
* currently set locale.
@ -2058,7 +2058,7 @@ class i18n extends Object implements TemplateGlobalProvider {
if($injectionArray) {
$regex = '/\{[\w\d]*\}/i';
if(!preg_match($regex, $returnValue)) {
// Legacy mode: If no injection placeholders are found,
// Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order.
// Fail silently in case the translation is outdated
preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs);
@ -2067,13 +2067,13 @@ class i18n extends Object implements TemplateGlobalProvider {
$injectionArray[] = '';
}
}
$replaced = vsprintf($returnValue, array_values($injectionArray));
$replaced = vsprintf($returnValue, array_values($injectionArray));
if($replaced) $returnValue = $replaced;
} else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found,
// but parameters are passed without names, replace them in fixed order.
$returnValue = preg_replace_callback(
$regex,
$regex,
function($matches) use(&$injectionArray) {
return $injectionArray ? array_shift($injectionArray) : '';
},
@ -2123,10 +2123,10 @@ class i18n extends Object implements TemplateGlobalProvider {
i18n::include_by_locale('en', isset($_GET['flush']));
i18n::include_by_locale('en_US', isset($_GET['flush']));
}
return self::$translators;
}
/**
* @param String
* @return Zend_Translate
@ -2137,7 +2137,7 @@ class i18n extends Object implements TemplateGlobalProvider {
}
return false;
}
/**
* @param Zend_Translate Needs to implement {@link i18nTranslateAdapterInterface}
* @param String If left blank will override the default translator.
@ -2152,14 +2152,14 @@ class i18n extends Object implements TemplateGlobalProvider {
// Add our new translator
if(!isset(self::$translators[$priority])) self::$translators[$priority] = array();
self::$translators[$priority][$name] = $translator;
// Resort array, ensuring highest priority comes first
krsort(self::$translators);
i18n::include_by_locale('en_US');
i18n::include_by_locale('en');
}
/**
* @param String
*/
@ -2182,7 +2182,7 @@ class i18n extends Object implements TemplateGlobalProvider {
}
return $languages;
}
/**
* Get a list of commonly used locales
*
@ -2196,7 +2196,7 @@ class i18n extends Object implements TemplateGlobalProvider {
}
return $languages;
}
/**
* Get a list of locales (code => language and country)
*
@ -2206,41 +2206,41 @@ class i18n extends Object implements TemplateGlobalProvider {
Deprecation::notice('3.2', 'Use the "i18n.all_locales" config setting instead');
return (array)Config::inst()->get('i18n', 'all_locales');
}
/**
* Matches a given locale with the closest translation available in the system
*
*
* @param string $locale locale code
* @return string Locale of closest available translation, if available
*/
public static function get_closest_translation($locale) {
// Check if exact match
$pool = self::get_existing_translations();
if(isset($pool[$locale])) return $locale;
// Fallback to best locale for common language
$lang = self::get_lang_from_locale($locale);
$candidate = self::get_locale_from_lang($lang);
if(isset($pool[$candidate])) return $candidate;
}
/**
* Searches the root-directory for module-directories
* (identified by having a _config.php on their first directory-level).
* Finds locales by filename convention ("<locale>.<extension>", e.g. "de_AT.yml").
*
*
* @return array
*/
public static function get_existing_translations() {
$locales = array();
// TODO Inspect themes
$modules = SS_ClassLoader::instance()->getManifest()->getModules();
foreach($modules as $module) {
if(!file_exists("{$module}/lang/")) continue;
$allLocales = Config::inst()->get('i18n', 'all_locales');
$moduleLocales = scandir("{$module}/lang/");
foreach($moduleLocales as $moduleLocale) {
@ -2253,7 +2253,7 @@ class i18n extends Object implements TemplateGlobalProvider {
// if the locale doesn't exist in Zend's CLDR data
$fullLocale = self::get_locale_from_lang($locale);
if(isset($allLocales[$fullLocale])) {
$locales[$fullLocale] = $allLocales[$fullLocale];
$locales[$fullLocale] = $allLocales[$fullLocale];
}
}
}
@ -2261,13 +2261,13 @@ class i18n extends Object implements TemplateGlobalProvider {
// sort by title (not locale)
asort($locales);
return $locales;
}
/**
* Get a name from a language code (two characters, e.g. "en").
*
*
* @see get_locale_name()
*
* @param mixed $code Language code
@ -2282,10 +2282,10 @@ class i18n extends Object implements TemplateGlobalProvider {
return (isset($langs[$code]['name'])) ? $langs[$code]['name'] : false;
}
}
/**
* Get a name from a locale code (xx_YY).
*
*
* @see get_language_name()
*
* @param mixed $code locale code
@ -2295,7 +2295,7 @@ class i18n extends Object implements TemplateGlobalProvider {
$langs = self::config()->all_locales;
return isset($langs[$code]) ? $langs[$code] : false;
}
/**
* Get a code from an English language name
*
@ -2306,10 +2306,10 @@ class i18n extends Object implements TemplateGlobalProvider {
$code = array_search($name,self::get_common_languages());
return ($code ? $code : $name);
}
/**
* Get the current tinyMCE language
*
*
* @return Language
*/
public static function get_tinymce_lang() {
@ -2317,28 +2317,28 @@ class i18n extends Object implements TemplateGlobalProvider {
if(isset($lang[self::get_locale()])) {
return $lang[self::get_locale()];
}
return 'en';
}
/**
* Searches the root-directory for module-directories
* (identified by having a _config.php on their first directory-level
* and a language-file with the default locale in the /lang-subdirectory).
*
*
* @return array
*/
public static function get_translatable_modules() {
$translatableModules = array();
$baseDir = Director::baseFolder();
$modules = scandir($baseDir);
foreach($modules as $module) {
$moduleDir = $baseDir . DIRECTORY_SEPARATOR . $module;
if(
is_dir($moduleDir)
is_dir($moduleDir)
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php")
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR
. self::$default_locale . ".php")
) {
$translatableModules[] = $module;
@ -2346,18 +2346,18 @@ class i18n extends Object implements TemplateGlobalProvider {
}
return $translatableModules;
}
/**
* Returns the "short" language name from a locale,
* e.g. "en_US" would return "en".
*
* e.g. "en_US" would return "en".
*
* @param string $locale E.g. "en_US"
* @return string Short language code, e.g. "en"
*/
public static function get_lang_from_locale($locale) {
return preg_replace('/(_|-).*/', '', $locale);
}
/**
* Provides you "likely locales"
* for a given "short" language code. This is a guess,
@ -2365,7 +2365,7 @@ class i18n extends Object implements TemplateGlobalProvider {
* could also mean "en_UK". Based on the Unicode CLDR
* project.
* @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
*
*
* @param string $lang Short language code, e.g. "en"
* @return string Long locale, e.g. "en_US"
*/
@ -2379,24 +2379,24 @@ class i18n extends Object implements TemplateGlobalProvider {
return $lang . '_' . strtoupper($lang);
}
}
/**
* Gets a RFC 1766 compatible language code,
* e.g. "en-US".
*
* @see http://www.ietf.org/rfc/rfc1766.txt
* @see http://tools.ietf.org/html/rfc2616#section-3.10
*
*
* @param string $locale
* @return string
*/
public static function convert_rfc1766($locale) {
return str_replace('_','-', $locale);
}
/**
* Given a PHP class name, finds the module where it's located.
*
*
* @param string $name
* @return string
*/
@ -2407,7 +2407,7 @@ class i18n extends Object implements TemplateGlobalProvider {
if (!$path) {
return false;
}
$path = Director::makeRelative($path);
$path = str_replace('\\', '/', $path);
@ -2418,15 +2418,15 @@ class i18n extends Object implements TemplateGlobalProvider {
/**
* Validates a "long" locale format (e.g. "en_US")
* by checking it against {@link $all_locales}.
*
*
* To add a locale to {@link $all_locales}, use the following example
* in your mysite/_config.php:
* <code>
* i18n::$allowed_locales['xx_XX'] = '<Language name>';
* </code>
*
*
* Note: Does not check for {@link $allowed_locales}.
*
*
* @return boolean
*/
public static function validate_locale($locale) {
@ -2436,12 +2436,12 @@ class i18n extends Object implements TemplateGlobalProvider {
}
/**
* Set the current locale, used as the default for
* Set the current locale, used as the default for
* any localized classes, such as {@link FormField} or {@link DBField}
* instances. Locales can also be persisted in {@link Member->Locale},
* for example in the {@link CMSMain} interface the Member locale
* overrules the global locale value set here.
*
*
* @param string $locale Locale to be set. See
* http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list
* of possible locales.
@ -2453,33 +2453,33 @@ class i18n extends Object implements TemplateGlobalProvider {
/**
* Get the current locale.
* Used by {@link Member::populateDefaults()}
*
*
* @return string Current locale in the system
*/
public static function get_locale() {
return (!empty(self::$current_locale)) ? self::$current_locale : self::$default_locale;
}
/**
* This is the "fallback locale", in case resources with the "current locale"
* (set through {@link set_locale()}) can't be found.
*
*
* If you just want to globally read/write a different locale (e.g. in a CMS interface),
* please use {@link get_locale()} and {@link set_locale()} instead.
*
*
* For example, {@link Requirements::add_i18n_javascript()} and {@link i18n::include_by_class()}
* use this "fallback locale" value to include fallback language files.
*
*
* @return String
*/
public static function default_locale() {
return self::$default_locale;
}
/**
* See {@link default_locale()} for usage.
*
*
*
*
* @param String $locale
*/
public static function set_default_locale($locale) {
@ -2501,10 +2501,10 @@ class i18n extends Object implements TemplateGlobalProvider {
} catch(Zend_Locale_Exception $e) {
$dir = Zend_Locale_Data::getList(i18n::get_lang_from_locale($locale), 'layout');
}
return ($dir && $dir['characters'] == 'right-to-left') ? 'rtl' : 'ltr';
}
/**
* Includes all available language files for a certain defined locale.
*
@ -2515,7 +2515,7 @@ class i18n extends Object implements TemplateGlobalProvider {
if($clean) {
Zend_Translate::clearCache();
}
// Get list of module => path pairs, and then just the names
$modules = SS_ClassLoader::instance()->getManifest()->getModules();
$moduleNames = array_keys($modules);
@ -2579,13 +2579,13 @@ class i18n extends Object implements TemplateGlobalProvider {
}
}
}
// Add empty translations to ensure the locales are "registered" with isAvailable(),
// and the next invocation of include_by_locale() doesn't cause a new reparse.
$adapter->addTranslation(
array(
// Cached by content hash, so needs to be locale dependent
'content' => array($locale => $locale),
'content' => array($locale => $locale),
'locale' => $locale,
'usetranslateadapter' => true
)
@ -2598,12 +2598,12 @@ class i18n extends Object implements TemplateGlobalProvider {
* Given a class name (a "locale namespace"), will search for its module and, if available,
* will load the resources for the currently defined locale.
* If not available, the original English resource will be loaded instead (to avoid blanks)
*
*
* @param string $class Resources for this class will be included, according to the set locale.
*/
public static function include_by_class($class) {
$module = self::get_owner_module($class);
$translators = array_reverse(self::get_translators(), true);
foreach($translators as $priority => $translators) {
foreach($translators as $name => $translator) {
@ -2626,5 +2626,5 @@ class i18n extends Object implements TemplateGlobalProvider {
'i18nScriptDirection' => 'get_script_direction',
);
}
}

View File

@ -8,7 +8,7 @@
*/
reload: function(ajaxOpts, successCallback) {
var self = this, form = this.closest('form'),
var self = this, form = this.closest('form'),
focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh
data = form.find(':input').serializeArray();
@ -23,7 +23,7 @@
ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data);
}
// For browsers which do not support history.pushState like IE9, ss framework uses hash to track
// For browsers which do not support history.pushState like IE9, ss framework uses hash to track
// the current location for PJAX, so for them we pass the query string stored in the hash instead
if(!window.history || !window.history.pushState){
if(window.location.hash && window.location.hash.indexOf('?') != -1){
@ -48,15 +48,15 @@
// multiple relationships via keyboard.
if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus();
// Update filter
// Update filter
if(self.find('.filter-header').length) {
var content;
if(ajaxOpts.data[0].filter=="show") {
content = '<span class="non-sortable"></span>';
self.addClass('show-filter').find('.filter-header').show();
self.addClass('show-filter').find('.filter-header').show();
} else {
content = '<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>';
self.removeClass('show-filter').find('.filter-header').hide();
self.removeClass('show-filter').find('.filter-header').hide();
}
self.find('.sortable-header th:last').html(content);
@ -104,7 +104,7 @@
$('.ss-gridfield :button[name=showFilter]').entwine({
onclick: function(e) {
onclick: function(e) {
$('.filter-header')
.show('slow') // animate visibility
.find(':input:first').focus(); // focus first search field
@ -198,11 +198,13 @@
$('.ss-gridfield-print-iframe').entwine({
onmatch: function(){
this._super();
this.hide().bind('load', function() {
this.focus();
var ifWin = this.contentWindow || this;
ifWin.print();
});;
});
},
onunmatch: function() {
this._super();
@ -268,15 +270,15 @@
}
});
$('.ss-gridfield[data-selectable] .ss-gridfield-items').entwine({
onmatch: function() {
onadd: function() {
this._super();
// TODO Limit to single selection
this.selectable();
},
onunmatch: function() {
onremove: function() {
this._super();
this.selectable('destroy');
if (this.data('selectable')) this.selectable('destroy');
}
});

View File

@ -74,7 +74,7 @@ ss.editorWrappers.tinyMCE = (function() {
this.statusKeyboardNavigation.destroy();
this.statusKeyboardNavigation = null;
}
}
};
ss.editorWrappers.tinyMCE.patched = true;
}
@ -89,15 +89,22 @@ ss.editorWrappers.tinyMCE = (function() {
// after an (undetected) inline change. This "blur" causes onChange
// to trigger, which will change the button markup to show "alternative" styles,
// effectively cancelling the original click event.
var interval;
jQuery(ed.getBody()).on('focus', function() {
interval = setInterval(function() {
ed.save();
}, 5000);
});
jQuery(ed.getBody()).on('blur', function() {
clearInterval(interval);
});
if(ed.settings.update_interval) {
var interval;
jQuery(ed.getBody()).on('focus', function() {
interval = setInterval(function() {
// Update underlying element as necessary
var element = jQuery(ed.getElement());
if(ed.isDirty()) {
// Set content without triggering editor content cleanup
element.val(ed.getContent({format : 'raw', no_events : 1}));
}
}, ed.settings.update_interval);
});
jQuery(ed.getBody()).on('blur', function() {
clearInterval(interval);
});
}
});
this.instance.onChange.add(function(ed, l) {
// Update underlying textarea on every change, so external handlers
@ -536,6 +543,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
* which are toggled through a type dropdown. Variations share fields, so there's only one "title" field in the form.
*/
$('form.htmleditorfield-linkform').entwine({
// TODO Entwine doesn't respect submits triggered by ENTER key
onsubmit: function(e) {
this.insertLink();
@ -551,8 +559,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
redraw: function() {
this._super();
var linkType = this.find(':input[name=LinkType]:checked').val(),
list = ['internal', 'external', 'file', 'email'];
var linkType = this.find(':input[name=LinkType]:checked').val();
this.addAnchorSelector();
@ -571,7 +578,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
if(linkType == 'anchor') {
this.find('.field[id$="AnchorSelector_Holder"]').show();
this.find('.field[id$="AnchorRefresh_Holder"]').show();
}
this.find('.field[id$="Description_Holder"]').show();
},
@ -622,8 +628,8 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
}
return {
href : href,
target : target,
href : href,
target : target,
title : this.find(':input[name=Description]').val()
};
},
@ -641,63 +647,135 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
this.close();
},
/**
* Builds an anchor selector element and injects it into the DOM next to the anchor field.
*/
addAnchorSelector: function() {
// Avoid adding twice
if(this.find(':input[name=AnchorSelector]').length) return;
var self = this, anchorSelector;
var self = this;
var anchorSelector = $(
'<select id="Form_EditorToolbarLinkForm_AnchorSelector" name="AnchorSelector"></select>'
);
this.find(':input[name=Anchor]').parent().append(anchorSelector);
// refresh the anchor selector on click, or in case of IE - button click
if( !$.browser.ie ) {
anchorSelector = $('<select id="Form_EditorToolbarLinkForm_AnchorSelector" name="AnchorSelector"></select>');
this.find(':input[name=Anchor]').parent().append(anchorSelector);
anchorSelector.focus(function(e) {
self.refreshAnchors();
});
} else {
var buttonRefresh = $('<a id="Form_EditorToolbarLinkForm_AnchorRefresh" title="Refresh the anchor list" alt="Refresh the anchor list" class="buttonRefresh"><span></span></a>');
anchorSelector = $('<select id="Form_EditorToolbarLinkForm_AnchorSelector" class="hasRefreshButton" name="AnchorSelector"></select>');
this.find(':input[name=Anchor]').parent().append(buttonRefresh).append(anchorSelector);
buttonRefresh.click(function(e) {
self.refreshAnchors();
});
}
// initialization
self.refreshAnchors();
// Initialise the anchor dropdown.
this.updateAnchorSelector();
// copy the value from dropdown to the text field
anchorSelector.change(function(e) {
self.find(':input[name="Anchor"]').val($(this).val());
});
},
// this function collects the anchors in the currently active editor and regenerates the dropdown
refreshAnchors: function() {
var selector = this.find(':input[name=AnchorSelector]'), anchors = [], ed = this.getEditor();
// name attribute is defined as CDATA, should accept all characters and entities
// http://www.w3.org/TR/1999/REC-html401-19991224/struct/links.html#h-12.2
if(ed) {
var raw = ed.getContent().match(/name="([^"]+?)"|name='([^']+?)'/gim);
if (raw && raw.length) {
for(var i = 0; i < raw.length; i++) {
anchors.push(raw[i].substr(6).replace(/"$/, ''));
/**
* Fetch relevant anchors, depending on the link type.
*
* @return $.Deferred A promise of an anchor array, or an error message.
*/
getAnchors: function() {
var linkType = this.find(':input[name=LinkType]:checked').val();
var dfdAnchors = $.Deferred();
switch (linkType) {
case 'anchor':
// Fetch from the local editor.
var collectedAnchors = [];
var ed = this.getEditor();
// name attribute is defined as CDATA, should accept all characters and entities
// http://www.w3.org/TR/1999/REC-html401-19991224/struct/links.html#h-12.2
if(ed) {
var raw = ed.getContent().match(/name="([^"]+?)"|name='([^']+?)'/gim);
if (raw && raw.length) {
for(var i = 0; i < raw.length; i++) {
collectedAnchors.push(raw[i].substr(6).replace(/"$/, ''));
}
}
}
}
dfdAnchors.resolve(collectedAnchors);
break;
case 'internal':
// Fetch available anchors from the target internal page.
var pageId = this.find(':input[name=internal]').val();
if (pageId) {
$.ajax({
url: $.path.addSearchParams(
this.attr('action').replace('LinkForm', 'getanchors'),
{'PageID': parseInt(pageId)}
),
success: function(body, status, xhr) {
dfdAnchors.resolve($.parseJSON(body));
},
error: function(xhr, status) {
dfdAnchors.reject(xhr.responseText);
}
});
} else {
dfdAnchors.resolve([]);
}
break;
default:
// This type does not support anchors at all.
dfdAnchors.reject(ss.i18n._t(
'HtmlEditorField.ANCHORSNOTSUPPORTED',
'Anchors are not supported for this link type.'
));
break;
}
return dfdAnchors.promise();
},
/**
* Update the anchor list in the dropdown.
*/
updateAnchorSelector: function() {
var self = this;
var selector = this.find(':input[name=AnchorSelector]');
var dfdAnchors = this.getAnchors();
// Inform the user we are loading.
selector.empty();
selector.append($(
'<option value="" selected="1">' +
ss.i18n._t('HtmlEditorField.SelectAnchor') +
ss.i18n._t('HtmlEditorField.LOOKINGFORANCHORS', 'Looking for anchors...') +
'</option>'
));
for (var j = 0; j < anchors.length; j++) {
selector.append($('<option value="'+anchors[j]+'">'+anchors[j]+'</option>'));
}
dfdAnchors.done(function(anchors) {
selector.empty();
selector.append($(
'<option value="" selected="1">' +
ss.i18n._t('HtmlEditorField.SelectAnchor') +
'</option>'
));
if (anchors) {
for (var j = 0; j < anchors.length; j++) {
selector.append($('<option value="'+anchors[j]+'">'+anchors[j]+'</option>'));
}
}
}).fail(function(message) {
selector.empty();
selector.append($(
'<option value="" selected="1">' +
message +
'</option>'
));
});
// Poke the selector for IE8, otherwise the changes won't be noticed.
if ($.browser.msie) selector.hide().show();
},
/**
* Updates the state of the dialog inputs to match the editor selection.
* If selection does not contain a link, resets the fields.
@ -722,103 +800,123 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
}
}
},
/**
* Return information about the currently selected link, suitable for population of the link form.
*
* Returns null if no link was currently selected.
*/
getCurrentLink: function() {
var selectedEl = this.getSelection(),
href = "", target = "", title = "", action = "insert", style_class = "";
// We use a separate field for linkDataSource from tinyMCE.linkElement.
// If we have selected beyond the range of an <a> element, then use use that <a> element to get the link data source,
// but we don't use it as the destination for the link insertion
var linkDataSource = null;
if(selectedEl.length) {
if(selectedEl.is('a')) {
// Element is a link
linkDataSource = selectedEl;
// TODO Limit to inline elements, otherwise will also apply to e.g. paragraphs which already contain one or more links
// } else if((selectedEl.find('a').length)) {
// // Element contains a link
// var firstLinkEl = selectedEl.find('a:first');
// if(firstLinkEl.length) linkDataSource = firstLinkEl;
} else {
// Element is a child of a link
linkDataSource = selectedEl = selectedEl.parents('a:first');
}
}
if(linkDataSource && linkDataSource.length) this.modifySelection(function(ed){
ed.selectNode(linkDataSource[0]);
});
// Is anchor not a link
if (!linkDataSource.attr('href')) linkDataSource = null;
if (linkDataSource) {
href = linkDataSource.attr('href');
target = linkDataSource.attr('target');
title = linkDataSource.attr('title');
style_class = linkDataSource.attr('class');
href = this.getEditor().cleanLink(href, linkDataSource);
action = "update";
/**
* Return information about the currently selected link, suitable for population of the link form.
*
* Returns null if no link was currently selected.
*/
getCurrentLink: function() {
var selectedEl = this.getSelection(),
href = "", target = "", title = "", action = "insert", style_class = "";
// We use a separate field for linkDataSource from tinyMCE.linkElement.
// If we have selected beyond the range of an <a> element, then use use that <a> element to get the link data source,
// but we don't use it as the destination for the link insertion
var linkDataSource = null;
if(selectedEl.length) {
if(selectedEl.is('a')) {
// Element is a link
linkDataSource = selectedEl;
// TODO Limit to inline elements, otherwise will also apply to e.g. paragraphs which already contain one or more links
// } else if((selectedEl.find('a').length)) {
// // Element contains a link
// var firstLinkEl = selectedEl.find('a:first');
// if(firstLinkEl.length) linkDataSource = firstLinkEl;
} else {
// Element is a child of a link
linkDataSource = selectedEl = selectedEl.parents('a:first');
}
}
if(linkDataSource && linkDataSource.length) this.modifySelection(function(ed){
ed.selectNode(linkDataSource[0]);
});
// Is anchor not a link
if (!linkDataSource.attr('href')) linkDataSource = null;
if (linkDataSource) {
href = linkDataSource.attr('href');
target = linkDataSource.attr('target');
title = linkDataSource.attr('title');
style_class = linkDataSource.attr('class');
href = this.getEditor().cleanLink(href, linkDataSource);
action = "update";
}
if(href.match(/^mailto:(.*)$/)) {
return {
LinkType: 'email',
email: RegExp.$1,
Description: title
};
} else if(href.match(/^(assets\/.*)$/) || href.match(/^\[file_link\s*(?:\s*|%20|,)?id=([0-9]+)\]?(#.*)?$/)) {
return {
LinkType: 'file',
file: RegExp.$1,
Description: title,
TargetBlank: target ? true : false
};
} else if(href.match(/^#(.*)$/)) {
return {
LinkType: 'anchor',
Anchor: RegExp.$1,
Description: title,
TargetBlank: target ? true : false
};
} else if(href.match(/^\[sitetree_link(?:\s*|%20|,)?id=([0-9]+)\]?(#.*)?$/i)) {
return {
LinkType: 'internal',
internal: RegExp.$1,
Anchor: RegExp.$2 ? RegExp.$2.substr(1) : '',
Description: title,
TargetBlank: target ? true : false
};
} else if(href) {
return {
LinkType: 'external',
external: href,
Description: title,
TargetBlank: target ? true : false
};
} else {
// No link/invalid link selected.
return null;
}
}
if(href.match(/^mailto:(.*)$/)) {
return {
LinkType: 'email',
email: RegExp.$1,
Description: title
};
} else if(href.match(/^(assets\/.*)$/) || href.match(/^\[file_link\s*(?:\s*|%20|,)?id=([0-9]+)\]?(#.*)?$/)) {
return {
LinkType: 'file',
file: RegExp.$1,
Description: title,
TargetBlank: target ? true : false
};
} else if(href.match(/^#(.*)$/)) {
return {
LinkType: 'anchor',
Anchor: RegExp.$1,
Description: title,
TargetBlank: target ? true : false
};
} else if(href.match(/^\[sitetree_link(?:\s*|%20|,)?id=([0-9]+)\]?(#.*)?$/i)) {
return {
LinkType: 'internal',
internal: RegExp.$1,
Anchor: RegExp.$2 ? RegExp.$2.substr(1) : '',
Description: title,
TargetBlank: target ? true : false
};
} else if(href) {
return {
LinkType: 'external',
external: href,
Description: title,
TargetBlank: target ? true : false
};
} else {
// No link/invalid link selected.
return null;
}
}
});
$('form.htmleditorfield-linkform input[name=LinkType]').entwine({
onclick: function(e) {
this.parents('form:first').redraw();
this._super();
},
onchange: function() {
this.parents('form:first').redraw();
// Update if a anchor-supporting link type is selected.
var linkType = this.parent().find(':checked').val();
if (linkType==='anchor' || linkType==='internal') {
this.parents('form.htmleditorfield-linkform').updateAnchorSelector();
}
this._super();
}
});
$('form.htmleditorfield-linkform input[name=internal]').entwine({
/**
* Update the anchor dropdown if a different page is selected in the "internal" dropdown.
*/
onvalueupdated: function() {
this.parents('form.htmleditorfield-linkform').updateAnchorSelector();
this._super();
}
});
$('form.htmleditorfield-linkform :submit[name=action_remove]').entwine({
onclick: function(e) {
this.parents('form:first').removeLink();
this._super();
return false;
}
});
@ -892,7 +990,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
this.find('.Actions .media-update')[editingSelected ? 'show' : 'hide']();
this.find('.ss-uploadfield-item-editform').toggleEditForm(editingSelected);
},
resetFields: function() {
resetFields: function() {
this.find('.ss-htmleditorfield-file').remove(); // Remove any existing views
this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
this.find('li.ss-uploadfield-item').remove(); // Remove all selected items
@ -977,6 +1075,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
$('form.htmleditorfield-form.htmleditorfield-mediaform input.remoteurl').entwine({
onadd: function() {
this._super();
this.validate();
},
@ -1128,7 +1227,9 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
* Logic similar to TinyMCE 'advimage' plugin, insertAndClose() method.
*/
insertHTML: function(ed) {
var form = this.closest('form'), node = form.getSelection(), ed = form.getEditor();
var form = this.closest('form');
var node = form.getSelection();
if (!ed) ed = form.getEditor();
// Get the attributes & extra data
var attrs = this.getAttributes(), extraData = this.getExtraData();
@ -1269,7 +1370,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
imgEl = $('<img />').attr(attrs).addClass('ss-htmleditorfield-file embed');
$.each(extraData, function (key, value) {
imgEl.attr('data-' + key, value)
imgEl.attr('data-' + key, value);
});
if(extraData.CaptionText) {
@ -1363,10 +1464,10 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
this.height(0);
itemInfo.find('.toggle-details-icon').removeClass('opened');
if(!this.hasClass('edited')){
text = ss.i18n._t('UploadField.NOCHANGES', 'No Changes')
text = ss.i18n._t('UploadField.NOCHANGES', 'No Changes');
status.addClass('ui-state-success-text');
}else{
text = ss.i18n._t('UploadField.CHANGESSAVED', 'Changes Made')
text = ss.i18n._t('UploadField.CHANGESSAVED', 'Changes Made');
this.removeClass('edited');
status.addClass('ui-state-success-text');
}

View File

@ -12,7 +12,7 @@
this._super();
},
onremove: function() {
if(this.data('uiTabs')) this.tabs('destroy');
if(this.data('tabs')) this.tabs('destroy');
this._super();
},
redrawTabs: function() {
@ -32,7 +32,7 @@
if(!matches) return;
$(this).attr('href', document.location.href.replace(/#.*/, '') + matches[0]);
});
}
}
});
});
})(jQuery);

View File

@ -2,15 +2,16 @@
$.entwine('ss', function($){
$('.ss-toggle').entwine({
onadd: function() {
this._super();
this.accordion({
collapsible: true,
active: (this.hasClass("ss-toggle-start-closed")) ? false : 0
});
this._super();
},
onremove: function() {
this.accordion('destroy');
if (this.data('accordion')) this.accordion('destroy');
this._super();
},
getTabSet: function() {

View File

@ -173,7 +173,11 @@
},
setValue: function(val) {
this.data('metadata', $.extend(this.data('metadata'), {id: val}));
this.find(':input:hidden').val(val).trigger('change');
this.find(':input:hidden').val(val)
// Trigger synthetic event so subscribers can workaround the IE8 problem with 'change' events
// not propagating on hidden inputs. 'change' is still triggered for backwards compatiblity.
.trigger('valueupdated')
.trigger('change');
},
getValue: function() {
return this.find(':input:hidden').val();
@ -423,11 +427,13 @@
$('.TreeDropdownField input[type=hidden]').entwine({
onadd: function() {
this._super();
this.bind('change.TreeDropdownField', function() {
$(this).getField().updateTitle();
});
},
onremove: function() {
this._super();
this.unbind('.TreeDropdownField');
}
});

View File

@ -94,7 +94,11 @@
},
_onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
$.blueimpUI.fileupload.prototype._onAlways.call(this, jqXHRorResult, textStatus, jqXHRorError, options);
if (this._active === 0) {
if(typeof(jqXHRorError) === 'string') {
$('.fileOverview .uploadStatus .state').text(ss.i18n._t('AssetUploadField.UploadField.UPLOADFAIL', 'Sorry your upload failed'));
$('.fileOverview .uploadStatus').addClass("bad").removeClass("good").removeClass("notice");
} else if (jqXHRorError.status === 200) {
$('.fileOverview .uploadStatus .state').text(ss.i18n._t('AssetUploadField.FILEUPLOADCOMPLETED', 'File upload completed!'));//.hide();
$('.ss-uploadfield-item-edit-all').show();
$('.fileOverview .uploadStatus').addClass("good").removeClass("notice").removeClass("bad");
@ -328,11 +332,11 @@
}
});
$('div.ss-upload .ss-uploadfield-files .ss-uploadfield-item').entwine({
onmatch: function() {
onadd: function() {
this._super();
this.closest('.ss-upload').find('.ss-uploadfield-addfile').addClass('borderTop');
},
onunmatch: function() {
onremove: function() {
$('.ss-uploadfield-files:not(:has(.ss-uploadfield-item))').closest('.ss-upload').find('.ss-uploadfield-addfile').removeClass('borderTop');
this._super();
}
@ -365,19 +369,25 @@
if(config.changeDetection) {
this.closest('form').trigger('dirty');
}
fileupload._trigger('destroy', e, {
context: item,
url: this.data('href'),
type: 'get',
dataType: fileupload.options.dataType
});
if (fileupload) {
fileupload._trigger('destroy', e, {
context: item,
url: this.data('href'),
type: 'get',
dataType: fileupload.options.dataType
});
}
}
} else {
// Removed files will be applied to object on save
if(config.changeDetection) {
this.closest('form').trigger('dirty');
}
fileupload._trigger('destroy', e, {context: item});
if (fileupload) {
fileupload._trigger('destroy', e, {context: item});
}
}
e.preventDefault(); // Avoid a form submit

View File

@ -138,33 +138,50 @@ ss.i18n = {
return stripStr(parts.join(" "));
},
/*
* printf()
* C-printf like function, which substitutes %s with parameters
* given in list. %%s is used to escape %s.
*
* Doesn't work in IE5.0 (splice)
*
* @param string S : string to perform printf on.
* @param string L : Array of arguments for printf()
*/
/**
* Substitutes %s with parameters
* given in list. %%s is used to escape %s.
*
* @param string S : The string to perform the substitutions on.
* @return string The new string with substitutions made
*/
sprintf: function(S) {
if (arguments.length == 1) return S;
var nS = "";
var tS = S.split("%s");
var args = [];
for (var i=1, len = arguments.length; i <len; ++i) {
var args = [],
len = arguments.length,
index = 0,
regx = new RegExp('(.?)(%s)', 'g'),
result;
for (var i=1; i<len; ++i) {
args.push(arguments[i]);
};
for(var i=0; i<args.length; i++) {
if (tS[i].lastIndexOf('%') == tS[i].length-1 && i != args.length-1)
tS[i] += "s"+tS.splice(i+1,1)[0];
nS += tS[i] + args[i];
}
return nS + tS[tS.length-1];
result = S.replace(regx, function(match, subMatch1, subMatch2, offset, string){
if (subMatch1 == '%') return match; // skip %%s
return subMatch1 + args[index++];
});
return result;
},
/**
* Substitutes variables with a list of injections.
*
* @param string S : The string to perform the substitutions on.
* @param object map : An object with the substitions map e.g. {var: value}
* @return string The new string with substitutions made
*/
inject: function(S, map) {
var regx = new RegExp("\{([A-Za-z0-9_]*)\}", "g"),
result;
result = S.replace(regx, function(match, key, offset, string){
return (map[key]) ? map[key] : match;
});
return result;
},
/**

View File

@ -18,20 +18,33 @@ ss.i18n = {
sprintf: function(S) {
if (arguments.length == 1) return S;
var nS = "";
var tS = S.split("%s");
var args = [];
for (var i=1, len = arguments.length; i <len; ++i) {
var args = [],
len = arguments.length,
index = 0,
regx = new RegExp('(.?)(%s)', 'g'),
result;
for (var i=1; i<len; ++i) {
args.push(arguments[i]);
};
for(var i=0; i<args.length; i++) {
if (tS[i].lastIndexOf('%') == tS[i].length-1 && i != args.length-1)
tS[i] += "s"+tS.splice(i+1,1)[0];
nS += tS[i] + args[i];
}
return nS + tS[tS.length-1];
result = S.replace(regx, function(match, subMatch1, subMatch2, offset, string){
if (subMatch1 == '%') return match; // skip %%s
return subMatch1 + args[index++];
});
return result;
},
inject: function(S, map) {
var regx = new RegExp("\{([A-Za-z0-9_]*)\}", "g"),
result;
result = S.replace(regx, function(match, key, offset, string){
return (map[key]) ? map[key] : match;
});
return result;
},
// stub methods

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

@ -0,0 +1,47 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/eo.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('eo', {
"VALIDATOR.FIELDREQUIRED": "Bonvole plenumu je \"%s\". Ĝi estas nepra.",
"HASMANYFILEFIELD.UPLOADING": "Alŝutiĝas %s...",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Ĉi vi vere volas forigi ĉi tiun rikordon?",
"LOADING": "ŝargas...",
"UNIQUEFIELD.SUGGESTED": "Ŝanĝis valoron al '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Necesas enigi valoron por ĉi tiu kampo",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Ĉi tiu kampo ne povas esti malplena",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "La signo '%s' ne estas uzebla en ĉi tiu kampo",
"UPDATEURL.CONFIRM": "Ĉu vi deziras ke mi ŝanĝu je la URL al:\n\n%s/\n\nKlaku al Akcepti por ke la URL ŝanĝiĝu, aŭ klaku al Nuligi por lasi ĝin kiel:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "La URL estas ŝanĝita al\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Forigi dosieron",
"FILEIFRAMEFIELD.UNATTACHFILE": "Malligi dosieron",
"FILEIFRAMEFIELD.DELETEIMAGE": "Forigi bildon",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Ĉi vi vere volas forigi ĉi tiun dosieron?",
"LeftAndMain.IncompatBrowserWarning": "Via foliumilo ne kongruas kun la CMS-fasado. Bonvole uzu je Explorer 7+, Google Chrome 10+ aŭ Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Okazis eraro dum akirado de datumoj el la servilo\n Bonvole reprovu poste.",
"HtmlEditorField.SelectAnchor": "Elekti ankron",
"UploadField.ConfirmDelete": "Ĉi vi vere volas forigi ĉi tiun dosieron el la servila dosierujo?",
"UploadField.PHP_MAXFILESIZE": "Dosiero superas alŝutan maksimuman grandon (php.ini direktivo)",
"UploadField.HTML_MAXFILESIZE": "La dosiero superas maksimuman dosiergrandon (HTML-formulara direktivo)",
"UploadField.ONLYPARTIALUPLOADED": "Dosiero nur parte alŝutiĝis",
"UploadField.NOFILEUPLOADED": "Neniu dosiero alŝutiĝis",
"UploadField.NOTMPFOLDER": "Mankas provizora dosierujo",
"UploadField.WRITEFAILED": "Malsukcesis skribi dosieron al disko",
"UploadField.STOPEDBYEXTENSION": "Kromprogramo haltigis dosieran alŝuton",
"UploadField.TOOLARGE": "Dosiero estas tro granda",
"UploadField.TOOSMALL": "La dosiero estas tro malgranda",
"UploadField.INVALIDEXTENSION": "Malvalida sufikso",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Superis maksimuman nombron da dosieroj",
"UploadField.UPLOADEDBYTES": "Elŝutitaj bajtoj superas grandon de dosiero",
"UploadField.EMPTYRESULT": "Vaka dosiero alŝute rezultis",
"UploadField.LOADING": "Ŝargas...",
"UploadField.Editing": "Redaktas...",
"UploadField.Uploaded": "Alŝutita",
"UploadField.OVERWRITEWARNING": "Jam ekzistas dosiero samnoma",
"TreeDropdownField.ENTERTOSEARCH": "Premu enigan klavon por serĉi",
"TreeDropdownField.OpenLink": "Malfermi",
"TreeDropdownField.FieldTitle": "(Elekti)",
"TreeDropdownField.SearchFieldTitle": "Elekti aŭ serĉi"
});
}

View File

@ -4,40 +4,40 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('nl', {
"VALIDATOR.FIELDREQUIRED": "Vul het veld \"%s\" in, dit is een verplicht veld.",
"HASMANYFILEFIELD.UPLOADING": "Uploading... %s",
"VALIDATOR.FIELDREQUIRED": "Vul \"%s\" in, dit is een verplicht veld.",
"HASMANYFILEFIELD.UPLOADING": "Uploaden... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Weet u zeker dat u dit record wilt verwijderen?",
"LOADING": "laden...",
"UNIQUEFIELD.SUGGESTED": "Waarde gewijzigd naar \"%s\" : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "U zult een nieuwe waarde voor dit veld moeten invoeren",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dit veld mag niet leeg blijven",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Het karakter \"%s\" mag niet gebruikt worden in dit veld",
"UPDATEURL.CONFIRM": "Wilt u de URL wijzigen naar:\n\n%s/\n\nKlik Ok om de URL te wijzigen, Klik Cancel om het te laten zoals het is:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "Het URL is veranderd naar \n\"%s\"",
"UPDATEURL.CONFIRM": "Wilt u de URL wijzigen naar:\n\n%s/\n\nKlik Ok om de URL te wijzigen, of klik Annuleren om het te laten zoals volgt:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "De URL is veranderd naar \n\"%s\"",
"FILEIFRAMEFIELD.DELETEFILE": "Verwijder bestand",
"FILEIFRAMEFIELD.UNATTACHFILE": "Deselecteer bestand",
"FILEIFRAMEFIELD.UNATTACHFILE": "Bestand ontkoppelen",
"FILEIFRAMEFIELD.DELETEIMAGE": "Verwijder afbeelding",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Weet u zeker dat u dit bestand wilt verwijderen?",
"LeftAndMain.IncompatBrowserWarning": "Je huidige browser is niet compatible, gebruik één van deze browsers Internet Explorer 7+, Google Chrome 10+ or Mozilla Firefox 3.5+.",
"LeftAndMain.IncompatBrowserWarning": "Uw huidige browser is niet compatibel met dit CMS. Gebruik één van deze browsers: Internet Explorer 7+, Google Chrome 10+ of Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Er is een fout opgetreden bij het ophalen van gegevens van de server\n Probeer later opnieuw.",
"HtmlEditorField.SelectAnchor": "Kies een anker",
"UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen uit het websitebestand?",
"UploadField.PHP_MAXFILESIZE": "Bestandsgrootte is hoger dan upload_max_filesize (php.ini directive)",
"UploadField.HTML_MAXFILESIZE": "Bestandsgrootte is hoger danMAX_FILE_SIZE (HTML form directive)",
"UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geupload",
"UploadField.NOFILEUPLOADED": "Geen bestand is geupload",
"UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen van de server?",
"UploadField.PHP_MAXFILESIZE": "Bestand is groter dan upload_max_filesize (limiet in php.ini)",
"UploadField.HTML_MAXFILESIZE": "Bestand is groter dan MAX_FILE_SIZE (limiet in HTML formulier)",
"UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geüpload",
"UploadField.NOFILEUPLOADED": "Er is geen bestand geüpload",
"UploadField.NOTMPFOLDER": "Mist een tijdelijke map",
"UploadField.WRITEFAILED": "Kan bestand niet naar schijf schrijven",
"UploadField.STOPEDBYEXTENSION": "Bestandsupload gestopt door extensie",
"UploadField.TOOLARGE": "Bestandsgrootte is te groot",
"UploadField.TOOSMALL": "Bestandsgrootte is te klein",
"UploadField.TOOLARGE": "Bestand is te groot",
"UploadField.TOOSMALL": "Bestand is te klein",
"UploadField.INVALIDEXTENSION": "Extensie is niet toegestaan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal overschreven",
"UploadField.UPLOADEDBYTES": "Upload overschrijd bestandsgrootte",
"UploadField.EMPTYRESULT": "Leeg bestand geupload",
"UploadField.LOADING": "Laden ...",
"UploadField.Editing": "Bijwerken ...",
"UploadField.Uploaded": "Geupload",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal bestanden overschreden",
"UploadField.UPLOADEDBYTES": "Geüploade bytes overschrijden bestandsgrootte",
"UploadField.EMPTYRESULT": "Leeg bestand geüpload",
"UploadField.LOADING": "Laden...",
"UploadField.Editing": "Bijwerken...",
"UploadField.Uploaded": "Geüpload",
"UploadField.OVERWRITEWARNING": "Bestand met dezelfde naam bestaat al",
"TreeDropdownField.ENTERTOSEARCH": "Druk op enter om te zoeken",
"TreeDropdownField.OpenLink": "Openen",

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

@ -0,0 +1,47 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/sl.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('sl', {
"VALIDATOR.FIELDREQUIRED": "Prosimo izpolnite \"%s\", to je zahtevano",
"HASMANYFILEFIELD.UPLOADING": "Nalagam ... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Izbrišem ta zapis?",
"LOADING": "nalagam ...",
"UNIQUEFIELD.SUGGESTED": "Spremenjena vrednost '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "V to polje bo potrebno vnesti novo vrednost",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Polje ne sme biti prazno",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Znak '%s' ne more biti uporabljen v tem polju",
"UPDATEURL.CONFIRM": "Would you like me to change the URL to:\n\n%s/\n\nClick Ok to change the URL, click Cancel to leave it as:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL je bil spremenjen v \n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Izbriši datoteko",
"FILEIFRAMEFIELD.UNATTACHFILE": "Un-Attach File",
"FILEIFRAMEFIELD.DELETEIMAGE": "Izbriši sliko",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Izbrišem to datoteko?",
"LeftAndMain.IncompatBrowserWarning": "Your browser is not compatible with the CMS interface. Please use Internet Explorer 7+, Google Chrome 10+ or Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Napak pri pridobivanju podatkov s strežnika.\nProsimo, poskusite ponovno ",
"HtmlEditorField.SelectAnchor": "Izberi sidro",
"UploadField.ConfirmDelete": "Izbrišem datoteko iz datotečnega sistema strežnika?",
"UploadField.PHP_MAXFILESIZE": "Datoteka presega največjo dovoljeno velikost \"upload_max_filesize\" (php.ini direktiva)",
"UploadField.HTML_MAXFILESIZE": "Datoteka presega največjo dovoljeno velikost \"MAX_FILE_SIZE\" (HTML direktiva)",
"UploadField.ONLYPARTIALUPLOADED": "Datoteka je bila le delno naložena",
"UploadField.NOFILEUPLOADED": "Nobena datoteka ni bila naložena",
"UploadField.NOTMPFOLDER": "Manjka začasna mapa",
"UploadField.WRITEFAILED": "Neuspešno pisanje datoteke na disk",
"UploadField.STOPEDBYEXTENSION": "Nalaganje datoteke je ustavila razširitev",
"UploadField.TOOLARGE": "Datoteka je prevelika",
"UploadField.TOOSMALL": "Datoteka je premajhna",
"UploadField.INVALIDEXTENSION": "Razširitev ni dovoljena",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Preseženo največje število datotek",
"UploadField.UPLOADEDBYTES": "Naloženi bajti presegajo velikost datoteke",
"UploadField.EMPTYRESULT": "Empty file upload result",
"UploadField.LOADING": "Nalaganje ...",
"UploadField.Editing": "Urejanje ...",
"UploadField.Uploaded": "Naloženo",
"UploadField.OVERWRITEWARNING": "Datoteka z enakim imenom že obstaja",
"TreeDropdownField.ENTERTOSEARCH": "Pritisni \"enter\" za iskanje",
"TreeDropdownField.OpenLink": "Odpri",
"TreeDropdownField.FieldTitle": "Izberi",
"TreeDropdownField.SearchFieldTitle": "Izberi al išči"
});
}

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

@ -0,0 +1,41 @@
{
"VALIDATOR.FIELDREQUIRED": "Bonvole plenumu je \"%s\". Ĝi estas nepra.",
"HASMANYFILEFIELD.UPLOADING": "Alŝutiĝas %s...",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Ĉi vi vere volas forigi ĉi tiun rikordon?",
"LOADING": "ŝargas...",
"UNIQUEFIELD.SUGGESTED": "Ŝanĝis valoron al '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Necesas enigi valoron por ĉi tiu kampo",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Ĉi tiu kampo ne povas esti malplena",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "La signo '%s' ne estas uzebla en ĉi tiu kampo",
"UPDATEURL.CONFIRM": "Ĉu vi deziras ke mi ŝanĝu je la URL al:\n\n%s/\n\nKlaku al Akcepti por ke la URL ŝanĝiĝu, aŭ klaku al Nuligi por lasi ĝin kiel:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "La URL estas ŝanĝita al\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Forigi dosieron",
"FILEIFRAMEFIELD.UNATTACHFILE": "Malligi dosieron",
"FILEIFRAMEFIELD.DELETEIMAGE": "Forigi bildon",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Ĉi vi vere volas forigi ĉi tiun dosieron?",
"LeftAndMain.IncompatBrowserWarning": "Via foliumilo ne kongruas kun la CMS-fasado. Bonvole uzu je Explorer 7+, Google Chrome 10+ aŭ Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Okazis eraro dum akirado de datumoj el la servilo\n Bonvole reprovu poste.",
"HtmlEditorField.SelectAnchor": "Elekti ankron",
"UploadField.ConfirmDelete": "Ĉi vi vere volas forigi ĉi tiun dosieron el la servila dosierujo?",
"UploadField.PHP_MAXFILESIZE": "Dosiero superas alŝutan maksimuman grandon (php.ini direktivo)",
"UploadField.HTML_MAXFILESIZE": "La dosiero superas maksimuman dosiergrandon (HTML-formulara direktivo)",
"UploadField.ONLYPARTIALUPLOADED": "Dosiero nur parte alŝutiĝis",
"UploadField.NOFILEUPLOADED": "Neniu dosiero alŝutiĝis",
"UploadField.NOTMPFOLDER": "Mankas provizora dosierujo",
"UploadField.WRITEFAILED": "Malsukcesis skribi dosieron al disko",
"UploadField.STOPEDBYEXTENSION": "Kromprogramo haltigis dosieran alŝuton",
"UploadField.TOOLARGE": "Dosiero estas tro granda",
"UploadField.TOOSMALL": "La dosiero estas tro malgranda",
"UploadField.INVALIDEXTENSION": "Malvalida sufikso",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Superis maksimuman nombron da dosieroj",
"UploadField.UPLOADEDBYTES": "Elŝutitaj bajtoj superas grandon de dosiero",
"UploadField.EMPTYRESULT": "Vaka dosiero alŝute rezultis",
"UploadField.LOADING": "Ŝargas...",
"UploadField.Editing": "Redaktas...",
"UploadField.Uploaded": "Alŝutita",
"UploadField.OVERWRITEWARNING": "Jam ekzistas dosiero samnoma",
"TreeDropdownField.ENTERTOSEARCH": "Premu enigan klavon por serĉi",
"TreeDropdownField.OpenLink": "Malfermi",
"TreeDropdownField.FieldTitle": "(Elekti)",
"TreeDropdownField.SearchFieldTitle": "Elekti aŭ serĉi"
}

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

@ -0,0 +1,41 @@
{
"VALIDATOR.FIELDREQUIRED": "Vennligst fyll ut det påkrevde feltet \"%s\"",
"HASMANYFILEFIELD.UPLOADING": "Laster opp ... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Er du sikker på at du vil slette denne oppføringen?",
"LOADING": "laster ...",
"UNIQUEFIELD.SUGGESTED": "Endret verdien til '%s': %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Du må skrive inn en ny verdi for dette feltet",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dette feltet kan ikke stå tomt",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Tegnet '%s' kan ikke brukes i dette feltet",
"UPDATEURL.CONFIRM": "Ønsker du å endre adressen til:\n\n%s/\n\nTrykk Ok for å endre adressen, trykk Avbryt for å beholde den som:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "Adressen har blitt endret til\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Slett fil",
"FILEIFRAMEFIELD.UNATTACHFILE": "Fjern vedlagt fil",
"FILEIFRAMEFIELD.DELETEIMAGE": "Slett bilde",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Er du sikker på du vil slette denne filen?",
"LeftAndMain.IncompatBrowserWarning": "Nettleseren din er ikke kompatibel med publiseringsgrensesnittet. Vennligst bruk Internet Explorer 7+, Google Chrome 10+ eller Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "En feil oppstod ved lesing fra serveren\nVennligst prøv på nytt senere.",
"HtmlEditorField.SelectAnchor": "Velg et anker",
"UploadField.ConfirmDelete": "Er du sikker på at du vil fjerne denne filen fra serverens filsystem?",
"UploadField.PHP_MAXFILESIZE": "Filen er større enn upload_max_filesize (innstilling i php.ini)",
"UploadField.HTML_MAXFILESIZE": "Filen er større enn MAX_FILE_SIZE (HTML-direktiv)",
"UploadField.ONLYPARTIALUPLOADED": "Filen ble bare delvis lastet opp",
"UploadField.NOFILEUPLOADED": "Ingen filer ble lastet opp",
"UploadField.NOTMPFOLDER": "Mangler en midlertidig mappe",
"UploadField.WRITEFAILED": "Klarte ikke å lagre filen på harddisken",
"UploadField.STOPEDBYEXTENSION": "Filopplastingen ble stoppet av en utvidelse",
"UploadField.TOOLARGE": "Filen er for stor",
"UploadField.TOOSMALL": "Filen er for liten",
"UploadField.INVALIDEXTENSION": "Filtypen er ikke tillatt",
"UploadField.MAXNUMBEROFFILESSIMPLE": "For mange filer",
"UploadField.UPLOADEDBYTES": "Lastet opp flere bytes enn filstørrelsen tilsier",
"UploadField.EMPTYRESULT": "Filopplastingen ga et tomt resultat",
"UploadField.LOADING": "Laster ...",
"UploadField.Editing": "Redigerer ...",
"UploadField.Uploaded": "Lastet opp",
"UploadField.OVERWRITEWARNING": "En fil med samme navn eksisterer allerede",
"TreeDropdownField.ENTERTOSEARCH": "Trykk enter for å søke",
"TreeDropdownField.OpenLink": "Åpne",
"TreeDropdownField.FieldTitle": "Velg",
"TreeDropdownField.SearchFieldTitle": "Velg eller søk"
}

View File

@ -1,38 +1,38 @@
{
"VALIDATOR.FIELDREQUIRED": "Vul het veld \"%s\" in, dit is een verplicht veld.",
"HASMANYFILEFIELD.UPLOADING": "Uploading... %s",
"VALIDATOR.FIELDREQUIRED": "Vul \"%s\" in, dit is een verplicht veld.",
"HASMANYFILEFIELD.UPLOADING": "Uploaden... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Weet u zeker dat u dit record wilt verwijderen?",
"LOADING": "laden...",
"UNIQUEFIELD.SUGGESTED": "Waarde gewijzigd naar \"%s\" : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "U zult een nieuwe waarde voor dit veld moeten invoeren",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dit veld mag niet leeg blijven",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Het karakter \"%s\" mag niet gebruikt worden in dit veld",
"UPDATEURL.CONFIRM": "Wilt u de URL wijzigen naar:\n\n%s/\n\nKlik Ok om de URL te wijzigen, Klik Cancel om het te laten zoals het is:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "Het URL is veranderd naar \n\"%s\"",
"UPDATEURL.CONFIRM": "Wilt u de URL wijzigen naar:\n\n%s/\n\nKlik Ok om de URL te wijzigen, of klik Annuleren om het te laten zoals volgt:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "De URL is veranderd naar \n\"%s\"",
"FILEIFRAMEFIELD.DELETEFILE": "Verwijder bestand",
"FILEIFRAMEFIELD.UNATTACHFILE": "Deselecteer bestand",
"FILEIFRAMEFIELD.UNATTACHFILE": "Bestand ontkoppelen",
"FILEIFRAMEFIELD.DELETEIMAGE": "Verwijder afbeelding",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Weet u zeker dat u dit bestand wilt verwijderen?",
"LeftAndMain.IncompatBrowserWarning": "Je huidige browser is niet compatible, gebruik één van deze browsers Internet Explorer 7+, Google Chrome 10+ or Mozilla Firefox 3.5+.",
"LeftAndMain.IncompatBrowserWarning": "Uw huidige browser is niet compatibel met dit CMS. Gebruik één van deze browsers: Internet Explorer 7+, Google Chrome 10+ of Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Er is een fout opgetreden bij het ophalen van gegevens van de server\n Probeer later opnieuw.",
"HtmlEditorField.SelectAnchor": "Kies een anker",
"UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen uit het websitebestand?",
"UploadField.PHP_MAXFILESIZE": "Bestandsgrootte is hoger dan upload_max_filesize (php.ini directive)",
"UploadField.HTML_MAXFILESIZE": "Bestandsgrootte is hoger danMAX_FILE_SIZE (HTML form directive)",
"UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geupload",
"UploadField.NOFILEUPLOADED": "Geen bestand is geupload",
"UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen van de server?",
"UploadField.PHP_MAXFILESIZE": "Bestand is groter dan upload_max_filesize (limiet in php.ini)",
"UploadField.HTML_MAXFILESIZE": "Bestand is groter dan MAX_FILE_SIZE (limiet in HTML formulier)",
"UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geüpload",
"UploadField.NOFILEUPLOADED": "Er is geen bestand geüpload",
"UploadField.NOTMPFOLDER": "Mist een tijdelijke map",
"UploadField.WRITEFAILED": "Kan bestand niet naar schijf schrijven",
"UploadField.STOPEDBYEXTENSION": "Bestandsupload gestopt door extensie",
"UploadField.TOOLARGE": "Bestandsgrootte is te groot",
"UploadField.TOOSMALL": "Bestandsgrootte is te klein",
"UploadField.TOOLARGE": "Bestand is te groot",
"UploadField.TOOSMALL": "Bestand is te klein",
"UploadField.INVALIDEXTENSION": "Extensie is niet toegestaan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal overschreven",
"UploadField.UPLOADEDBYTES": "Upload overschrijd bestandsgrootte",
"UploadField.EMPTYRESULT": "Leeg bestand geupload",
"UploadField.LOADING": "Laden ...",
"UploadField.Editing": "Bijwerken ...",
"UploadField.Uploaded": "Geupload",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal bestanden overschreden",
"UploadField.UPLOADEDBYTES": "Geüploade bytes overschrijden bestandsgrootte",
"UploadField.EMPTYRESULT": "Leeg bestand geüpload",
"UploadField.LOADING": "Laden...",
"UploadField.Editing": "Bijwerken...",
"UploadField.Uploaded": "Geüpload",
"UploadField.OVERWRITEWARNING": "Bestand met dezelfde naam bestaat al",
"TreeDropdownField.ENTERTOSEARCH": "Druk op enter om te zoeken",
"TreeDropdownField.OpenLink": "Openen",

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

@ -0,0 +1,41 @@
{
"VALIDATOR.FIELDREQUIRED": "Prosimo izpolnite \"%s\", to je zahtevano",
"HASMANYFILEFIELD.UPLOADING": "Nalagam ... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Izbrišem ta zapis?",
"LOADING": "nalagam ...",
"UNIQUEFIELD.SUGGESTED": "Spremenjena vrednost '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "V to polje bo potrebno vnesti novo vrednost",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Polje ne sme biti prazno",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Znak '%s' ne more biti uporabljen v tem polju",
"UPDATEURL.CONFIRM": "Would you like me to change the URL to:\n\n%s/\n\nClick Ok to change the URL, click Cancel to leave it as:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL je bil spremenjen v \n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Izbriši datoteko",
"FILEIFRAMEFIELD.UNATTACHFILE": "Un-Attach File",
"FILEIFRAMEFIELD.DELETEIMAGE": "Izbriši sliko",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Izbrišem to datoteko?",
"LeftAndMain.IncompatBrowserWarning": "Your browser is not compatible with the CMS interface. Please use Internet Explorer 7+, Google Chrome 10+ or Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Napak pri pridobivanju podatkov s strežnika.\nProsimo, poskusite ponovno ",
"HtmlEditorField.SelectAnchor": "Izberi sidro",
"UploadField.ConfirmDelete": "Izbrišem datoteko iz datotečnega sistema strežnika?",
"UploadField.PHP_MAXFILESIZE": "Datoteka presega največjo dovoljeno velikost \"upload_max_filesize\" (php.ini direktiva)",
"UploadField.HTML_MAXFILESIZE": "Datoteka presega največjo dovoljeno velikost \"MAX_FILE_SIZE\" (HTML direktiva)",
"UploadField.ONLYPARTIALUPLOADED": "Datoteka je bila le delno naložena",
"UploadField.NOFILEUPLOADED": "Nobena datoteka ni bila naložena",
"UploadField.NOTMPFOLDER": "Manjka začasna mapa",
"UploadField.WRITEFAILED": "Neuspešno pisanje datoteke na disk",
"UploadField.STOPEDBYEXTENSION": "Nalaganje datoteke je ustavila razširitev",
"UploadField.TOOLARGE": "Datoteka je prevelika",
"UploadField.TOOSMALL": "Datoteka je premajhna",
"UploadField.INVALIDEXTENSION": "Razširitev ni dovoljena",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Preseženo največje število datotek",
"UploadField.UPLOADEDBYTES": "Naloženi bajti presegajo velikost datoteke",
"UploadField.EMPTYRESULT": "Empty file upload result",
"UploadField.LOADING": "Nalaganje ...",
"UploadField.Editing": "Urejanje ...",
"UploadField.Uploaded": "Naloženo",
"UploadField.OVERWRITEWARNING": "Datoteka z enakim imenom že obstaja",
"TreeDropdownField.ENTERTOSEARCH": "Pritisni \"enter\" za iskanje",
"TreeDropdownField.OpenLink": "Odpri",
"TreeDropdownField.FieldTitle": "Izberi",
"TreeDropdownField.SearchFieldTitle": "Izberi al išči"
}

View File

@ -1,13 +1,32 @@
eo:
AssetAdmin:
NEWFOLDER: Nova dosierujo
SHOWALLOWEDEXTS: 'Vidigi permesitajn sufiksojn'
AssetTableField:
CREATED: 'Unue alŝutita'
DIM: Dimensioj
FILENAME: Nomo de dosiero
FOLDER: Dosierujo
LASTEDIT: 'Laste ŝanĝita'
OWNER: Posedanto
SIZE: 'Grando'
TITLE: Titolo
TYPE: 'Tipo'
URL: URL
AssetUploadField:
ChooseFiles: 'Elekti dosierojn'
DRAGFILESHERE: 'Ŝovi dosieron ĉi tien'
DROPAREA: 'Lasi zonon'
EDITALL: 'Redakti ĉiujn'
EDITANDORGANIZE: 'Redakti kaj organizi'
EDITINFO: 'Redakti dosierojn'
FILES: Dosieroj
FROMCOMPUTER: 'Elekti dosierojn el via komputilo'
FROMCOMPUTERINFO: 'Alŝuti el via komputilo'
TOTAL: Totalo
TOUPLOAD: 'Elekti dosierojn alŝutotajn...'
UPLOADINPROGRESS: 'Bonvolu atendi...alŝuto daŭras'
UPLOADOR:
BBCodeParser:
ALIGNEMENT: Ĝisrandigo
ALIGNEMENTEXAMPLE: 'ĝisrandigita dekstren'
@ -33,16 +52,26 @@ eo:
UNORDERED: 'Neordigita listo'
UNORDEREDDESCRIPTION: 'Neordigita listo'
UNORDEREDEXAMPLE1: 'neordigita ero 1'
BackLink_Button_ss:
Back: Retro
BasicAuth:
ENTERINFO: 'bonvolu enigi salutnomon kaj pasvorton.'
ERRORNOTADMIN: 'Tiu uzanto ne estas administranto.'
ERRORNOTREC: 'Kiuj salutnomo / pasvorto ne estas rekonebla'
Boolean:
ANY: Ajna
CMSLoadingScreen_ss:
LOADING: Ŝargas...
REQUIREJS: 'La CMS bezonas ke vi enŝaltis Ĝavaskripton.'
CMSMain:
ACCESS: 'Aliro al sekcio ''{title}'''
ACCESSALLINTERFACES: 'Aliro al ĉiuj interfacoj de CMS'
ACCESSALLINTERFACESHELP: 'Nuligas pli specifajn alirajn agordojn.'
SAVE: Konservi
CMSPageHistoryController_versions_ss:
PREVIEW: 'Antaŭvido de retejo'
CMSProfileController:
MENUTITLE: 'Mia agordaro'
ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Vi ŝanĝis vian pasvorton por'
CHANGEPASSWORDTEXT2: 'Nun vi povas uzi la jenan legitimaĵon por ensaluti:'
@ -50,75 +79,204 @@ eo:
HELLO: Saluton
PASSWORD: Pasvorto
ConfirmedPasswordField:
ATLEAST: 'Pasvorto devas esti almenaŭ {min} signojn longa.'
BETWEEN: 'Pasvorto devas esti inter {min} kaj {max} signojn longa.'
MAXIMUM: 'Pasvorto devas esti ne pli ol {min} signojn longa.'
SHOWONCLICKTITLE: 'Ŝangi Pasvorton'
ContentController:
NOTLOGGEDIN: 'Ne ensalutis'
CreditCardField:
FIRST: unuan
FOURTH: kvaran
SECOND: duan
THIRD: trian
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Datumaj Objektoj'
SINGULARNAME: 'Datuma Objekto'
Date:
DAY: tago
DAYS: tagoj
HOUR: horo
HOURS: horoj
LessThanMinuteAgo: 'malpli ol minuto'
MIN: min
MINS: min
MONTH: monato
MONTHS: monatoj
SEC: sek
SECS: sek
TIMEDIFFAGO: 'antaŭ {difference}'
TIMEDIFFIN: 'en {difference}'
YEAR: jaro
YEARS: jaroj
DateField:
NOTSET: 'ne agordita'
TODAY: hodiaŭ
VALIDDATEFORMAT2: 'Bonvole enigu validan datan formaton ({format})'
VALIDDATEMAXDATE: 'Necesas ke via dato estu pli aĝa ol, aŭ egala al la maksimuma permesita dato ({date})'
VALIDDATEMINDATE: 'Necesas ke via dato estu pli nova ol, aŭ egala al la minimuma permesita dato ({date})'
DatetimeField:
NOTSET: 'Ne agordita'
Director:
INVALID_REQUEST: 'Malvalida peto'
DropdownField:
CHOOSE: (Elekti)
CHOOSESEARCH: '(Elekti aŭ serĉi)'
EmailField:
VALIDATION: 'Bonvolu enigi readreson'
Enum:
ANY: Ajna
File:
AviType: 'AVI videa dosiero'
Content: Enhavo
CssType: 'CSS-dosiero'
DmgType: 'Apple-diska bildo'
DocType: 'Word-dokumento'
Filename: Dosiernomo
GifType: 'GIF-bildo - taŭga por figuroj'
GzType: 'GZIP-kunpremita dosiero'
HtlType: 'HTML-dosiero'
HtmlType: 'HTML-dosiero'
INVALIDEXTENSION: 'Malvalida sufikso (validaj: {extensions})'
INVALIDEXTENSIONSHORT: 'Malvalida sufikso'
IcoType: 'Bildsimbolo'
JpgType: 'JPEG-bildo - taŭga por fotoj'
JsType: 'Ĝavaskripta dosiero'
Mp3Type: 'MP3-sondosiero'
MpgType: 'AVI-videa dosiero'
NOFILESIZE: 'Dosiero havas nul bajtojn'
NOVALIDUPLOAD: 'Dosiero ne estas valida alŝutaĵo.'
Name: Nomo
PLURALNAME: Dosieroj
PdfType: 'Dosiero de Adobe Acrobat PDF'
PngType: 'PNG-bildo - ĝeneralcela formato'
SINGULARNAME: Dosiero
TOOLARGE: 'Dosiero estas tro granda; maksimumo estas {size}'
TOOLARGESHORT: 'Grando de dosiero superas je {size}'
TiffType: 'Markita bildoformato'
Title: Titolo
WavType: 'WAV-sondosiero'
XlsType: 'Kalkultabelo de Excel'
ZipType: 'ZIP-kunpremita dosiero'
Filesystem:
SYNCRESULTS: 'sinkronigo finiĝis: kreiĝis {createdcount} elementoj, foriĝis {createdcount} elementoj.'
Folder:
PLURALNAME: Dosierujoj
SINGULARNAME: Dosierujo
ForgotPasswordEmail_ss:
HELLO: Saluton
TEXT1: 'Jen via'
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."
FIELDISREQUIRED: '{name} estas bezonata'
SubmitBtnLabel: Iri
VALIDATIONCREDITNUMBER: 'Bonvole certigu ke vi ĝuste enigis la kreditkarton {number}'
VALIDATIONNOTUNIQUE: 'La enirita valoron ne unika'
VALIDATIONPASSWORDSDONTMATCH: 'Pasvortoj ne matĉas'
VALIDATIONPASSWORDSNOTEMPTY: 'Pasvortoj ne povas est malplena'
VALIDATIONSTRONGPASSWORD: 'Pasvorto devas havi almenaŭ unu signon kaj unu literon.'
VALIDATOR: Validigilo
VALIDCURRENCY: 'Bonvole enigu validan kurzon'
CSRF_EXPIRED_MESSAGE: 'Via seanco finiĝis. Bonvole resendu la formularon.'
FormField:
Example: 'ekz. %s'
NONE: neniu
GridAction:
DELETE_DESCRIPTION: Forigi
Delete: Forigi
UnlinkRelation: Malligi
GridField:
Add: 'Aldoni je {name}'
Filter: Filtri
FilterBy: 'Filtri laŭ'
Find: Serĉi
LEVELUP: 'Samniveligi'
LinkExisting: 'Ligi ekzistantajn'
NewRecord: 'Novaj %s'
NoItemsFound: 'Neniu elemento troviĝis'
PRINTEDAT: 'Presita ĉe'
PRINTEDBY: 'Presita de'
PlaceHolder: 'Serĉi je {type}'
PlaceHolderWithLabels: 'Serĉi je {type} laŭ {name}'
RelationSearch: 'Serĉi rilatojn'
ResetFilter: Restartigi
GridFieldAction_Delete:
DeletePermissionsFailure: 'Mankas permeso forigi'
EditPermissionsFailure: 'Mankas permeso malligi rikordon'
GridFieldDetailForm:
CancelBtn: Rezigni
Create: Krei
Delete: Forigi
DeletePermissionsFailure: 'Mankas permeso forigi'
Deleted: 'Forigita %s %s'
Save: Konservi
Saved: 'Konservita {name} {link}'
GridFieldEditButton_ss:
EDIT: Redakti
GridFieldItemEditView:
Go_back: 'Retro'
Group:
AddRole: 'Aldoni rolon por ĉi tiu grupo'
Code: 'Grupa Kodo'
DefaultGroupTitleAdministrators: Administrantoj
DefaultGroupTitleContentAuthors: 'Enhavaŭtoroj'
Description: Priskribo
GroupReminder: 'Se vi elektas patran grupon, ĉi tiu grupo prenos ĉiujn ĝiajn rolojn'
HierarchyPermsError: 'Ne povas agordi patran grupon "%s" kun privilegiaj permesoj (bezonas ADMIN-aliron)'
Locked: 'Ŝlosita?'
NoRoles: 'Ne trovis rolon'
PLURALNAME: Grupoj
Parent: 'Patra Grupo'
RolesAddEditLink: 'Aldoni/redakti rolojn'
SINGULARNAME: Grupo
Sort: 'Ordiga Ordo'
has_many_Permissions: Permesoj
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>"
ResultCreated: 'Kreiĝis {count} grupoj'
ResultDeleted: 'Forigis %d grupojn'
ResultUpdated: 'Aktualigis %d grupojn'
Hierarchy:
InfiniteLoopNotAllowed: 'Senfina iteracio troviĝis en la "{type}"-hierarkio. Bonvole ŝanĝu la patron por solvi tion.'
HtmlEditorField:
ADDURL: 'Aldoni je URL'
ADJUSTDETAILSDIMENSIONS: 'Detaloj kaj dimensioj'
ANCHORVALUE: Ankri
BUTTONADDURL: 'Aldoni je url'
BUTTONINSERT: Enmeti
BUTTONINSERTLINK: 'Almeti ligilon'
BUTTONREMOVELINK: 'Forigi ligilon'
BUTTONUpdate: Ĝisdatigi
CAPTIONTEXT: 'Titola teksto'
CSSCLASS: 'Ĝisrandigo / stilo'
CSSCLASSCENTER: 'Centrita, sola.'
CSSCLASSLEFT: 'Maldekstre, kaj teksto ĉirkaŭfluas.'
CSSCLASSLEFTALONE: 'Maldekstre sole'
CSSCLASSRIGHT: 'Dekstre, kaj teksto ĉirkaŭfluas.'
DETAILS: Detaloj
EMAIL: 'Retpoŝta adreso'
FILE: Dosiero
FOLDER: Dosierujo
FROMCMS: 'El la CMS'
FROMCOMPUTER: 'El via komputilo'
FROMWEB: 'El la TTT'
FindInFolder: 'Serĉi en dosierujo'
IMAGEALT: 'Alternativa teksto (alt)'
IMAGEALTTEXT: 'Alternativa teksto (alt) - vidiĝas se ne eblas vidigi bildon'
IMAGEALTTEXTDESC: 'Vidiĝas se ne eblas vidigi bildon'
IMAGEDIMENSIONS: Dimensioj
IMAGEHEIGHTPX: Alto
IMAGETITLE: 'Titola teksto (ŝpruchelpilo) - por plua informo pri la bildo'
IMAGETITLETEXT: 'Teksto de titolo (ŝpruchelpilo)'
IMAGETITLETEXTDESC: 'Por plua informo pri la bildo'
IMAGEWIDTHPX: Larĝo
INSERTMEDIA: 'Enmeti memorilon'
LINK: 'Ligilo'
LINKANCHOR: 'Ankri al ĉi tiu paĝo'
LINKDESCR: 'Ligila priskribo'
@ -129,34 +287,68 @@ eo:
LINKOPENNEWWIN: 'Malfermi ligilon en nova fenestro?'
LINKTO: 'Ligilo al'
PAGE: Paĝo
URL: URL
URLNOTANOEMBEDRESOURCE: 'La URL ''{url}'' ne estas konvertebla al memorilo.'
UpdateMEDIA: 'Ĝisdatigi memorilon'
Image:
PLURALNAME: Dosieroj
SINGULARNAME: Dosiero
Image_Cached:
PLURALNAME: Dosieroj
SINGULARNAME: Dosiero
Image_iframe_ss:
TITLE: 'Iframe por alŝuti bildon'
LeftAndMain:
CANT_REORGANISE: 'Vi ne rajtas ŝanĝi supronivelajn paĝojn. Via ŝanĝo ne konserviĝis.'
DELETED: Forigita.
DropdownBatchActionsDefault: Agoj
HELP: Helpo
PAGETYPE: 'Tipo de paĝo:'
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.'
PreviewButton: Antaŭvido
REORGANISATIONSUCCESSFUL: 'Sukcese reorganizis la retejan arbon.'
SAVEDUP: Konservita.
ShowAsList: 'vidigi kiel liston'
TooManyPages: 'Tro da paĝoj'
ValidationError: 'Validiga eraro'
VersionUnknown: Nekonata
LeftAndMain_Menu_ss:
Hello: Saluton
LOGOUT: 'Elsaluti'
LoginAttempt:
Email: 'Retadreso'
IP: 'IP-Adreso'
PLURALNAME: 'Provoj ensaluti'
SINGULARNAME: 'Provo ensaluti'
Status: Stato
Member:
BUTTONCHANGEPASSWORD: 'Ŝanĝi Pasvorton'
ADDGROUP: 'Aldoni grupon'
BUTTONCHANGEPASSWORD: 'Ŝanĝi pasvorton'
BUTTONLOGIN: 'Ensaluti'
BUTTONLOGINOTHER: 'Ensaluti kiel alia homo'
BUTTONLOSTPASSWORD: 'Mi perdis mian pasvorton'
CANTEDIT: 'Vi ne rajtas fari tion'
CONFIRMNEWPASSWORD: 'Konfirmu novan pasvorton'
CONFIRMPASSWORD: 'Konfirmu pasvorton'
DATEFORMAT: 'Formato de dato'
DefaultAdminFirstname: 'Defaŭlta Administranto'
DefaultDateTime: apriora
EMAIL: Retpoŝto
EMPTYNEWPASSWORD: 'La nova pasvorto ne povas esti nula, bonvole refaru'
ENTEREMAIL: 'Bonvolu enigi retadreson por atingi ligilon por reagordi pasvorton.'
ERRORLOCKEDOUT2: 'Via konto estas provizore malvalidigita pro troaj provoj ensaluti. Bonvole reprovu post {count} minutoj.'
ERRORNEWPASSWORD: 'Via ricev enirita vian novan pasvorton malsame, prov denove'
ERRORPASSWORDNOTMATCH: 'Via aktuala pasvorto ne matĉo, bonvolu prov denove'
ERRORWRONGCRED: 'La donitaj detaloj ŝajnas malĝustaj. Bonvole reprovu.'
FIRSTNAME: 'Antaŭnomo'
INTERFACELANG: 'Interfaca Lingvo'
INVALIDNEWPASSWORD: 'Ni ne povis akcepti tiun pasvorton: {password}'
LOGGEDINAS: 'Vi ensalutis kiel {name}.'
NEWPASSWORD: 'Novan pasvorton'
NoPassword: 'Mankas pasvorto por ĉi tiu membro.'
PASSWORD: Pasvorto
PLURALNAME: Membroj
REMEMBERME: 'Memoru min je la sekva fojo?'
@ -164,7 +356,10 @@ eo:
SUBJECTPASSWORDCHANGED: 'Via pasvorto estas ŝanĝita'
SUBJECTPASSWORDRESET: 'Via pasvorto reagordis ligilon'
SURNAME: Familia nomo
TIMEFORMAT: 'Formato de horo'
VALIDATIONMEMBEREXISTS: 'Jam ekzistas membro kun la sama %s'
ValidationIdentifierFailed: 'Ne povas anstataŭigi ekzistantan membron #{id} per sama identigilo ({name} = {value}))'
WELCOMEBACK: 'Bonvenon denove, {firstname}'
YOUROLDPASSWORD: 'Vian malnovan pasvorton'
belongs_many_many_Groups: Grupoj
db_LastVisited: 'Dato de Lasta Vizito'
@ -175,29 +370,98 @@ eo:
db_PasswordExpiry: 'Pasvorta Limdato'
MemberAuthenticator:
TITLE: 'Retpoŝto &amp; Pasvorto'
MemberDatetimeOptionsetField:
AMORPM: 'ATM (Ante meridiem) or PTM (Post meridiem)'
Custom: Propra
DATEFORMATBAD: 'Ne validas la formato de dato'
DAYNOLEADING: 'Tago de monato sen antaŭira nulo'
DIGITSDECFRACTIONSECOND: 'Almenaŭ unu cifero indikanta dekuman frakcion de sekundo'
FOURDIGITYEAR: 'Kvarcifera jaro'
FULLNAMEMONTH: 'Tuta nomo de monato (ekz. junio)'
HOURNOLEADING: 'Horo sen antaŭira nulo'
MINUTENOLEADING: 'Minuto sen antaŭira nulo'
MONTHNOLEADING: 'Monato sen antaŭira nulo'
Preview: Antaŭvido
SHORTMONTH: 'Mallonga nomo de monato (ekz. jun)'
TWODIGITDAY: 'Ducifera tago de monato'
TWODIGITHOUR: 'Ducifera horo (00 ĝis 23)'
TWODIGITMINUTE: 'Ducifera minuto (00 ĝis 59)'
TWODIGITMONTH: 'Ducifera monato (01=januaro, ktp)'
TWODIGITSECOND: 'Ducifera sekundo (00 ĝis 59)'
TWODIGITYEAR: 'Ducifera jaro'
Toggle: 'Vidigi aranĝa helpo'
MemberImportForm:
Help1: '<p>Gravaj membroj en <em>CSV-formato</em> (perkome disigitaj valoroj ). <small><a href="#" class="toggle-advanced">Vidigi spertulan uzadon</a></small></p>'
Help1: '<p>Importi membrojn en <em>CSV-formato</em> (diskomaj valoroj ). <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 uzuloj pariĝas per ilia unika atributo <em>Kodo</em>, kaj aktualiĝas per eventualaj valoroj el \n⇥la importita dosiero</li>\n⇥<li>Grupoj estas agordebla per kolumno <em>Grupoj</em>.</li>\n⇥<li>Grupoj estas identigeblaj per sia atributo <em>Kodo</em>. \nOpaj grupoj estu apartigitaj de komo. Ekzistantaj grupaj membrecoj \n⇥ne nuliĝas.</li>\n⇥</ul>\n</div>"
ResultCreated: 'Krei {count} membrojn'
ResultDeleted: 'Forigis %d membrojn'
ResultNone: 'Neniu ŝanĝo'
ResultUpdated: 'Aktualigis {count} membrojn'
MemberPassword:
PLURALNAME: 'Membraj pasvortoj'
SINGULARNAME: 'Membra pasvorto'
MemberTableField:
APPLY_FILTER: 'Apliki filtrilon'
ModelAdmin:
DELETE: Forigi
DELETEDRECORDS: 'Forigis {count} rikordojn.'
EMPTYBEFOREIMPORT: 'Anstataŭigi datumojn'
IMPORT: 'Importi el CSV'
IMPORTEDRECORDS: 'Importis {count} rikordojn.'
NOCSVFILE: 'Bonvolu foliumi por CSV-dosiero importota'
NOIMPORT: 'Nenio importota'
RESET: Reagordi
Title: 'Datenaj modeloj'
UPDATEDRECORDS: 'Aktualigis {count} rikordojn.'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Datenbazaj kolumnoj'
IMPORTSPECLINK: 'Vidigi agordaron por %s'
IMPORTSPECRELATIONS: Rilatoj
IMPORTSPECTITLE: 'Agordaro por %s'
ModelAdmin_Tools_ss:
FILTER: Filtri
IMPORT: Importi
ModelSidebar_ss:
IMPORT_TAB_HEADER: Importi
SEARCHLISTINGS: Serĉi
MoneyField:
FIELDLABELAMOUNT: Kvanto
FIELDLABELCURRENCY: Kurzo
NullableField:
IsNullLabel: 'Estas senvalora'
NumericField:
VALIDATION: '''{value}'' ne estas numero, nur numeroj estas akcepteblaj por ĉi tiu kampo'
Pagination:
Page: Paĝo
View: Vido
PasswordValidator:
LOWCHARSTRENGTH: 'Bonvole plifortigu la pasvortan aldonante la jenajn signojn: %s'
PREVPASSWORD: 'Vi jam uzis tiun pasvorton pasintece, do bonvole elektu novan pasvorton'
TOOSHORT: 'Pasvorto estas tro mallonga; ĝi devas esti almenaŭ %s signojn longa'
Permission:
AdminGroup: Administranto
CMS_ACCESS_CATEGORY: 'CMS-aliro'
FULLADMINRIGHTS: 'Ĉiuj administraj rajtoj'
FULLADMINRIGHTS_HELP: 'Implicas kaj superregas ĉiujn aliajn agorditajn permesojn.'
PLURALNAME: Permesoj
SINGULARNAME: Permeso
PermissionCheckboxSetField:
FromRoleOnGroup: 'heredita de rolo "%s" en grupo "%s"'
AssignedTo: 'agordita al "{title}"'
FromGroup: 'heredita el grupo "{title}"'
FromRole: 'heredita el rolo "{title}"'
FromRoleOnGroup: 'heredita el rolo "%s" en grupo "%s"'
PermissionRole:
OnlyAdminCanApply: 'Nur administranto povas apliki'
PLURALNAME: Roloj
SINGULARNAME: Rolo
Title: Titolo
PermissionRoleCode:
PLURALNAME: 'Permesrolaj kodoj'
PermsError: 'Ne povas agordi kodon "%s" kun privilegiaj permesoj (bezonas ADMIN-aliron)'
SINGULARNAME: 'Permesrola kodo'
Permissions:
PERMISSIONS_CATEGORY: 'Roloj kaj aliraj permesoj'
UserPermissionsIntro: 'Atribui grupojn al la uzanto modifos iliajn permesojn. Vidu la grupan sekcion por detaloj de permesoj pri unuopa grupo.'
PhoneNumberField:
VALIDATION: 'Bonvolu enigi validan telefonnumeron'
Security:
@ -209,8 +473,12 @@ eo:
ERRORPASSWORDPERMISSION: 'Vi devas ensaluti por ŝanĝi vian pasvorton!'
LOGGEDOUT: 'Vi elsalutis. Se vi volas ensaluti denove, enigu viajn legitimaĵon sube.'
LOGIN: 'Ensaluti'
LOSTPASSWORDHEADER: 'Perdis pasvorton'
NOTEPAGESECURED: 'Tiu paĝo estas sekurigita. Enigu viajn akreditaĵojn sube kaj vi aliros pluen.'
NOTERESETLINKINVALID: '<p>La pasvorta reagorda ligilo estas malvalida aŭ finiĝis.</p><p>Vi povas peti novan <a href="{link1}">ĉi tie</a> aŭ ŝanĝi vian pasvorton post <a href="{link2}">vi ensalutis</a>.</p>'
NOTERESETPASSWORD: 'Enigu vian retpoŝtan adreson kaj ni sendos al vi ligilon per kiu vi povas reagordi vian pasvorton'
PASSWORDSENTHEADER: 'Pasvorta reagorda ligilo sendiĝis al ''{email}'''
PASSWORDSENTTEXT: 'Dankon! Reagordita ligilo sendiĝis al ''{email}'', kondiĉe ke konto ekzistas por tiu retadreso.'
SecurityAdmin:
ACCESS_HELP: 'Permesi vidigi, enmeti kaj redakti uzantojn, aldone al agordi permesojn kaj rolojn al ili.'
APPLY_ROLES: 'Apliki roloj al grupoj'
@ -218,22 +486,74 @@ eo:
EDITPERMISSIONS: 'Agordi permesojn kaj IP-adresojn ĉe ĉiu grupo'
EDITPERMISSIONS_HELP: 'Eblo redakti Permesojn kaj IP-adresojn por grupo. Bezonas la permesilon "Aliro al sekcio ''Sekureco''"'
GROUPNAME: 'Grupa Nomo'
IMPORTGROUPS: 'Importi grupojn'
IMPORTUSERS: 'Importi uzulojn'
MEMBERS: Membroj
MENUTITLE: Sekureco
MemberListCaution: 'Averto: forigi membrojn el ĉi tiu listo forigos ilin el ĉiuj grupoj kaj la datumbazo.'
NEWGROUP: 'Nova Grupo'
PERMISSIONS: Permesoj
ROLES: Roloj
ROLESDESCRIPTION: 'Ĉi tiu sekcio ebligas aldoni rolojn al ĉi tiu grupo. Roloj estas logikaj grupoj de permesoj, kiuj estas redakteblaj en la langeto Roloj'
TABROLES: Roloj
Users: Uzuloj
SecurityAdmin_MemberImportForm:
BtnImport: 'Importi'
FileFieldLabel: 'CSV-dosiero <small>(Permesitaj sufiksoj: *.csv)</small>'
SilverStripeNavigator:
Auto: Aŭtomate
ChangeViewMode: 'Ŝanĝi vidigan reĝimon'
Desktop: Labortablo
DualWindowView: 'Duopa fenestro'
Edit: Redakti
EditView: 'Redakta reĝimo'
Mobile: Poŝtelefono
PreviewState: 'Antaŭvida stato'
PreviewView: 'Antaŭvida reĝimo'
Responsive: Reagema
SplitView: 'Disiga reĝimo'
Tablet: Tabulkomputilo
ViewDeviceWidth: 'Agordi antaŭvidan larĝon'
Width: larĝo
SiteTree:
TABMAIN: Ĉefaj
TableListField:
CSVEXPORT: 'Eksporti al CSV'
Print: Presi
TableListField_PageControls_ss:
OF: de
TimeField:
VALIDATEFORMAT: 'Bonvole enigu validan horan formaton ({format})'
ToggleField:
LESS: malpli
MORE: pli
UploadField:
ATTACHFILE: 'Alligi dosieron'
ATTACHFILES: 'Alligi dosierojn'
AttachFile: 'Alligi dosiero(j)n'
CHOOSEANOTHERFILE: 'Elekti alian dosieron'
CHOOSEANOTHERINFO: 'Anstataŭigi ĉi tiun dosieron per iu el la dosiera konservejo'
DELETE: 'Forigi el dosieroj'
DELETEINFO: 'Forigi porĉiame ĉi tiun dosieron el la dosiera konservejo'
DOEDIT: Konservi
DROPFILE: 'forigi dosieron'
DROPFILES: 'forigi dosierojn'
Dimensions: Dimensioj
EDIT: Redakti
EDITINFO: 'Redakti ĉi tiun dosieron'
FIELDNOTSET: 'Ne trovis informon'
FROMCOMPUTER: 'El via komputilo'
FROMCOMPUTERINFO: 'Elekti el dosieroj'
FROMFILES: 'El dosieroj'
HOTLINKINFO: 'Informo: Ĉi tiu bildo ligiĝos. Bonvole certigu ke vi havas permeson de la origina retejokreinto por fari tion.'
MAXNUMBEROFFILES: 'Superis la maksimuman nombron {count} da dosieroj.'
MAXNUMBEROFFILESONE: 'Povas alŝuti nur unu dosieron'
MAXNUMBEROFFILESSHORT: 'Povas alŝuti ĝis {count} dosierojn'
OVERWRITEWARNING: 'Jam ekzistas dosiero samnoma'
REMOVE: Forigi
REMOVEINFO: 'Forigu ĉi tiun dosieron el ĉi tie, sed ne forigu ĝin al la dosierujo'
STARTALL: 'Startigi ĉiujn'
Saved: Konservis
UPLOADSINTO: 'konservas en /{path}'
Versioned:
has_many_Versions: Versioj

View File

@ -1,6 +1,6 @@
nl:
AssetAdmin:
NEWFOLDER: Nieuwe Map
NEWFOLDER: Nieuwe map
SHOWALLOWEDEXTS: 'Toon toegestane extensies'
AssetTableField:
CREATED: 'Eerste upload'
@ -15,17 +15,17 @@ nl:
URL: URL
AssetUploadField:
ChooseFiles: 'Selecteer bestand'
DRAGFILESHERE: 'Sleep bestanden hier'
DROPAREA: 'Sleep hier'
DRAGFILESHERE: 'Sleep bestanden hierheen'
DROPAREA: 'Sleep hierheen'
EDITALL: 'Alle bewerken'
EDITANDORGANIZE: 'Bewerk en beheer'
EDITINFO: 'Bewerk alle bestanden'
FILES: Bestanden
FROMCOMPUTER: 'Selecteer bestand op computer'
FROMCOMPUTER: 'Selecteer bestanden op uw computer'
FROMCOMPUTERINFO: 'Uploaden vanaf uw computer'
TOTAL: Totaal
TOUPLOAD: 'Selecteer bestanden'
UPLOADINPROGRESS: 'Een ogenblik geduld ... upload wordt uitgevoerd'
TOUPLOAD: 'Selecteer bestanden...'
UPLOADINPROGRESS: 'Een ogenblik geduld... upload wordt uitgevoerd'
UPLOADOR: OF
BBCodeParser:
ALIGNEMENT: Uitlijning
@ -55,14 +55,14 @@ nl:
BackLink_Button_ss:
Back: Terug
BasicAuth:
ENTERINFO: 'Voer een gebruikers naam en wachtwoord in.'
ENTERINFO: 'Voer een gebruikersnaam en wachtwoord in.'
ERRORNOTADMIN: 'Die gebruiker is geen beheerder.'
ERRORNOTREC: 'De gebruikersnaam en/of wachtwoord wordt niet herkend'
ERRORNOTREC: 'De gebruikersnaam en/of het wachtwoord wordt niet herkend'
Boolean:
ANY: Elke
CMSLoadingScreen_ss:
LOADING: Laden...
REQUIREJS: 'Het CMS heeft JavaScript nodig om te werken.'
REQUIREJS: 'Het CMS vereist dat JavaScript ingeschakeld is.'
CMSMain:
ACCESS: 'Toegang tot het ''{title}'' gedeelte'
ACCESSALLINTERFACES: 'Toegang tot alle CMS onderdelen'
@ -93,8 +93,8 @@ nl:
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Gegeven Objecten'
SINGULARNAME: 'Gegeven Object'
PLURALNAME: 'Data objecten'
SINGULARNAME: 'Data object'
Date:
DAY: dag
DAYS: dagen
@ -105,17 +105,17 @@ nl:
MINS: minuten
MONTH: maand
MONTHS: maanden
SEC: second
SEC: seconde
SECS: seconden
TIMEDIFFAGO: '{difference} geleden'
TIMEDIFFIN: '{difference} geleden'
TIMEDIFFIN: 'in {difference}'
YEAR: jaar
YEARS: jaren
DateField:
NOTSET: 'niet ingesteld'
TODAY: vandaag
VALIDDATEFORMAT2: 'Vul een geldige datumformaat in ({format})'
VALIDDATEMAXDATE: 'De datum moet nieuwer of gelijk zijn aan de minimale datum ({date})'
VALIDDATEFORMAT2: 'Vul een geldig datumformaat in ({format})'
VALIDDATEMAXDATE: 'De datum moet ouder of gelijk zijn aan de maximale datum ({date})'
VALIDDATEMINDATE: 'De datum moet nieuwer of gelijk zijn aan de minimale datum ({date})'
DatetimeField:
NOTSET: 'Niet ingesteld'
@ -125,7 +125,7 @@ nl:
CHOOSE: (Kies)
CHOOSESEARCH: '(Kies of zoek)'
EmailField:
VALIDATION: 'Gelieve een email adres in te voeren.'
VALIDATION: 'Gelieve een e-mailadres in te voeren.'
Enum:
ANY: Elke
File:
@ -135,14 +135,14 @@ nl:
DmgType: 'Apple disk image'
DocType: 'Word document'
Filename: 'Bestandsnaam '
GifType: 'GIF afbeelding - voor diagrammen'
GifType: 'GIF afbeelding - goed voor diagrammen'
GzType: 'GZIP gecomprimeerd bestand'
HtlType: 'HTML bestand'
HtmlType: 'HTML bestand'
INVALIDEXTENSION: 'Extensie is niet toegestaan (Toegestaan: {extensions})'
INVALIDEXTENSIONSHORT: 'Extensie is niet toegestaan'
IcoType: 'Icoon bestand'
JpgType: 'JPG afbeelding - voor foto'
JpgType: 'JPEG afbeelding - goed voor foto''s'
JsType: 'Javascript bestand'
Mp3Type: 'MP3 audio bestand'
MpgType: 'MPEG video bestand'
@ -151,10 +151,10 @@ nl:
Name: Naam
PLURALNAME: Bestanden
PdfType: 'Adobe Acrobat PDF bestand'
PngType: 'PNG adbeelding - voor allerlei afbeeldingen'
PngType: 'PNG afbeelding - goed voor allerlei afbeeldingen'
SINGULARNAME: Bestand
TOOLARGE: 'Bestandsgrootte is te groot, maximaal {size} toegestaan'
TOOLARGESHORT: 'Bestandsgrootte is hoger dan {size}'
TOOLARGE: 'Bestand is te groot, maximaal {size} toegestaan'
TOOLARGESHORT: 'Bestand is groter dan {size}'
TiffType: 'Tagged beeldformaat'
Title: 'Titel '
WavType: 'WAV audio bestand'
@ -171,19 +171,19 @@ nl:
TEXT2: 'wachtwoord reset link'
TEXT3: voor
Form:
CSRF_FAILED_MESSAGE: "Er lijkt een technisch probleem te zijn. Klikt u op de knop terug, vernieuw uw browser, en probeer het opnieuw."
CSRF_FAILED_MESSAGE: "Er lijkt een technisch probleem te zijn. Klik op de knop 'terug', ververs de pagina, en probeer het opnieuw."
FIELDISREQUIRED: '{name} is verplicht'
SubmitBtnLabel: Gaan
SubmitBtnLabel: Versturen
VALIDATIONCREDITNUMBER: 'Gelieve uw credit card number {number} juist in te vullen'
VALIDATIONNOTUNIQUE: 'De ingevoerde waarde is niet uniek'
VALIDATIONPASSWORDSDONTMATCH: 'Wachtwoorden komen niet overeen'
VALIDATIONPASSWORDSNOTEMPTY: 'Wachtwoorden mogen niet leeg zijn'
VALIDATIONSTRONGPASSWORD: 'Wachtwoorden moeten bestaan uit minstens één cijfer en één alfanumeriek karakter.'
VALIDATOR: Controleur
VALIDCURRENCY: 'Vul een geldige valuta in'
VALIDATOR: Validator
VALIDCURRENCY: 'Vul een geldige munteenheid in'
CSRF_EXPIRED_MESSAGE: 'Uw sessie is verlopen. Verzend het formulier opnieuw.'
FormField:
Example: 'e.g. %s'
Example: 'bv. %s'
NONE: geen
GridAction:
DELETE_DESCRIPTION: Verwijderen
@ -193,9 +193,9 @@ nl:
Add: '{name} toevoegen'
Filter: Filter
FilterBy: 'Filteren'
Find: Zoek
Find: Zoeken
LEVELUP: 'Niveau omhoog'
LinkExisting: 'Bestaande link'
LinkExisting: 'Koppel een bestaand item'
NewRecord: 'Nieuw %s'
NoItemsFound: 'Geen items gevonden.'
PRINTEDAT: 'Geprint op'
@ -203,13 +203,13 @@ nl:
PlaceHolder: 'Zoek {type}'
PlaceHolderWithLabels: 'Zoek {type} op {name}'
RelationSearch: 'Zoek relatie'
ResetFilter: Herstellen
ResetFilter: Resetten
GridFieldAction_Delete:
DeletePermissionsFailure: 'Onvoldoende rechten om te verwijderen'
EditPermissionsFailure: 'Geen permissie'
EditPermissionsFailure: 'Geen toelating om te ontkoppelen'
GridFieldDetailForm:
CancelBtn: Annuleren
Create: Creëren
Create: Aanmaken
Delete: Verwijderen
DeletePermissionsFailure: 'Onvoldoende rechten om te verwijderen'
Deleted: '%s %s verwijderd'
@ -231,19 +231,19 @@ nl:
NoRoles: 'Geen rollen gevonden'
PLURALNAME: Groepen
Parent: 'Bovenliggende groep'
RolesAddEditLink: 'Toevoegen/wijzigingen rollen'
RolesAddEditLink: 'Rollen beheren'
SINGULARNAME: Groep
Sort: 'Sorteer-richting'
has_many_Permissions: Rechten
many_many_Members: Leden
GroupImportForm:
Help1: '<p>Importeer en of meerdere groepen in <em>CSV</em> formaat (Kommagescheiden bestandsformaat). <small><a href="#" class="toggle-advanced">Toon geavanceerd gebruik</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Geavanceerd gebruik</h4>\n<ul>\n<li>Toegestane kolommen: <em>%s</em></li>\n<li>Bestaande leden worden geïdentificeerd door middel van hun unieke <em>Code</em> waarde en aangepast met de nieuwe waarden van het geïmporteerde bestand</li>\n<li>Groepen kunnen toegewezen worden met de <em>Groups</em> kolom. Groepen worden geïdentificeerd met hun <em>Code</em> waarde en meerdere groepen kunnen worden gescheiden met een komma. Bestaande groep lidmaatschappen worden niet gewist.</li>\n</ul>\n</div>\n</ul>\n</div>"
Help1: '<p>Importeer een of meerdere groepen in <em>CSV</em>-formaat (comma-separated values). <small><a href="#" class="toggle-advanced">Toon geavanceerd gebruik</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Geavanceerd gebruik</h4>\n<ul>\n<li>Toegestane kolommen: <em>%s</em></li>\n<li>Bestaande groepen worden geïdentificeerd door middel van hun unieke <em>Code</em>-waarde, en aangepast met de nieuwe waarden vanuit het geïmporteerde bestand</li>\n<li>Groepshiërarchiën kunnen aangemaakt worden door een <em>ParentCode</em>-kolom te gebruiken</li>\n<li>Toegangscodeskunnen toegewezen worden met de <em>PermissionCode</em> kolom. Bestaande toegangscodes worden niet verwijderd.</li>\n</ul>\n</div>"
ResultCreated: '{count} groepen aangemaakt'
ResultDeleted: '%d groepen verwijderd'
ResultUpdated: '%d groepen aangepast'
Hierarchy:
InfiniteLoopNotAllowed: 'Oneindige lus gevonden in "{type}" hiërarchie. Wijzig de niveau hoger om dit op te lossen'
InfiniteLoopNotAllowed: 'Oneindige lus gevonden in "{type}" hiërarchie. Wijzig het hogere niveau om dit op te lossen'
HtmlEditorField:
ADDURL: 'Voeg URL toe'
ADJUSTDETAILSDIMENSIONS: 'Details en afmetingen'
@ -255,32 +255,32 @@ nl:
BUTTONUpdate: Bijwerken
CAPTIONTEXT: 'Onderschrift'
CSSCLASS: 'Uitlijning / stijl'
CSSCLASSCENTER: 'Gecentreerd, op zichzelf staand.'
CSSCLASSLEFT: 'Aan de linkerkant, met tekst eromheen.'
CSSCLASSLEFTALONE: 'Links, op zichzelf staand.'
CSSCLASSRIGHT: 'Aan de rechterkant, met tekst eromheen.'
CSSCLASSCENTER: 'Gecentreerd.'
CSSCLASSLEFT: 'Aan de linkerkant, met tekst naast.'
CSSCLASSLEFTALONE: 'Aan de linkerkant, zonder iets naast.'
CSSCLASSRIGHT: 'Aan de rechterkant, met tekst naast.'
DETAILS: Details
EMAIL: 'Emailadres'
EMAIL: 'E-mailadres'
FILE: Bestand
FOLDER: Map
FROMCMS: 'Vanaf CMS'
FROMCMS: 'Uit het CMS'
FROMCOMPUTER: 'Vanaf computer'
FROMWEB: 'Vanaf een website'
FindInFolder: 'Zoek in map'
IMAGEALT: 'Alternatieve tekst (alt tekst)'
IMAGEALTTEXT: 'Alternatieve tekst (alt-tekst) - wordt gebruikt als de afbeelding niet geladen kan worden '
IMAGEALTTEXTDESC: 'Voor schermlezers of als afbeelding niet weergegeven kan worden '
IMAGEALTTEXTDESC: 'Voor schermlezers, of als de afbeelding niet weergegeven kan worden '
IMAGEDIMENSIONS: Dimensies
IMAGEHEIGHTPX: Hoogte
IMAGETITLE: 'Titel tekst (tooltip) - Toon extra informatie over de afbeelding'
IMAGETITLETEXT: 'Titel tekst (tooltip)'
IMAGETITLETEXTDESC: 'Titel tekst (tooltip) - Toon extra informatie over de afbeelding'
IMAGETITLE: 'Tooltip (title) - Toon extra informatie over de afbeelding'
IMAGETITLETEXT: 'Tooltip (title)'
IMAGETITLETEXTDESC: 'Toon extra informatie over de afbeelding'
IMAGEWIDTHPX: Breedte
INSERTMEDIA: 'Invoegen'
LINK: 'Link'
INSERTMEDIA: 'Media invoegen'
LINK: 'Link invoegen'
LINKANCHOR: 'Anker op deze pagina'
LINKDESCR: 'Link omschrijving'
LINKEMAIL: 'Emailadres'
LINKDESCR: 'Linkomschrijving'
LINKEMAIL: 'E-mailadres'
LINKEXTERNAL: 'Een andere website'
LINKFILE: 'Een bestand downloaden'
LINKINTERNAL: 'Pagina op deze site'
@ -299,67 +299,67 @@ nl:
Image_iframe_ss:
TITLE: 'Afbeelding uploaden'
LeftAndMain:
CANT_REORGANISE: 'Je hebt geen rechten hiervoor'
CANT_REORGANISE: 'U hebt geen rechten om de pagina''s op het Top niveau aan te passen. Uw aanpassing is niet opgeslagen. '
DELETED: Verwijderd.
DropdownBatchActionsDefault: Acties
HELP: Help
PAGETYPE: 'Pagina type: '
PERMAGAIN: 'U bent uitgelogd uit het CMS. Als U weer wilt inloggen vul dan uw gebruikersnaam en wachtwoord hier beneden in.'
PERMALREADY: 'Helaas, dat deel van het CMS is niet toegankelijk voor U. Hieronder kunt U als iemand anders inloggen.'
PERMDEFAULT: 'Geef uw e-mailadres en wachtwoord voor toegang tot het CMS.'
PLEASESAVE: 'Deze pagina kon niet bijgewerkt worden, omdat deze nog niet is bewaard.'
PERMAGAIN: 'U bent uitgelogd uit het CMS. Als u weer wilt inloggen vul dan uw gebruikersnaam en wachtwoord hieronder in.'
PERMALREADY: 'Helaas, dat deel van het CMS is niet toegankelijk voor u. Hieronder kunt u als iemand anders inloggen.'
PERMDEFAULT: 'Geef uw e-mailadres en wachtwoord in om in te loggen op het CMS.'
PLEASESAVE: 'Deze pagina kon niet bijgewerkt worden, omdat deze nog niet is opgeslagen.'
PreviewButton: Voorbeeld
REORGANISATIONSUCCESSFUL: 'Menu-indeling is aangepast'
SAVEDUP: Opgeslagen
ShowAsList: 'Laat als lijst zien'
SAVEDUP: Opgeslagen.
ShowAsList: 'laat als lijst zien'
TooManyPages: 'Te veel pagina''s'
ValidationError: 'Validatiefout'
VersionUnknown: onbekend
VersionUnknown: Onbekend
LeftAndMain_Menu_ss:
Hello: Hallo
LOGOUT: 'Uitloggen'
LoginAttempt:
Email: 'Email adres '
IP: 'IP Adres'
Email: 'E-mailadres '
IP: 'IP adres'
PLURALNAME: 'Pogingen om in te loggen'
SINGULARNAME: 'Pogingen om in te loggen'
SINGULARNAME: 'Poging om in te loggen'
Status: Status
Member:
ADDGROUP: 'Groep toevoegen'
BUTTONCHANGEPASSWORD: 'Wachtwoord veranderen'
BUTTONLOGIN: 'Inloggen'
BUTTONLOGINOTHER: 'Als iemand anders inloggen'
BUTTONLOSTPASSWORD: 'Ik ben mijn wachtwoord vergeten...'
BUTTONLOSTPASSWORD: 'Ik ben mijn wachtwoord vergeten'
CANTEDIT: 'Je hebt geen rechten hiervoor'
CONFIRMNEWPASSWORD: 'Bevestig het nieuwe wachtwoord'
CONFIRMPASSWORD: 'Bevestig wachtwoord'
DATEFORMAT: 'Datum formaat'
DefaultAdminFirstname: 'Standaard Beheerder'
DefaultDateTime: Standaard
DefaultDateTime: standaard
EMAIL: Email
EMPTYNEWPASSWORD: 'Het nieuwe wachtwoord mag niet leeg zijn, probeer opnieuw'
ENTEREMAIL: 'Typ uw e-mailadres om een link te ontvangen waarmee u uw wachtwoord kunt resetten.'
ERRORLOCKEDOUT2: 'Uw account is tijdelijk uitgeschakeld als gevolg van te veel mislukte pogingen om in te loggen. Probeer het over {count} minuten aub.'
ERRORNEWPASSWORD: 'Het nieuwe wachtwoord komt niet overeen met de bevestiging, probeer het nogmaals'
ERRORPASSWORDNOTMATCH: 'Huidige wachtwoord kom niet overeen, probeer het nogmaals'
ERRORLOCKEDOUT2: 'Uw account is tijdelijk uitgeschakeld als gevolg van te veel mislukte pogingen om in te loggen. Probeer het over {count} minuten opnieuw.'
ERRORNEWPASSWORD: 'Het nieuwe wachtwoord komt niet overeen, probeer het nogmaals'
ERRORPASSWORDNOTMATCH: 'Uw huidige wachtwoord kom niet overeen, probeer het nogmaals'
ERRORWRONGCRED: 'De ingevulde gegevens lijken niet correct. Probeer het nog een keer.'
FIRSTNAME: 'Voornaam'
INTERFACELANG: 'Interface Taal'
INTERFACELANG: 'Interface taal'
INVALIDNEWPASSWORD: 'Dit is geen goed wachtwoord: {password}'
LOGGEDINAS: 'Je bent ingelogd als {name}.'
NEWPASSWORD: 'Nieuw Wachtwoord'
LOGGEDINAS: 'U bent ingelogd als {name}.'
NEWPASSWORD: 'Nieuw wachtwoord'
NoPassword: 'Er is geen wachtwoord voor deze gebruiker.'
PASSWORD: Wachtwoord
PLURALNAME: Leden
REMEMBERME: 'Wachtwoord onthouden voor de volgende keer?'
SINGULARNAME: Lid
SUBJECTPASSWORDCHANGED: 'Uw wachtwoord is veranderd'
SUBJECTPASSWORDRESET: 'Link om Uw wachtwoord opnieuw aan te maken'
SUBJECTPASSWORDRESET: 'Link om uw wachtwoord opnieuw aan te maken'
SURNAME: Achternaam
TIMEFORMAT: 'Tijd formaat'
VALIDATIONMEMBEREXISTS: 'Er bestaat al een lid met dit emailadres, %s'
ValidationIdentifierFailed: 'Een bestaande gebruiker #{id} kan niet dezelfde unieke velden hebben ({name} = {value}))'
WELCOMEBACK: 'Welkom terug {firstname}'
WELCOMEBACK: 'Welkom terug, {firstname}'
YOUROLDPASSWORD: 'Uw oude wachtwoord'
belongs_many_many_Groups: Groepen
db_LastVisited: 'Datum van het laatste bezoek'
@ -367,31 +367,31 @@ nl:
db_LockedOutUntil: 'Gesloten tot'
db_NumVisit: 'Aantal bezoeken'
db_Password: Wachtwoord
db_PasswordExpiry: 'Wachtwoord Vervaldatum'
db_PasswordExpiry: 'Wachtwoord vervaldatum'
MemberAuthenticator:
TITLE: 'Email &amp; Wachtwoord'
MemberDatetimeOptionsetField:
AMORPM: 'AM (Ante meridiem) of PM (Post meridiem)'
Custom: Aangepast
DATEFORMATBAD: 'Datum is niet correct opgegeven'
DAYNOLEADING: 'Dag van de maand zonder voorloop-nul'
DIGITSDECFRACTIONSECOND: 'Een of meer cijfers die een decimale fractie van een seconde'
FOURDIGITYEAR: 'jaar (yyyy)'
FULLNAMEMONTH: 'Volledige naam van de maand (Bijv. Juni)'
DAYNOLEADING: 'Dag van de maand zonder voorloopnul'
DIGITSDECFRACTIONSECOND: 'Een of meer cijfers die een decimale fractie van een seconde voorstellen'
FOURDIGITYEAR: '4-cijfers jaar'
FULLNAMEMONTH: 'Volledige naam van de maand (bv. juni)'
HOURNOLEADING: 'Uur zonder voorloopnul'
MINUTENOLEADING: 'Minuut zonder voorloopnul'
MONTHNOLEADING: 'Dag van de maand zonder voorloop-nul'
MONTHNOLEADING: 'Dag van de maand zonder voorloopnul'
Preview: Voorbeeld
SHORTMONTH: 'Korte naam van de maand (Bijv. Jun)'
TWODIGITDAY: 'Dag van de maand (met voorloop-nul)'
TWODIGITHOUR: 'Twee cijfer van het uur (00 tot 23)'
TWODIGITMINUTE: 'Minuten met voorloop-nul (00 tot 59)'
TWODIGITMONTH: 'Maand in twee cijfers (01 = januari, enz.)'
TWODIGITSECOND: 'Twee cijfer van het uur (00 tot 23)'
TWODIGITYEAR: 'Twee-cijferig jaar'
SHORTMONTH: 'Korte naam van de maand (bv. jun)'
TWODIGITDAY: 'Dag van de maand (met voorloopnul)'
TWODIGITHOUR: '2-cijfers uur (00 tot 23)'
TWODIGITMINUTE: '2-cijfers minuten (00 tot 59)'
TWODIGITMONTH: '2-cijfers maand (01 = januari, enz.)'
TWODIGITSECOND: '2-cijfers seconden (00 tot 59)'
TWODIGITYEAR: '2-cijfers jaar'
Toggle: 'Toon opmaak hulp'
MemberImportForm:
Help1: '<p>Importeer leden in <em>CSV</em> formaat (Kommagescheiden bestandsformaat). <small><a href="#" class="toggle-advanced">Toon geavanceerd gebruik</a></small></p>'
Help1: '<p>Importeer leden in <em>CSV</em>-formaat (comma-separated values). <small><a href="#" class="toggle-advanced">Toon geavanceerd gebruik</a></small></p>'
Help2: "<div class=\"advanced\">\n\t<h4>Advanced usage</h4>\n\t<ul>\n\t<li>Allowed columns: <em>%s</em></li>\n\t<li>Existing users are matched by their unique <em>Code</em> property, and updated with any new values from\n\tthe imported file.</li>\n\t<li>Groups can be assigned by the <em>Groups</em> column. Groups are identified by their <em>Code</em> property,\n\tmultiple groups can be separated by comma. Existing group memberships are not cleared.</li>\n\t</ul>\n</div>"
ResultCreated: '{count} leden aangemaakt'
ResultDeleted: '%d leden verwijderd'
@ -404,20 +404,20 @@ nl:
APPLY_FILTER: 'Filter toepassen'
ModelAdmin:
DELETE: Verwijderen
DELETEDRECORDS: '{count} records verwijderd'
DELETEDRECORDS: '{count} records verwijderd.'
EMPTYBEFOREIMPORT: 'Vervang gegevens'
IMPORT: 'Importeren vanuit CSV'
IMPORTEDRECORDS: '{count} records geïmporteerd'
NOCSVFILE: 'Selecteer een CSV bestand op uw computer om te importeren'
NOIMPORT: 'Niks om te importeren'
RESET: Herstel
Title: 'Gegevens modellen'
IMPORTEDRECORDS: '{count} records geïmporteerd.'
NOCSVFILE: 'Selecteer een CSV bestand om te importeren'
NOIMPORT: 'Niets om te importeren'
RESET: Reset
Title: 'Data modellen'
UPDATEDRECORDS: '{count} records bijgewerkt'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Database kolommen'
IMPORTSPECLINK: 'Toon specificaties van %s'
IMPORTSPECRELATIONS: Relaties
IMPORTSPECTITLE: 'Specificaties van %s'
IMPORTSPECTITLE: 'Specificatie van %s'
ModelAdmin_Tools_ss:
FILTER: Filter
IMPORT: Importeren
@ -426,16 +426,16 @@ nl:
SEARCHLISTINGS: Zoeken
MoneyField:
FIELDLABELAMOUNT: Aantal
FIELDLABELCURRENCY: Valuta
FIELDLABELCURRENCY: Munteenheid
NullableField:
IsNullLabel: 'is nul'
IsNullLabel: 'Is null'
NumericField:
VALIDATION: '''{value}'' is geen getal, enkel getallen worden door dit veld geaccepteerd'
Pagination:
Page: Pagina
View: Bekijk
PasswordValidator:
LOWCHARSTRENGTH: 'Maak a.u.b. uw wachtwoord sterker door meer van de volgende karakters te gebruiken: %s'
LOWCHARSTRENGTH: 'Maak a.u.b. uw wachtwoord sterker door enkele van de volgende karakters te gebruiken: %s'
PREVPASSWORD: 'U heeft dit wachtwoord in het verleden al gebruikt, kies a.u.b. een nieuw wachtwoord.'
TOOSHORT: 'Het wachtwoord is te kort, het moet minimaal %s karakters hebben'
Permission:
@ -461,29 +461,29 @@ nl:
SINGULARNAME: 'Machtigingen rol code'
Permissions:
PERMISSIONS_CATEGORY: 'Rollen en toegangsrechten'
UserPermissionsIntro: 'Groepen aan deze gebruiker toewijzen zullen de permissies aanpassen. Zie de sectie groepen voor meer informatie over machtigingen voor afzonderlijke groepen.'
UserPermissionsIntro: 'Groepen aan deze gebruiker toewijzen zullen diens permissies aanpassen. Zie de sectie Groepen voor meer informatie over machtigingen voor afzonderlijke groepen.'
PhoneNumberField:
VALIDATION: 'Voer een geldig telefoonnummer in'
Security:
ALREADYLOGGEDIN: 'Je hebt niet de juiste rechten, om deze pagina te kunnen bekijken. Als je een ander account met de juiste rechten hebt, kun je hier opnieuw inloggen.'
ALREADYLOGGEDIN: 'U hebt geen toegang tot deze pagina. Als u een andere account met de nodige rechten hebt, kan u hieronder opnieuw inloggen.'
BUTTONSEND: 'Nieuw wachtwoord aanmaken'
CHANGEPASSWORDBELOW: 'U kunt Uw wachtwoord hier beneden veranderen.'
CHANGEPASSWORDHEADER: 'Verander Uw wachtwoord'
CHANGEPASSWORDBELOW: 'U kunt uw wachtwoord hieronder veranderen.'
CHANGEPASSWORDHEADER: 'Verander uw wachtwoord'
ENTERNEWPASSWORD: 'Voer een nieuw wachtwoord in.'
ERRORPASSWORDPERMISSION: 'U moet ingelogd zijn om Uw wachtwoord te kunnen veranderen!'
LOGGEDOUT: 'U bent uitgelogd. Als U weer wilt inloggen kunt U Uw gegevens hier beneden invoeren.'
ERRORPASSWORDPERMISSION: 'U moet ingelogd zijn om uw wachtwoord te kunnen veranderen!'
LOGGEDOUT: 'U bent uitgelogd. Als u weer wilt inloggen kunt u uw gegevens hieronder invoeren.'
LOGIN: 'Meld aan'
LOSTPASSWORDHEADER: 'Wachtwoord vergeten'
NOTEPAGESECURED: 'Deze pagina is beveiligd. Voer Uw gegevens in en U wordt automatisch doorgestuurd.'
NOTERESETLINKINVALID: '<p>De link om uw wachtwoord te kunnen wijzigen is niet meer geldig.</p><p>U kunt het <a href="{link1}">opnieuw proberen</a> of uw wachtwoord aanpassen door <a href="{link2}">in te loggen</a>.</p>'
NOTEPAGESECURED: 'Deze pagina is beveiligd. Voer uw gegevens in en u wordt automatisch doorgestuurd.'
NOTERESETLINKINVALID: '<p>De link om uw wachtwoord te kunnen wijzigen is niet meer geldig.</p><p>U kunt <a href="{link1}">een nieuwe link aanvragen</a> of uw wachtwoord aanpassen door <a href="{link2}">in te loggen</a>.</p>'
NOTERESETPASSWORD: 'Voer uw e-mailadres in en we sturen een link waarmee u een nieuw wachtwoord kunt instellen.'
PASSWORDSENTHEADER: 'Wachtwoord herstel link verzonden naar {email}'
PASSWORDSENTTEXT: 'Bedankt! Er is een link verstuurt naar {email} om uw wachtwoord te herstellen.'
PASSWORDSENTTEXT: 'Bedankt! Er is een link verstuurd naar {email} om uw wachtwoord opnieuw in te stellen, in de veronderstelling dat er een account bestaat voor dit e-mailadres.'
SecurityAdmin:
ACCESS_HELP: 'Bevoegdheid voor bekijken, toevoegen en bewerken van leden, en toewijzen van rechten en rollen aan hen.'
APPLY_ROLES: 'Pas rollen toe aan groepen'
APPLY_ROLES_HELP: 'Bewerkingsmogelijkheid van rollen voor groepen. Heeft rechten voor "Beveiligingsectie" nodig.'
EDITPERMISSIONS: 'Bewerk rechten en IP-adressen bij elke groep'
APPLY_ROLES_HELP: 'Bewerkingsmogelijkheid van rollen voor groepen. Heeft rechten voor "Gebruikerssectie" nodig.'
EDITPERMISSIONS: 'Bewerk rechten voor groepen'
EDITPERMISSIONS_HELP: 'Bewerkingsmogelijkheid van Rechten en IP adressen voor groepen. Heeft rechten voor "Beveiligingsectie" nodig.'
GROUPNAME: 'Groep naam'
IMPORTGROUPS: 'Importeer groepen'
@ -498,7 +498,7 @@ nl:
TABROLES: Rollen
Users: Gebruikers
SecurityAdmin_MemberImportForm:
BtnImport: 'Importeer'
BtnImport: 'Importeer vanuit CSV'
FileFieldLabel: 'CSV Bestand <small>(Toegestane extensies: *.csv)</small>'
SilverStripeNavigator:
Auto: Automatisch
@ -523,7 +523,7 @@ nl:
TableListField_PageControls_ss:
OF: van
TimeField:
VALIDATEFORMAT: 'Vul een geldige datumformaat in ({format})'
VALIDATEFORMAT: 'Vul een geldig datumformaat in ({format})'
ToggleField:
LESS: minder
MORE: meer
@ -534,17 +534,17 @@ nl:
CHOOSEANOTHERFILE: 'Kies een ander bestand'
CHOOSEANOTHERINFO: 'Vervang dit bestand met een ander uit de bestandsopslag'
DELETE: 'Volledig verwijderen'
DELETEINFO: 'Verwijder dit bestand uit bestandsopslag van de website.'
DELETEINFO: 'Verwijder dit bestand definitief van de bestandsopslag'
DOEDIT: Opslaan
DROPFILE: 'Bestand hiernaar toe slepen'
DROPFILES: 'Sleep hier je bestanden'
Dimensions: Afmeting
DROPFILE: 'sleep een bestand hierheen'
DROPFILES: 'sleep hier bestanden heen'
Dimensions: Afmetingen
EDIT: Bewerken
EDITINFO: 'Bewerk dit bestand'
FIELDNOTSET: 'Bestandsinformatie niet gevonden'
FROMCOMPUTER: 'Vanaf computer'
FROMCOMPUTERINFO: 'Kies uit bestanden'
FROMFILES: 'Bestaande bestanden'
FROMFILES: 'Vanuit bestaande bestanden'
HOTLINKINFO: 'Info: Deze afbeelding wordt hotlinked. Zorg ervoor dat u de machtigingen van de oorspronkelijke site maker om dit te doen.'
MAXNUMBEROFFILES: 'Maximale aantal van {count} bestand(en) overschreden.'
MAXNUMBEROFFILESONE: 'Kan slechts één bestand uploaden'
@ -554,6 +554,6 @@ nl:
REMOVEINFO: 'Verwijder (ontkoppel) dit bestand, maar behoud het in bestandsopslag van de website.'
STARTALL: 'Start alle'
Saved: Opgeslagen
UPLOADSINTO: 'Wordt opgeslagen in /{path}'
UPLOADSINTO: 'wordt opgeslagen in /{path}'
Versioned:
has_many_Versions: Versies

View File

@ -1,15 +1,18 @@
sl:
AssetAdmin:
NEWFOLDER: Nova mapa
SHOWALLOWEDEXTS: 'Pokaži dovoljene razširitve'
AssetTableField:
CREATED: 'Naloženo na začetku'
DIM: Dimenzije
FILENAME: Ime datoteke
FOLDER: Mapa
LASTEDIT: 'Zadnje naloženo'
OWNER: Lastnik
SIZE: 'Velikost'
SIZE: 'Velikost datoteke'
TITLE: Naslov
TYPE: 'Tip'
URL: URL
AssetUploadField:
ChooseFiles: 'Izberite datoteke'
DRAGFILESHERE: 'Potegnite datoteke na to mesto '
@ -64,6 +67,8 @@ sl:
ACCESSALLINTERFACES: 'Dostop do vseh sklopov CMS'
ACCESSALLINTERFACESHELP: 'Omogoča bolj specifične nastavitve za možnosti dostop.'
SAVE: Shrani
CMSPageHistoryController_versions_ss:
PREVIEW: 'Predogled spletne strani'
CMSProfileController:
MENUTITLE: 'Moj profil'
ChangePasswordEmail_ss:
@ -77,6 +82,8 @@ sl:
BETWEEN: 'Geslo mora biti dolgo od {min} do {max} znakov.'
MAXIMUM: 'Geslo je lahko dolgo največ {max} znakov.'
SHOWONCLICKTITLE: 'Spremeni geslo'
ContentController:
NOTLOGGEDIN: 'Neprijavljen'
CreditCardField:
FIRST: prvič
FOURTH: četrtič
@ -85,16 +92,35 @@ sl:
DataObject:
PLURALNAME: 'Podatkovni objekti'
SINGULARNAME: 'Podatkovni objekt'
Date:
DAY: dan
DAYS: dnevi
HOUR: ura
HOURS: ure
LessThanMinuteAgo: 'manj kot minuta'
MIN: minuta
MINS: inute
MONTH: mesec
MONTHS: meseci
SEC: sekunda
SECS: sekunde
TIMEDIFFAGO: 'pred {difference}'
TIMEDIFFIN: 'v {difference}'
YEAR: leto
YEARS: leta
DateField:
NOTSET: 'ni nastavljeno'
TODAY: danes
VALIDDATEFORMAT2: 'Prosim, vnesite ustrezno obliko datuma ({format})'
VALIDDATEMAXDATE: 'Datum mora biti starejši ali enak največjemu dovoljenemu datumu ({date})'
VALIDDATEMINDATE: 'Datum mora biti novejši ali enak najmanjšemu dovoljenemu datumu ({date})'
DatetimeField:
NOTSET: 'Ni nastavljeno'
Director:
INVALID_REQUEST: 'Napačna zahteva'
DropdownField:
CHOOSE: (Izberi)
CHOOSESEARCH: '(Izberi ali išči)'
EmailField:
VALIDATION: 'Prosim, vpišite e-naslov.'
Enum:
@ -106,6 +132,7 @@ sl:
DocType: 'Word datoteka'
Filename: Ime datoteke
GifType: 'GIF slika - primerna za diagrame'
GzType: 'GZIP stisnjena datoteka'
HtlType: 'HTML datoteka'
HtmlType: 'HTML datoteka'
INVALIDEXTENSION: 'Podaljševanje ni dovoljeno (dovoljeno: {extensions})'
@ -128,6 +155,8 @@ sl:
WavType: 'WAV avdio datoteka'
XlsType: 'Excel preglednica'
ZipType: 'ZIP stisnjena datoteka'
Filesystem:
SYNCRESULTS: 'Sinhronizacija končana: {createdcount} ustvarjenih elementov, {deletedcount} izbrisanih elementov'
Folder:
PLURALNAME: Mape
SINGULARNAME: Mapa
@ -137,6 +166,8 @@ sl:
TEXT2: 'povezava za ponastavitev gesla'
TEXT3: za
Form:
CSRF_FAILED_MESSAGE: "Verjetno je prišlo do tehničnih napak. Kliknite \"the back button\", osvežite brskalnik in poskusite ponovno."
FIELDISREQUIRED: '{name} je potrebno'
SubmitBtnLabel: Naprej
VALIDATIONCREDITNUMBER: 'Prosim, preverite, da ste vnesli številko kreditne kartice {number} pravilno.'
VALIDATIONNOTUNIQUE: 'Vpisana vrednost ni unikatna'
@ -145,7 +176,9 @@ sl:
VALIDATIONSTRONGPASSWORD: 'Geslo naj vsebuje vsaj eno črko in vsaj eno številko.'
VALIDATOR: Preverjanje
VALIDCURRENCY: 'Prosim, vnesite pravo valuto.'
CSRF_EXPIRED_MESSAGE: 'Vaša seja je potekla. Prosimo ponovno oddajte obrazec '
FormField:
Example: 'npr. %s'
NONE: brez
GridAction:
DELETE_DESCRIPTION: Izbriši
@ -153,10 +186,17 @@ sl:
UnlinkRelation: Odstrani povezavo
GridField:
Add: 'Dodaj {name}'
Filter: Filter
FilterBy: 'Filtriraj po'
Find: Poišči
LinkExisting: 'Poveži na'
NewRecord: 'Novih %s'
NoItemsFound: 'Ni rezultatov'
PRINTEDAT: 'Tiskano pri'
PRINTEDBY: 'Natisnil'
PlaceHolder: 'Poišči {type} '
PlaceHolderWithLabels: 'Poišči {type} glede na {name}'
RelationSearch: 'Povezano iskanje'
ResetFilter: Ponastavi
GridFieldAction_Delete:
DeletePermissionsFailure: 'Ni dovoljenja za brisanje'
@ -165,13 +205,22 @@ sl:
Create: Ustvari
Delete: Izbriši
DeletePermissionsFailure: 'Ni dovoljenja za brisanje'
Deleted: 'Izbrisanih %s %s'
Save: Shrani
Saved: 'Shranjeno {name} {link}'
GridFieldEditButton_ss:
EDIT: Uredi
GridFieldItemEditView:
Go_back: 'Nazaj'
Group:
AddRole: 'Skupinam pripiši vloge'
Code: 'Koda skupine'
DefaultGroupTitleAdministrators: Administratorji
DefaultGroupTitleContentAuthors: 'Avtorji vsebine'
Description: Opis
GroupReminder: 'Če izberete nadrejeno skupino, bo ta skupina prevzela vse njene vloge'
Locked: 'Zaklenjeno za urejanje?'
NoRoles: 'Ni najdenih vlog'
PLURALNAME: Skupine
Parent: 'Nadrejena skupina'
RolesAddEditLink: 'Dodaj in uredi vloge'
@ -187,6 +236,7 @@ sl:
HtmlEditorField:
ADDURL: 'Dodaj URL'
ANCHORVALUE: Sidro
BUTTONADDURL: 'Dodaj url'
BUTTONINSERT: Vstavi
BUTTONINSERTLINK: 'Vstavi povezavo'
BUTTONREMOVELINK: 'Odstrani povezavo'
@ -211,6 +261,7 @@ sl:
IMAGETITLETEXT: 'Naslov (tooltip)'
IMAGETITLETEXTDESC: 'Za dodatne informacije o sliki'
IMAGEWIDTHPX: Širina
INSERTMEDIA: 'Vstavi multimedijsko datoteko'
LINK: 'Povezava'
LINKANCHOR: 'Sidro na tej strani'
LINKDESCR: 'Opis povezave'
@ -221,6 +272,8 @@ sl:
LINKOPENNEWWIN: 'Naj se povezava odpira v novem oknu?'
LINKTO: 'Poveži na'
PAGE: Stran
URL: URL
UpdateMEDIA: 'Posodobi multimedijsko datoteko'
Image:
PLURALNAME: Datoteke
SINGULARNAME: Datoteka
@ -242,6 +295,10 @@ sl:
PreviewButton: Predogled
REORGANISATIONSUCCESSFUL: 'Struktura spletnega mesta je bila uspešno spremenjena.'
SAVEDUP: Shranjeno.
ShowAsList: 'pokaži kot seznam'
TooManyPages: 'Preveč strani'
ValidationError: 'Napaka pri potrjevanju'
VersionUnknown: Neznano
LeftAndMain_Menu_ss:
Hello: Pozdravljeni,
LOGOUT: 'Odjava'
@ -260,17 +317,21 @@ sl:
CANTEDIT: 'Nimate dovoljenja za to dejanje'
CONFIRMNEWPASSWORD: 'Potrdi novo geslo'
CONFIRMPASSWORD: 'Potrdi geslo'
DATEFORMAT: 'Format datuma'
DefaultAdminFirstname: 'Privzeti administrator'
DefaultDateTime: privzeto
EMAIL: E-naslov
EMPTYNEWPASSWORD: 'Polje za vpis novega gesla ne sme ostati prazno. Poskusite ponovno.'
ENTEREMAIL: 'Vpišite e-naslov, na katerega vam bomo nato poslali povezavo za ponastavitev gesla.'
ERRORNEWPASSWORD: 'Novo geslo ste drugače zapisali - poskusite ponovno.'
ERRORPASSWORDNOTMATCH: 'Vpisani novi gesli se ne ujemata. Poskusite ponovno.'
ERRORWRONGCRED: 'Izgleda, da podani podatki niso točni. Poskusite ponovno'
FIRSTNAME: 'Ime'
INTERFACELANG: 'Jezik'
INVALIDNEWPASSWORD: 'Tega gesla ne moremo sprejeti: {password}'
LOGGEDINAS: 'Vpisan(a) si kot: {name}.'
NEWPASSWORD: 'Novo geslo'
NoPassword: 'Za tega člana ni gesla'
PASSWORD: Geslo
PLURALNAME: Uporabniki
REMEMBERME: 'Zapomni si me do prihodnjič'
@ -278,6 +339,7 @@ sl:
SUBJECTPASSWORDCHANGED: 'Geslo je bilo spremenjeno'
SUBJECTPASSWORDRESET: 'Povezava za resetiranje vašega gesla'
SURNAME: Priimek
TIMEFORMAT: 'Format časovnega zapisa'
VALIDATIONMEMBEREXISTS: 'Uporabnik s tem %s že obstaja'
WELCOMEBACK: 'Ponovno pozdravljeni, {firstname}'
YOUROLDPASSWORD: 'Staro geslo'
@ -318,12 +380,25 @@ sl:
MemberPassword:
PLURALNAME: 'Gesla uporabnika'
SINGULARNAME: 'Geslo uporabnika'
MemberTableField:
APPLY_FILTER: 'Uporabi filter'
ModelAdmin:
DELETE: Izbriši
DELETEDRECORDS: 'Izbrisanih je {count} zapisov.'
EMPTYBEFOREIMPORT: 'Nadomesti podatke'
IMPORT: 'Uvozi CSV-datoteko'
IMPORTEDRECORDS: 'Uvoženih je {count} zapisov.'
NOCSVFILE: 'Poiščite CSV-datoteko za uvoz'
NOIMPORT: 'Ne najdem ničesar za uvoz'
RESET: Ponastavi
Title: 'Podatkovni modeli'
UPDATEDRECORDS: 'Posodobi {count} zapisov.'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Stolpci podatkovne baze'
IMPORTSPECLINK: 'Pokaži specifikacije za %s'
IMPORTSPECTITLE: 'Specifikacije za %s'
ModelAdmin_Tools_ss:
FILTER: Filtriraj
IMPORT: Uvozi
ModelSidebar_ss:
IMPORT_TAB_HEADER: Uvozi
@ -339,6 +414,7 @@ sl:
Page: Stran
View: Poglej
Permission:
AdminGroup: Administrator
CMS_ACCESS_CATEGORY: 'Dostop do CMS-vmesnika'
FULLADMINRIGHTS: 'Popolne administratorske pravice'
FULLADMINRIGHTS_HELP: 'Lahko izniči oziroma upravlja z vsemi drugimi dovoljenji.'
@ -364,6 +440,7 @@ sl:
ERRORPASSWORDPERMISSION: 'Da bi lahko zamenjali geslo, se morate prijaviti.'
LOGGEDOUT: 'Vaša prijava je bila prekinjena. Če se želite ponovno prijaviti, vpišite svoje podatke.'
LOGIN: 'Prijava'
LOSTPASSWORDHEADER: 'Izgubljeno geslo'
NOTEPAGESECURED: 'Stran je zaščitena. Da bi lahko nadaljevali, vpišite svoje podatke.'
NOTERESETLINKINVALID: '<p>Povezava za ponastavitev gesla je napačna ali pa je njena veljavnost potekla.</p><p><a href="{link1}">Tukaj</a> lahko zaprosite za novo povezavo or pa zamenjate geslo, ko <a href="{link2}">se prijavite v sistem</a>.</p>'
NOTERESETPASSWORD: 'Vpišite e-naslov, na katerega vam bomo poslali povezavo za ponastavitev gesla'
@ -376,6 +453,8 @@ sl:
EDITPERMISSIONS: 'Upravljanje dovoljenj za skupine'
EDITPERMISSIONS_HELP: 'Možnost upravljanja z dovoljenji in IP-naslovi skupin. Potrebujete dovoljenje za dostop do sklopa "Varnost".'
GROUPNAME: 'Ime skupine'
IMPORTGROUPS: 'Uvozi skupine'
IMPORTUSERS: 'Uvozi uporabnike'
MEMBERS: Člani
MENUTITLE: Varnost
MemberListCaution: 'Pozor! Če boste odstranili uporabnike s tega seznama, jih boste hkrati odstranili iz vseh drugih skupin in izbrisali iz podatkovne zbirke.'
@ -384,11 +463,22 @@ sl:
ROLES: Vloge
ROLESDESCRIPTION: 'Tukaj lahko skupini dodajate vloge. Vloge so logični sklopi dovoljenj, ki jih urejate v zavihku "Vloge".'
TABROLES: Vloge
Users: Uporabniki
SecurityAdmin_MemberImportForm:
BtnImport: 'Uvozi'
FileFieldLabel: 'CSV-datoteka <small>(Samo končnica: *.csv)</small>'
SilverStripeNavigator:
Auto: Samodejno
ChangeViewMode: 'Spremeni pogled'
Desktop: Namizje
Edit: Uredi
EditView: 'Način za urejanje'
PreviewState: 'Stanje predogleda'
PreviewView: 'Način za predogled'
Responsive: Odziven
SplitView: 'Razdeljeni način'
ViewDeviceWidth: 'Izberi širino predogleda'
Width: širina
SiteTree:
TABMAIN: Domov
TableListField:
@ -402,7 +492,10 @@ sl:
LESS: manj
MORE: več
UploadField:
ATTACHFILE: 'Pripni datoteko'
ATTACHFILES: 'Pripni datoteke'
AttachFile: 'Pripni datoteko (ali več njih)'
CHOOSEANOTHERFILE: 'Izberi še eno datoteko'
DELETE: 'Izbriši iz zbirke naloženih datotek'
DELETEINFO: 'Dokončno izbriši datoteko iz knjižnjice datotek'
DOEDIT: Shrani
@ -414,11 +507,16 @@ sl:
FIELDNOTSET: 'Pred uvozom počisti bazo podatkov'
FROMCOMPUTER: 'Z vašega računalnika'
FROMCOMPUTERINFO: 'Izberite iz zbirke naloženih datotek'
FROMFILES: 'Iz datotek'
HOTLINKINFO: 'Info: Ta slika bo dostopna prek povezave. Prosim, preverite, da imate dovoljenje avtorja za tako uporabo.'
MAXNUMBEROFFILES: 'Doseženo je največje možno število datotek: {count}'
MAXNUMBEROFFILESONE: 'Naložite lahko samo eno datoteko'
MAXNUMBEROFFILESSHORT: 'Naložite lahko največ {count} datotek'
OVERWRITEWARNING: 'Datoteka z enakim imenom že obstaja'
REMOVE: Odstrani
REMOVEINFO: 'Odstrani datoteko, vendar je ne izbriši iz knjižnjice datotek'
STARTALL: 'Zaženi vse'
Saved: Shranjeno
UPLOADSINTO: 'shrani v /{path}'
Versioned:
has_many_Versions: Verzije

View File

@ -234,9 +234,9 @@ class DB {
$dbClass = $databaseConfig['type'];
// Using Injector->get allows us to use registered configurations
// Using Injector->create allows us to use registered configurations
// which may or may not map to explicit objects
$conn = Injector::inst()->get($dbClass);
$conn = Injector::inst()->create($dbClass);
$conn->connect($databaseConfig);
self::set_conn($conn, $label);

View File

@ -81,7 +81,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
private static $singular_name = null;
/**
* Human-readable pluaral name
* Human-readable plural name
* @var string
* @config
*/

View File

@ -115,7 +115,8 @@ class Hierarchy extends DataExtension {
if($limitToMarked && $rootCall) {
$this->markingFinished($numChildrenMethod);
}
if($nodeCountCallback) {
$nodeCountWarning = $nodeCountCallback($this->owner, $this->owner->$numChildrenMethod());
if($nodeCountWarning) return $nodeCountWarning;
@ -130,6 +131,7 @@ class Hierarchy extends DataExtension {
}
if($children) {
if($attributes) {
$attributes = " $attributes";
}
@ -139,10 +141,15 @@ class Hierarchy extends DataExtension {
foreach($children as $child) {
if(!$limitToMarked || $child->isMarked()) {
$foundAChild = true;
$output .= (is_callable($titleEval)) ? $titleEval($child) : eval("return $titleEval;");
if(is_callable($titleEval)) {
$output .= $titleEval($child, $numChildrenMethod);
} else {
$output .= eval("return $titleEval;");
}
$output .= "\n";
$numChildren = $child->$numChildrenMethod();
if(
// Always traverse into opened nodes (they might be exposed as parents of search results)
$child->isExpanded()
@ -158,7 +165,7 @@ class Hierarchy extends DataExtension {
} else {
$output .= $child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked,
$childrenMethod, $numChildrenMethod, false, $nodeCountThreshold);
}
}
} elseif($child->isTreeOpened()) {
// Since we're not loading children, don't mark it as open either
$child->markClosed();
@ -256,7 +263,7 @@ class Hierarchy extends DataExtension {
return call_user_func($func, $node);
}
}
/**
* Mark all children of the given node that match the marking filter.
* @param DataObject $node Parent node.
@ -276,11 +283,6 @@ class Hierarchy extends DataExtension {
foreach($children as $child) {
$markingMatches = $this->markingFilterMatches($child);
if($markingMatches) {
// Filtered results should always show opened, since actual matches
// might be hidden by non-matching parent nodes.
if($this->markingFilter) {
$child->markOpened();
}
if($child->$numChildrenMethod()) {
$child->markUnexpanded();
} else {
@ -315,14 +317,14 @@ class Hierarchy extends DataExtension {
*
* @return string
*/
public function markingClasses() {
public function markingClasses($numChildrenMethod="numChildren") {
$classes = '';
if(!$this->isExpanded()) {
$classes .= " unexpanded";
}
// Set jstree open state, or mark it as a leaf (closed) if there are no children
if(!$this->numChildren()) {
if(!$this->$numChildrenMethod()) {
$classes .= " jstree-leaf closed";
} elseif($this->isTreeOpened()) {
$classes .= " jstree-open";

View File

@ -73,11 +73,11 @@ class Image extends File {
private static $force_resample = false;
public static function set_backend($backend) {
self::$backend = $backend;
self::config()->backend = $backend;
}
public static function get_backend() {
return self::$backend;
return self::config()->backend;
}
/**
@ -471,7 +471,7 @@ class Image extends File {
$cacheFile = call_user_func_array(array($this, "cacheFilename"), $args);
$backend = Injector::inst()->createWithArgs(self::$backend, array(
$backend = Injector::inst()->createWithArgs(self::config()->backend, array(
Director::baseFolder()."/" . $this->Filename,
$args
));

View File

@ -165,7 +165,7 @@ class HTMLText extends Text {
/* Then look for the first sentence ending. We could probably use a nice regex, but for now this will do */
$words = preg_split('/\s+/', $paragraph);
foreach ($words as $i => $word) {
if (preg_match('/\.$/', $word) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word)) {
if (preg_match('/(!|\?|\.)$/', $word) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word)) {
return implode(' ', array_slice($words, 0, $i+1));
}
}

View File

@ -101,17 +101,20 @@ class Text extends StringField {
* Caution: Not XML/HTML-safe - does not respect closing tags.
*/
public function FirstSentence() {
$data = Convert::xml2raw( $this->value );
if( !$data ) return "";
$sentences = explode( '.', $data );
if( count( $sentences ) )
return $sentences[0] . '.';
else
return $this->Summary(20);
}
$paragraph = Convert::xml2raw( $this->value );
if( !$paragraph ) return "";
$words = preg_split('/\s+/', $paragraph);
foreach ($words as $i => $word) {
if (preg_match('/(!|\?|\.)$/', $word) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word)) {
return implode(' ', array_slice($words, 0, $i+1));
}
}
/* If we didn't find a sentence ending, use the summary. We re-call rather than using paragraph so that
* Summary will limit the result this time */
return $this->Summary(20);
}
/**
* Caution: Not XML/HTML-safe - does not respect closing tags.

View File

@ -280,8 +280,9 @@ abstract class SQLConditionalExpression extends SQLExpression {
$filter = "(" . implode(") AND (", $join['filter']) . ")";
}
$table = strpos(strtoupper($join['table']), 'SELECT') ? $join['table'] : "\"" . $join['table'] . "\"";
$aliasClause = ($alias != $join['table']) ? " AS \"$alias\"" : "";
$joins[$alias] = strtoupper($join['type']) . ' JOIN "' . $join['table'] . "\"$aliasClause ON $filter";
$joins[$alias] = strtoupper($join['type']) . " JOIN " . $table . "$aliasClause ON $filter";
}
}

View File

@ -204,9 +204,6 @@ body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fie
text-shadow: 0px 1px 0px rgba(#fff, 0.5);
&.ui-state-error-text {
max-width:70%;
position:absolute;
right:5px;
text-shadow: 0px 1px 0px rgba(#fff, 0.6);
color: darken($color-button-destructive, 10%);
}

View File

@ -13,7 +13,11 @@
.middleColumn {
// TODO .middleColumn styling should probably be theme specific (eg cms ui will look different than blackcandy)
// so we should move this style into the cms and black candy files
width: 510px;
min-width: 510px;
max-width: 600px; // Capped width. 600px is about the average size of a preview split view
width:100%;
margin-left:0;
clear:both;
padding: 0;
background: #fff;
border: 1px solid lighten($color-medium-separator, 20%);

View File

@ -213,6 +213,58 @@ class CmsUiContext extends BehatContext {
}
}
/**
* @When /^I (expand|collapse) "([^"]*)" in the tree$/
*/
public function iExpandInTheTree($action, $nodeText) {
//Tries to find the first visiable matched Node in the page
$page = $this->getSession()->getPage();
$treeEl = $this->getCmsTreeElement();
$treeNode = $treeEl->findLink($nodeText);
assertNotNull($treeNode, sprintf('%s link not found', $nodeText));
$cssIcon = $treeNode->getParent()->getAttribute("class");
if($action == "expand") {
//ensure it is collapsed
if(false === strpos($cssIcon, 'jstree-open')) {
$nodeIcon = $treeNode->getParent()->find('css', '.jstree-icon');
assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
$nodeIcon->click();
}
} else {
//ensure it is expanded
if(false === strpos($cssIcon, 'jstree-closed')) {
$nodeIcon = $treeNode->getParent()->find('css', '.jstree-icon');
assertTrue($nodeIcon->isVisible(), "CMS node '$nodeText' not found");
$nodeIcon->click();
}
}
}
/**
* @When /^I should (not |)see a "([^"]*)" CMS tab$/
*/
public function iShouldSeeACmsTab($negate, $tab) {
$this->getSession()->wait(
5000,
"window.jQuery && window.jQuery('.ui-tabs-nav').size() > 0"
);
$page = $this->getSession()->getPage();
$tabsets = $page->findAll('css', '.ui-tabs-nav');
assertNotNull($tabsets, 'CMS tabs not found');
$tab_element = null;
foreach($tabsets as $tabset) {
$tab_element = $tabset->find('named', array('link_or_button', "'$tab'"));
if($tab_element) break;
}
if($negate) {
assertNull($tab_element, sprintf('%s tab found', $tab));
} else {
assertNotNull($tab_element, sprintf('%s tab not found', $tab));
}
}
/**
* @When /^I click the "([^"]*)" CMS tab$/
*/

View File

@ -96,7 +96,7 @@ class DirectorTest extends SapphireTest {
$rootURL = Director::protocolAndHost();
$_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/";
Config::inst()->update('Director', 'alternate_base_url', '/mysite/');
// Test already absolute url
$this->assertEquals($rootURL, Director::absoluteURL($rootURL));
$this->assertEquals($rootURL, Director::absoluteURL($rootURL, true));

View File

@ -0,0 +1,107 @@
<?php
/**
*
*
* @author <marcus@silverstripe.com.au>
* @license BSD License http://www.silverstripe.org/bsd-license
*/
class AopProxyTest extends SapphireTest {
public function testBeforeMethodsCalled() {
$proxy = new AopProxyService();
$aspect = new BeforeAfterCallTestAspect();
$proxy->beforeCall = array(
'myMethod' => $aspect
);
$proxy->proxied = new ProxyTestObject();
$result = $proxy->myMethod();
$this->assertEquals('myMethod', $aspect->called);
$this->assertEquals(42, $result);
}
public function testBeforeMethodBlocks() {
$proxy = new AopProxyService();
$aspect = new BeforeAfterCallTestAspect();
$aspect->block = true;
$proxy->beforeCall = array(
'myMethod' => $aspect
);
$proxy->proxied = new ProxyTestObject();
$result = $proxy->myMethod();
$this->assertEquals('myMethod', $aspect->called);
// the actual underlying method will NOT have been called
$this->assertNull($result);
// set up an alternative return value
$aspect->alternateReturn = 84;
$result = $proxy->myMethod();
$this->assertEquals('myMethod', $aspect->called);
// the actual underlying method will NOT have been called,
// instead the alternative return value
$this->assertEquals(84, $result);
}
public function testAfterCall() {
$proxy = new AopProxyService();
$aspect = new BeforeAfterCallTestAspect();
$proxy->afterCall = array(
'myMethod' => $aspect
);
$proxy->proxied = new ProxyTestObject();
$aspect->modifier = function ($value) {
return $value * 2;
};
$result = $proxy->myMethod();
$this->assertEquals(84, $result);
}
}
class ProxyTestObject {
public function myMethod() {
return 42;
}
}
class BeforeAfterCallTestAspect implements BeforeCallAspect, AfterCallAspect {
public $block = false;
public $called;
public $alternateReturn;
public $modifier;
public function beforeCall($proxied, $method, $args, &$alternateReturn) {
$this->called = $method;
if ($this->block) {
if ($this->alternateReturn) {
$alternateReturn = $this->alternateReturn;
}
return false;
}
}
public function afterCall($proxied, $method, $args, $result) {
if ($this->modifier) {
$modifier = $this->modifier;
return $modifier($result);
}
}
}

View File

@ -391,6 +391,97 @@ class HierarchyTest extends SapphireTest {
);
}
/**
* This test checks that deleted ('archived') child pages don't set a css class on the parent
* node that makes it look like it has children
*/
public function testGetChildrenAsULNodeDeletedOnLive() {
$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
// delete all children under obj2
$obj2a->delete();
$obj2aa->delete();
$obj2ab->delete();
// Don't pre-load all children
$nodeCountThreshold = 1;
$childrenMethod = 'AllChildren';
$numChildrenMethod = 'numChildren';
$root = new HierarchyTest_Object();
$root->markPartialTree($nodeCountThreshold, null, $childrenMethod, $numChildrenMethod);
// As in LeftAndMain::getSiteTreeFor() but simpler and more to the point for testing purposes
$titleFn = function(&$child, $numChildrenMethod="") {
return '<li class="' . $child->markingClasses($numChildrenMethod).
'" id="' . $child->ID . '">"' . $child->Title;
};
$html = $root->getChildrenAsUL(
"",
$titleFn,
null,
true, // limit to marked
$childrenMethod,
$numChildrenMethod,
true,
$nodeCountThreshold
);
// Get the class attribute from the $obj2 node in the sitetree, class 'jstree-leaf' means it's a leaf node
$nodeClass = $this->getNodeClassFromTree($html, $obj2);
$this->assertEquals('jstree-leaf closed', $nodeClass, 'object2 should not have children in the sitetree');
}
/**
* This test checks that deleted ('archived') child pages _do_ set a css class on the parent
* node that makes it look like it has children when getting all children including deleted
*/
public function testGetChildrenAsULNodeDeletedOnStage() {
$obj2 = $this->objFromFixture('HierarchyTest_Object', 'obj2');
$obj2a = $this->objFromFixture('HierarchyTest_Object', 'obj2a');
$obj2aa = $this->objFromFixture('HierarchyTest_Object', 'obj2aa');
$obj2ab = $this->objFromFixture('HierarchyTest_Object', 'obj2b');
// delete all children under obj2
$obj2a->delete();
$obj2aa->delete();
$obj2ab->delete();
// Don't pre-load all children
$nodeCountThreshold = 1;
$childrenMethod = 'AllChildrenIncludingDeleted';
$numChildrenMethod = 'numHistoricalChildren';
$root = new HierarchyTest_Object();
$root->markPartialTree($nodeCountThreshold, null, $childrenMethod, $numChildrenMethod);
// As in LeftAndMain::getSiteTreeFor() but simpler and more to the point for testing purposes
$titleFn = function(&$child, $numChildrenMethod="") {
return '<li class="' . $child->markingClasses($numChildrenMethod).
'" id="' . $child->ID . '">"' . $child->Title;
};
$html = $root->getChildrenAsUL(
"",
$titleFn,
null,
true, // limit to marked
$childrenMethod,
$numChildrenMethod,
true,
$nodeCountThreshold
);
// Get the class attribute from the $obj2 node in the sitetree
$nodeClass = $this->getNodeClassFromTree($html, $obj2);
// Object2 can now be expanded
$this->assertEquals('unexpanded jstree-closed closed', $nodeClass, 'obj2 should have children in the sitetree');
}
/**
* @param String $html [description]
* @param array $nodes Breadcrumb path as array
@ -417,6 +508,25 @@ class HierarchyTest extends SapphireTest {
self::assertThat((bool)$match, self::isFalse(), $message);
}
/**
* Get the HTML class attribute from a node in the sitetree
*
* @param $html
* @param $node
* @return string
*/
protected function getNodeClassFromTree($html, $node) {
$parser = new CSSContentParser($html);
$xpath = '//ul/li[@id="' . $node->ID . '"]';
$object = $parser->getByXpath($xpath);
foreach($object[0]->attributes() as $key => $attr) {
if($key == 'class') {
return (string)$attr;
}
}
return '';
}
}
class HierarchyTest_Object extends DataObject implements TestOnly {
@ -428,4 +538,8 @@ class HierarchyTest_Object extends DataObject implements TestOnly {
'Hierarchy',
"Versioned('Stage', 'Live')",
);
}
public function cmstreeclasses() {
return $this->markingClasses();
}
}

View File

@ -353,6 +353,27 @@ class SQLQueryTest extends SapphireTest {
);
}
public function testJoinSubSelect() {
$query = new SQLQuery();
$query->setFrom('MyTable');
$query->addInnerJoin('(SELECT * FROM MyOtherTable)',
'Mot.MyTableID = MyTable.ID', 'Mot');
$query->addLeftJoin('(SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
. 'GROUP BY MyOtherTableID)',
'Mlt.MyOtherTableID = Mot.ID', 'Mlt');
$query->setOrderBy('COALESCE(Mlt.MyLastTableCount, 0) DESC');
$this->assertSQLEquals('SELECT *, COALESCE(Mlt.MyLastTableCount, 0) AS "_SortColumn0" FROM MyTable '.
'INNER JOIN (SELECT * FROM MyOtherTable) AS "Mot" ON Mot.MyTableID = MyTable.ID ' .
'LEFT JOIN (SELECT MyLastTable.MyOtherTableID, COUNT(1) as MyLastTableCount FROM MyLastTable '
. 'GROUP BY MyOtherTableID) AS "Mlt" ON Mlt.MyOtherTableID = Mot.ID ' .
'ORDER BY "_SortColumn0" DESC',
$query->sql($parameters)
);
}
public function testSetWhereAny() {
$query = new SQLSelect();
$query->setFrom('MyTable');

View File

@ -87,7 +87,28 @@ class TextTest extends SapphireTest {
$this->assertEquals($expectedValue, $textObj->LimitSentences(2));
}
}
public function testFirstSentance() {
$cases = array(
'' => '',
'First sentence.' => 'First sentence.',
'First sentence. Second sentence' => 'First sentence.',
'First sentence? Second sentence' => 'First sentence?',
'First sentence! Second sentence' => 'First sentence!',
'<p>First sentence.</p>' => 'First sentence.',
'<p>First sentence. Second sentence. Third sentence</p>' => 'First sentence.',
'<p>First sentence. <em>Second sentence</em>. Third sentence</p>' => 'First sentence.',
'<p>First sentence. <em class="dummyClass">Second sentence</em>. Third sentence</p>'
=> 'First sentence.'
);
foreach($cases as $originalValue => $expectedValue) {
$textObj = new Text('Test');
$textObj->setValue($originalValue);
$this->assertEquals($expectedValue, $textObj->FirstSentence());
}
}
/**
* Test {@link Text->BigSummary()}
*/