Merge pull request #3379 from mateusz/merge-back

Merge 3.1 back
This commit is contained in:
Sean Harvey 2014-08-14 09:44:07 +12:00
commit 5cd85f6496
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'); if(empty($_REQUEST['executeForm']) && !$this->request->isAjax()) $this->extend('accessedCMS');
// Set the members html editor config // 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 // 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 // file because insufficient information exists when that is being processed
$htmlEditorConfig = HtmlEditorConfig::get_active(); $htmlEditorConfig = HtmlEditorConfig::get_active();
@ -797,7 +799,12 @@ class LeftAndMain extends Controller implements PermissionProvider {
? $filter->getChildrenMethod() ? $filter->getChildrenMethod()
: 'AllChildrenIncludingDeleted'; : 'AllChildrenIncludingDeleted';
if(!$numChildrenMethod) $numChildrenMethod = 'numChildren'; if(!$numChildrenMethod) {
$numChildrenMethod = 'numChildren';
if($filter && $filter->getNumChildrenMethod()) {
$numChildrenMethod = $filter->getNumChildrenMethod();
}
}
if(!$filterFunction) $filterFunction = ($filter) ? array($filter, 'isPageIncluded') : null; if(!$filterFunction) $filterFunction = ($filter) ? array($filter, 'isPageIncluded') : null;
// Get the tree root // Get the tree root
@ -810,7 +817,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
$obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod); $obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod);
// Ensure current page is exposed // 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 :-( // NOTE: SiteTree/CMSMain coupling :-(
if(class_exists('SiteTree')) { 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 // getChildrenAsUL is a flexible and complex way of traversing the tree
$controller = $this; $controller = $this;
$recordController = ($this->stat('tree_class') == 'SiteTree') ? singleton('CMSPageEditController') : $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); $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. // 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 if($id === "") continue; // $id may be a blank string, which is invalid and should be skipped over
$record = $this->getRecord($id); $record = $this->getRecord($id);
if(!$record) continue; // In case a page is no longer available
$recordController = ($this->stat('tree_class') == 'SiteTree') $recordController = ($this->stat('tree_class') == 'SiteTree')
? singleton('CMSPageEditController') ? singleton('CMSPageEditController')
: $this; : $this;
@ -1866,10 +1878,22 @@ class LeftAndMain_TreeNode extends ViewableData {
*/ */
protected $isCurrent; 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->obj = $obj;
$this->link = $link; $this->link = $link;
$this->isCurrent = $isCurrent; $this->isCurrent = $isCurrent;
$this->numChildrenMethod = $numChildrenMethod;
} }
/** /**
@ -1890,7 +1914,7 @@ class LeftAndMain_TreeNode extends ViewableData {
} }
public function getClasses() { public function getClasses() {
$classes = $this->obj->CMSTreeClasses(); $classes = $this->obj->CMSTreeClasses($this->numChildrenMethod);
if($this->isCurrent) $classes .= " current"; if($this->isCurrent) $classes .= " current";
$flags = $this->obj->hasMethod('getStatusFlags') ? $this->obj->getStatusFlags() : false; $flags = $this->obj->hasMethod('getStatusFlags') ? $this->obj->getStatusFlags() : false;
if ($flags) { if ($flags) {

View File

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

View File

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

View File

@ -585,11 +585,14 @@ jQuery.noConflict();
var newFragments = {}, newContentEls; var newFragments = {}, newContentEls;
// If content type is text/json (ignoring charset and other parameters) // 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; newFragments = data;
} else { } else {
// Fall back to replacing the content fragment if HTML is returned // 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 // 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 // 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() { setTimeout(function() {
form.clickedButton = null; form.clickedButton = null;
}, 10); }, 10);
} }
}); });
this.redraw(); this.redraw();
this._super(); this._super();

View File

@ -1,6 +1,6 @@
(function($) { (function($) {
$.entwine('ss', function($){ $.entwine('ss', function($){
$('.memberdatetimeoptionset').entwine({ $('.memberdatetimeoptionset').entwine({
onmatch: function() { onmatch: function() {
this.find('.description .toggle-content').hide(); 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.SAVED": "Opgeslagen",
"ModelAdmin.REALLYDELETE": "Weet u zeker dat u wilt verwijderen?", "ModelAdmin.REALLYDELETE": "Weet u zeker dat u wilt verwijderen?",
"ModelAdmin.DELETED": "Verwijderd", "ModelAdmin.DELETED": "Verwijderd",
"ModelAdmin.VALIDATIONERROR": "Validatie fout", "ModelAdmin.VALIDATIONERROR": "Validatiefout",
"LeftAndMain.PAGEWASDELETED": "Deze pagina is verwijderd. Om een pagina aan te passen, selecteer pagina aan de linkerkant." "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); $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' 'Hierarchy'
); );
public function CMSTreeClasses() {}
} }

View File

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

View File

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

View File

@ -513,10 +513,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$haystack, $haystack,
$message = '', $message = '',
$ignoreCase = FALSE, $ignoreCase = FALSE,
$checkForObjectIdentity = TRUE $checkForObjectIdentity = TRUE,
$checkForNonObjectIdentity = false
) { ) {
if ($haystack instanceof DBField) $haystack = (string)$haystack; 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( public static function assertNotContains(
@ -524,10 +525,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$haystack, $haystack,
$message = '', $message = '',
$ignoreCase = FALSE, $ignoreCase = FALSE,
$checkForObjectIdentity = TRUE $checkForObjectIdentity = TRUE,
$checkForNonObjectIdentity = false
) { ) {
if ($haystack instanceof DBField) $haystack = (string)$haystack; 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 * Represents a test usage session of a web-app
* It will maintain session-state from request to request * It will maintain session-state from request to request
* *
* @package framework * @package framework
* @subpackage testing * @subpackage testing
*/ */
class TestSession { 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, * created in {@link session} in the normal controller stack,
* e.g. to overwrite Member::currentUser() with custom login data. * e.g. to overwrite Member::currentUser() with custom login data.
*
* @var Controller
*/ */
protected $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; private $lastUrl;
@ -28,7 +40,7 @@ class TestSession {
$this->controller->setSession($this->session); $this->controller->setSession($this->session);
$this->controller->pushCurrent(); $this->controller->pushCurrent();
} }
public function __destruct() { public function __destruct() {
// Shift off anything else that's on the stack. This can happen if something throws // Shift off anything else that's on the stack. This can happen if something throws
// an exception that causes a premature TestSession::__destruct() call // an exception that causes a premature TestSession::__destruct() call
@ -36,15 +48,21 @@ class TestSession {
if(Controller::has_curr()) $this->controller->popCurrent(); if(Controller::has_curr()) $this->controller->popCurrent();
} }
/** /**
* Submit a get request * Submit a get request
*
* @uses Director::test() * @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) { public function get($url, $session = null, $headers = null, $cookies = null) {
$headers = (array) $headers; $headers = (array) $headers;
if($this->lastUrl && !isset($headers['Referer'])) $headers['Referer'] = $this->lastUrl; 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); = Director::test($url, null, $session ? $session : $this->session, null, null, $headers, $cookies);
$this->lastUrl = $url; $this->lastUrl = $url;
if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING); if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING);
@ -53,7 +71,15 @@ class TestSession {
/** /**
* Submit a post request * Submit a post request
*
* @uses Director::test() * @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) { public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null) {
$headers = (array) $headers; $headers = (array) $headers;
@ -64,24 +90,24 @@ class TestSession {
if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING); if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING);
return $this->lastResponse; return $this->lastResponse;
} }
/** /**
* Submit the form with the given HTML ID, filling it out with the given data. * Submit the form with the given HTML ID, filling it out with the given data.
* Acts on the most recent response. * Acts on the most recent response.
* *
* Any data parameters have to be present in the form, with exact form field name * Any data parameters have to be present in the form, with exact form field name
* and values, otherwise they are removed from the submission. * and values, otherwise they are removed from the submission.
* *
* Caution: Parameter names have to be formatted * Caution: Parameter names have to be formatted
* as they are in the form submission, not as they are interpreted by PHP. * as they are in the form submission, not as they are interpreted by PHP.
* Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two')) * Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
* Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two') * Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
* *
* @see http://www.simpletest.org/en/form_testing_documentation.html * @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 $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 string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
* @param Array $data Map of GET/POST data. * @param array $data Map of GET/POST data.
* @return SS_HTTPResponse * @return SS_HTTPResponse
*/ */
public function submitForm($formID, $button = null, $data = array()) { public function submitForm($formID, $button = null, $data = array()) {
@ -108,15 +134,17 @@ class TestSession {
$postVars = array(); $postVars = array();
parse_str($submission->_encode(), $postVars); parse_str($submission->_encode(), $postVars);
return $this->post($url, $postVars); return $this->post($url, $postVars);
} else { } else {
user_error("TestSession::submitForm called when there is no form loaded." user_error("TestSession::submitForm called when there is no form loaded."
. " Visit the page with the form first", E_USER_WARNING); . " Visit the page with the form first", E_USER_WARNING);
} }
} }
/** /**
* If the last request was a 3xx response, then follow the redirection * 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() { public function followRedirection() {
if($this->lastResponse->getHeader('Location')) { if($this->lastResponse->getHeader('Location')) {
@ -125,25 +153,29 @@ class TestSession {
return $this->get($url); return $this->get($url);
} }
} }
/** /**
* Returns true if the last response was a 3xx redirection * Returns true if the last response was a 3xx redirection
*
* @return bool
*/ */
public function wasRedirected() { public function wasRedirected() {
$code = $this->lastResponse->getStatusCode(); $code = $this->lastResponse->getStatusCode();
return $code >= 300 && $code < 400; 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() { public function lastResponse() {
return $this->lastResponse; return $this->lastResponse;
} }
/** /**
* Return the fake HTTP_REFERER; set each time get() or post() is called. * Return the fake HTTP_REFERER; set each time get() or post() is called.
* *
* @return string * @return string
*/ */
public function lastUrl() { public function lastUrl() {
@ -152,19 +184,27 @@ class TestSession {
/** /**
* Get the most recent response's content * Get the most recent response's content
*
* @return string
*/ */
public function lastContent() { public function lastContent() {
if(is_string($this->lastResponse)) return $this->lastResponse; if(is_string($this->lastResponse)) return $this->lastResponse;
else return $this->lastResponse->getBody(); else return $this->lastResponse->getBody();
} }
/**
* Return a CSSContentParser containing the most recent response
*
* @return CSSContentParser
*/
public function cssParser() { public function cssParser() {
return new CSSContentParser($this->lastContent()); return new CSSContentParser($this->lastContent());
} }
/** /**
* Get the last response as a SimplePage object * Get the last response as a SimplePage object
*
* @return SimplePage The response if available
*/ */
public function lastPage() { public function lastPage() {
require_once("thirdparty/simpletest/http.php"); require_once("thirdparty/simpletest/http.php");
@ -176,13 +216,15 @@ class TestSession {
$page = &$builder->parse(new TestSession_STResponseWrapper($this->lastResponse)); $page = &$builder->parse(new TestSession_STResponseWrapper($this->lastResponse));
$builder->free(); $builder->free();
unset($builder); unset($builder);
return $page; return $page;
} }
} }
/** /**
* Get the current session, as a Session object * Get the current session, as a Session object
*
* @return Session
*/ */
public function session() { public function session() {
return $this->session; return $this->session;
@ -191,41 +233,66 @@ class TestSession {
/** /**
* Wrapper around SS_HTTPResponse to make it look like a SimpleHTTPResposne * Wrapper around SS_HTTPResponse to make it look like a SimpleHTTPResposne
* *
* @package framework * @package framework
* @subpackage testing * @subpackage testing
*/ */
class TestSession_STResponseWrapper { class TestSession_STResponseWrapper {
/**
* @var SS_HTTPResponse
*/
private $response; private $response;
public function __construct(SS_HTTPResponse $response) { public function __construct(SS_HTTPResponse $response) {
$this->response = $response; $this->response = $response;
} }
/**
* @return string
*/
public function getContent() { public function getContent() {
return $this->response->getBody(); return $this->response->getBody();
} }
/**
* @return string
*/
public function getError() { public function getError() {
return ""; return "";
} }
/**
* @return null
*/
public function getSent() { public function getSent() {
return null; return null;
} }
/**
* @return string
*/
public function getHeaders() { public function getHeaders() {
return ""; return "";
} }
/**
* @return string 'GET'
*/
public function getMethod() { public function getMethod() {
return "GET"; return "GET";
} }
/**
* @return string
*/
public function getUrl() { public function getUrl() {
return ""; return "";
} }
/**
* @return null
*/
public function getRequestData() { public function getRequestData() {
return null; 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 ## ## 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.5-rc1](rc/3.1.5-rc1) - 7 May 2014
* [3.1.4-rc1](rc/3.1.4-rc1) - 1 April 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 The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
for a more detailed introduction to creating new `ModelAdmin` interfaces, read 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 In this document we'll take the `ProductAdmin` class used in the
[ModelAdmin referencee](../reference/modeladmin#setup) and so how we can change [ModelAdmin reference](../reference/modeladmin#setup) and so how we can change
the menu behaviour by using the static `$menu_title` and `$menu_icon` statics to the menu behaviour by using the `$menu_title` and `$menu_icon` statics to
provide a custom title and icon. provide a custom title and icon.
### Defining a Custom 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 ## Adding an external link to the menu
On top of your administration windows, the menu can also have external links 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. Google to the menu.
First, we need to define a `[api:LeftAndMainExtension]` which will contain our 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` To have the link appear, make sure you add the extension to the `LeftAndMain`
class. For more information about configuring extensions see the class. For more information about configuring extensions see the
[DataExtension referencee](../reference/dataextension). [DataExtension reference](../reference/dataextension).
:::php :::php
LeftAndMain::add_extension('CustomLeftAndMain') LeftAndMain::add_extension('CustomLeftAndMain')
@ -93,4 +93,4 @@ class. For more information about configuring extensions see the
## Related ## 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 // return either true or false
} }
public function getStatusFlags(){ public function getStatusFlags($cached = true) {
$flags = parent::getStatusFlags(); $flags = parent::getStatusFlags($cached);
$flags['scheduledtopublish'] = "Scheduled To Publish"; $flags['scheduledtopublish'] = "Scheduled To Publish";
return $flags; 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 __'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()` 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 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. 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 ## 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): 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 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. 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 ## Install WAMP
1. Download WampServer from http://www.wampserver.com/en/download.php 1. Go to the [WampServer download page](http://www.wampserver.com/en/#download-wrapper).
2. Run the installer. By default, it will install to C:\wamp. You can choose your own install path if you wish; the 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.
directories mentioned below will also be different. 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.
3. Once WampServer has been installed and launched, you will see a little half circle gauge in the task bar, next to 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 white. If it's yellow or red, then something is wrong. If you 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.
can't see the gauge, 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
4. If something's wrong, this usually means that you have another service on port 80 or port 3306. Here are some then return to green.
common sources of problems to check. After correcting these issues, left-click the gauge and choose 'restart all 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
services'. It might a short while to restart, but the gauge should go to white. 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.
* 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.
## Install SilverStripe ## 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 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).
* Unpack the archive into `C:\wamp\www`
* Rename the unpacked directory from `C:\wamp\www\silverstripe-vX.X.X` to `C:\wamp\www\silverstripe` ```bash
* Visit `http://localhost/silverstripe` - you will see SilverStripe's installation screen. $ cd /c/wamp/www
* You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two. $ composer create-project silverstripe/installer ./silverstripe
* Once the installer has finished, visit `http://localhost/silverstripe`. You should see your new SilverStripe site's ```
### 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. home page.
## Troubleshooting ## 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 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.
that SilverStripe does not have sufficient permissions.
* 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 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 ## Sharing your Opinion
* [silverstripe.org/forums](http://silverstripe.org/forums): Forums on silverstripe.org * [silverstripe.org/forums](http://silverstripe.org/forums): Forums on silverstripe.org
* [silverstripe-dev](http://groups.google.com/group/silverstripe-dev): Core development mailinglist * [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-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 * [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, Many customizations of the SilverStripe CMS interface start here,
by adding, removing or configuring fields. by adding, removing or configuring fields.
Example getCMSFields implementation Here is an example getCMSFields implementation:
:::php :::php
class MyDataObject extends DataObject { class MyDataObject extends DataObject {
$db = array( $db = array(
'IsActive' => 'Boolean' 'IsActive' => 'Boolean'
); );
public function getCMSFields() { public function getCMSFields() {
return new FieldList( return new FieldList(
new CheckboxField('IsActive') 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. data management interfaces with very little custom coding.
You can also alter the fields of built-in and module `DataObject` classes through 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 `[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 ### 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 :::php
class Team extends DataObject { 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 **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. 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 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. already enforce these permissions.
## Indexes ## 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),
urlencode($address) urlencode($address)
); );
$width = (isset($args['width']) && $args['width']) ? $args['width'] : 400; $width = (isset($arguments['width']) && $arguments['width']) ? $arguments['width'] : 400;
$height = (isset($args['height']) && $args['height']) ? $args['height'] : 300; $height = (isset($arguments['height']) && $arguments['height']) ? $arguments['height'] : 300;
return sprintf( return sprintf(
'<iframe width="%d" height="%d" src="%s" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>', '<iframe width="%d" height="%d" src="%s" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>',
$width, $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: when navigating the tree or adding a new page:
:::php :::php
class StaggPage extends Page { class StaffPage extends Page {
private static $singular_name = 'Staff Directory'; private static $singular_name = 'Staff Directory';
private static $plural_name = 'Staff Directories'; private static $plural_name = 'Staff Directories';
private static $description = 'Two-column layout with a list of staff members'; 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, You can also add custom "badges" to each page in the tree,
which denote status. Built-in examples are "Draft" and "Deleted" flags. 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 You can limit the amount of records returned in a DataList by using the
`limit()` method. `limit()` method.
:::php :::php
// Returning the first 5 members, sorted alphabetically by Surname // Returning the first 5 members, sorted alphabetically by Surname
$members = Member::get()->sort('Surname')->limit(5); $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')); 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 :::js
// MYMODULE.MYENTITY contains "Really delete %s articles by %s authors?" // MYMODULE.MYENTITY contains "Really delete %s articles by %s?"
alert(ss.i18n.sprintf( alert(ss.i18n.sprintf(
ss.i18n._t('MYMODULE.MYENTITY'), ss.i18n._t('MYMODULE.MYENTITY'),
42, 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?" // 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 ## Limitations
* No detecting/conversion of character encodings (we rely fully on UTF-8) * 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() { public function __construct() {
parent::__construct(); parent::__construct();
$this->validator = new Upload_Validator(); $this->validator = Injector::inst()->create('Upload_Validator');
$this->replaceFile = self::config()->replaceFile; $this->replaceFile = self::config()->replaceFile;
} }

View File

@ -42,7 +42,7 @@ class CheckboxSetField extends OptionsetField {
* @var array * @var array
*/ */
protected $defaultItems = array(); protected $defaultItems = array();
/** /**
* @todo Explain different source data that can be used with this field, * @todo Explain different source data that can be used with this field,
* e.g. SQLMap, ArrayList or an array. * e.g. SQLMap, ArrayList or an array.
@ -50,6 +50,21 @@ class CheckboxSetField extends OptionsetField {
public function Field($properties = array()) { public function Field($properties = array()) {
Requirements::css(FRAMEWORK_DIR . '/css/CheckboxSetField.css'); 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; $source = $this->source;
$values = $this->value; $values = $this->value;
$items = array(); $items = array();
@ -99,47 +114,48 @@ class CheckboxSetField extends OptionsetField {
} }
} }
} }
if(is_array($source)) { if(is_array($source)) {
unset($source['']); unset($source['']);
} }
$odd = 0;
$options = array(); $options = array();
if ($source == null) $source = 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)
));
}
} }
$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. * Default selections, regardless of the {@link setValue()} settings.
* Note: Items marked as disabled through {@link setDisabledItems()} can still be * Note: Items marked as disabled through {@link setDisabledItems()} can still be

View File

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

View File

@ -190,7 +190,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
private static $allowed_actions = array( private static $allowed_actions = array(
'LinkForm', 'LinkForm',
'MediaForm', 'MediaForm',
'viewfile' 'viewfile',
'getanchors'
); );
/** /**
@ -312,6 +313,17 @@ class HtmlEditorField_Toolbar extends RequestHandler {
return $form; 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 * Return a {@link Form} instance allowing a user to
* add images and flash objects to the TinyMCE content editor. * add images and flash objects to the TinyMCE content editor.
@ -321,7 +333,7 @@ class HtmlEditorField_Toolbar extends RequestHandler {
public function MediaForm() { public function MediaForm() {
// TODO Handle through GridState within field - currently this state set too late to be useful here (during // TODO Handle through GridState within field - currently this state set too late to be useful here (during
// request handling) // request handling)
$parentID = $this->controller->getRequest()->requestVar('ParentID'); $parentID = $this->getAttachParentID();
$fileFieldConfig = GridFieldConfig::create()->addComponents( $fileFieldConfig = GridFieldConfig::create()->addComponents(
new GridFieldFilterHeader(), new GridFieldFilterHeader(),
@ -349,7 +361,9 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$fromCMS = new CompositeField( $fromCMS = new CompositeField(
new LiteralField('headerSelect', new LiteralField('headerSelect',
'<h4>'.sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.FindInFolder', 'Find in Folder')).'</h4>'), '<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 $fileField
); );
@ -508,6 +522,41 @@ class HtmlEditorField_Toolbar extends RequestHandler {
))->renderWith($this->templateViewFile); ))->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 * Similar to {@link File->getCMSFields()}, but only returns fields
* for manipulating the instance of the file as inserted into the HTML content, * 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) { if($total > 0) {
$rows = array(); $rows = array();
foreach($list as $idx => $record) { foreach($list as $idx => $record) {
if(!$record->canView()) { if($record->hasMethod('canView') && !$record->canView()) {
continue; continue;
} }
$rowContent = ''; $rowContent = '';

View File

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

View File

@ -8,7 +8,7 @@
*/ */
reload: function(ajaxOpts, successCallback) { 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 focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh
data = form.find(':input').serializeArray(); data = form.find(':input').serializeArray();
@ -23,7 +23,7 @@
ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data); 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 // 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.history || !window.history.pushState){
if(window.location.hash && window.location.hash.indexOf('?') != -1){ if(window.location.hash && window.location.hash.indexOf('?') != -1){
@ -48,15 +48,15 @@
// multiple relationships via keyboard. // multiple relationships via keyboard.
if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus(); if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus();
// Update filter // Update filter
if(self.find('.filter-header').length) { if(self.find('.filter-header').length) {
var content; var content;
if(ajaxOpts.data[0].filter=="show") { if(ajaxOpts.data[0].filter=="show") {
content = '<span class="non-sortable"></span>'; content = '<span class="non-sortable"></span>';
self.addClass('show-filter').find('.filter-header').show(); self.addClass('show-filter').find('.filter-header').show();
} else { } else {
content = '<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>'; 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); self.find('.sortable-header th:last').html(content);
@ -104,7 +104,7 @@
$('.ss-gridfield :button[name=showFilter]').entwine({ $('.ss-gridfield :button[name=showFilter]').entwine({
onclick: function(e) { onclick: function(e) {
$('.filter-header') $('.filter-header')
.show('slow') // animate visibility .show('slow') // animate visibility
.find(':input:first').focus(); // focus first search field .find(':input:first').focus(); // focus first search field
@ -198,11 +198,13 @@
$('.ss-gridfield-print-iframe').entwine({ $('.ss-gridfield-print-iframe').entwine({
onmatch: function(){ onmatch: function(){
this._super();
this.hide().bind('load', function() { this.hide().bind('load', function() {
this.focus(); this.focus();
var ifWin = this.contentWindow || this; var ifWin = this.contentWindow || this;
ifWin.print(); ifWin.print();
});; });
}, },
onunmatch: function() { onunmatch: function() {
this._super(); this._super();
@ -268,15 +270,15 @@
} }
}); });
$('.ss-gridfield[data-selectable] .ss-gridfield-items').entwine({ $('.ss-gridfield[data-selectable] .ss-gridfield-items').entwine({
onmatch: function() { onadd: function() {
this._super(); this._super();
// TODO Limit to single selection // TODO Limit to single selection
this.selectable(); this.selectable();
}, },
onunmatch: function() { onremove: function() {
this._super(); 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.destroy();
this.statusKeyboardNavigation = null; this.statusKeyboardNavigation = null;
} }
} };
ss.editorWrappers.tinyMCE.patched = true; ss.editorWrappers.tinyMCE.patched = true;
} }
@ -89,15 +89,22 @@ ss.editorWrappers.tinyMCE = (function() {
// after an (undetected) inline change. This "blur" causes onChange // after an (undetected) inline change. This "blur" causes onChange
// to trigger, which will change the button markup to show "alternative" styles, // to trigger, which will change the button markup to show "alternative" styles,
// effectively cancelling the original click event. // effectively cancelling the original click event.
var interval; if(ed.settings.update_interval) {
jQuery(ed.getBody()).on('focus', function() { var interval;
interval = setInterval(function() { jQuery(ed.getBody()).on('focus', function() {
ed.save(); interval = setInterval(function() {
}, 5000); // Update underlying element as necessary
}); var element = jQuery(ed.getElement());
jQuery(ed.getBody()).on('blur', function() { if(ed.isDirty()) {
clearInterval(interval); // 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) { this.instance.onChange.add(function(ed, l) {
// Update underlying textarea on every change, so external handlers // 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. * which are toggled through a type dropdown. Variations share fields, so there's only one "title" field in the form.
*/ */
$('form.htmleditorfield-linkform').entwine({ $('form.htmleditorfield-linkform').entwine({
// TODO Entwine doesn't respect submits triggered by ENTER key // TODO Entwine doesn't respect submits triggered by ENTER key
onsubmit: function(e) { onsubmit: function(e) {
this.insertLink(); this.insertLink();
@ -551,8 +559,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
redraw: function() { redraw: function() {
this._super(); this._super();
var linkType = this.find(':input[name=LinkType]:checked').val(), var linkType = this.find(':input[name=LinkType]:checked').val();
list = ['internal', 'external', 'file', 'email'];
this.addAnchorSelector(); this.addAnchorSelector();
@ -571,7 +578,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
if(linkType == 'anchor') { if(linkType == 'anchor') {
this.find('.field[id$="AnchorSelector_Holder"]').show(); this.find('.field[id$="AnchorSelector_Holder"]').show();
this.find('.field[id$="AnchorRefresh_Holder"]').show();
} }
this.find('.field[id$="Description_Holder"]').show(); this.find('.field[id$="Description_Holder"]').show();
}, },
@ -622,8 +628,8 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
} }
return { return {
href : href, href : href,
target : target, target : target,
title : this.find(':input[name=Description]').val() title : this.find(':input[name=Description]').val()
}; };
}, },
@ -641,63 +647,135 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
this.close(); this.close();
}, },
/**
* Builds an anchor selector element and injects it into the DOM next to the anchor field.
*/
addAnchorSelector: function() { addAnchorSelector: function() {
// Avoid adding twice // Avoid adding twice
if(this.find(':input[name=AnchorSelector]').length) return; 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 // Initialise the anchor dropdown.
if( !$.browser.ie ) { this.updateAnchorSelector();
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();
// copy the value from dropdown to the text field // copy the value from dropdown to the text field
anchorSelector.change(function(e) { anchorSelector.change(function(e) {
self.find(':input[name="Anchor"]').val($(this).val()); 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); * Fetch relevant anchors, depending on the link type.
if (raw && raw.length) { *
for(var i = 0; i < raw.length; i++) { * @return $.Deferred A promise of an anchor array, or an error message.
anchors.push(raw[i].substr(6).replace(/"$/, '')); */
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.empty();
selector.append($( selector.append($(
'<option value="" selected="1">' + '<option value="" selected="1">' +
ss.i18n._t('HtmlEditorField.SelectAnchor') + ss.i18n._t('HtmlEditorField.LOOKINGFORANCHORS', 'Looking for anchors...') +
'</option>' '</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. * Updates the state of the dialog inputs to match the editor selection.
* If selection does not contain a link, resets the fields. * 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'); * Return information about the currently selected link, suitable for population of the link form.
target = linkDataSource.attr('target'); *
title = linkDataSource.attr('title'); * Returns null if no link was currently selected.
style_class = linkDataSource.attr('class'); */
href = this.getEditor().cleanLink(href, linkDataSource); getCurrentLink: function() {
action = "update"; 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({ $('form.htmleditorfield-linkform input[name=LinkType]').entwine({
onclick: function(e) { onclick: function(e) {
this.parents('form:first').redraw(); this.parents('form:first').redraw();
this._super();
}, },
onchange: function() { onchange: function() {
this.parents('form:first').redraw(); 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({ $('form.htmleditorfield-linkform :submit[name=action_remove]').entwine({
onclick: function(e) { onclick: function(e) {
this.parents('form:first').removeLink(); this.parents('form:first').removeLink();
this._super();
return false; return false;
} }
}); });
@ -892,7 +990,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
this.find('.Actions .media-update')[editingSelected ? 'show' : 'hide'](); this.find('.Actions .media-update')[editingSelected ? 'show' : 'hide']();
this.find('.ss-uploadfield-item-editform').toggleEditForm(editingSelected); 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-htmleditorfield-file').remove(); // Remove any existing views
this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
this.find('li.ss-uploadfield-item').remove(); // Remove all selected 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({ $('form.htmleditorfield-form.htmleditorfield-mediaform input.remoteurl').entwine({
onadd: function() { onadd: function() {
this._super();
this.validate(); this.validate();
}, },
@ -1128,7 +1227,9 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
* Logic similar to TinyMCE 'advimage' plugin, insertAndClose() method. * Logic similar to TinyMCE 'advimage' plugin, insertAndClose() method.
*/ */
insertHTML: function(ed) { 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 // Get the attributes & extra data
var attrs = this.getAttributes(), extraData = this.getExtraData(); 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'); imgEl = $('<img />').attr(attrs).addClass('ss-htmleditorfield-file embed');
$.each(extraData, function (key, value) { $.each(extraData, function (key, value) {
imgEl.attr('data-' + key, value) imgEl.attr('data-' + key, value);
}); });
if(extraData.CaptionText) { if(extraData.CaptionText) {
@ -1363,10 +1464,10 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
this.height(0); this.height(0);
itemInfo.find('.toggle-details-icon').removeClass('opened'); itemInfo.find('.toggle-details-icon').removeClass('opened');
if(!this.hasClass('edited')){ 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'); status.addClass('ui-state-success-text');
}else{ }else{
text = ss.i18n._t('UploadField.CHANGESSAVED', 'Changes Made') text = ss.i18n._t('UploadField.CHANGESSAVED', 'Changes Made');
this.removeClass('edited'); this.removeClass('edited');
status.addClass('ui-state-success-text'); status.addClass('ui-state-success-text');
} }

View File

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

View File

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

View File

@ -173,7 +173,11 @@
}, },
setValue: function(val) { setValue: function(val) {
this.data('metadata', $.extend(this.data('metadata'), {id: 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() { getValue: function() {
return this.find(':input:hidden').val(); return this.find(':input:hidden').val();
@ -423,11 +427,13 @@
$('.TreeDropdownField input[type=hidden]').entwine({ $('.TreeDropdownField input[type=hidden]').entwine({
onadd: function() { onadd: function() {
this._super();
this.bind('change.TreeDropdownField', function() { this.bind('change.TreeDropdownField', function() {
$(this).getField().updateTitle(); $(this).getField().updateTitle();
}); });
}, },
onremove: function() { onremove: function() {
this._super();
this.unbind('.TreeDropdownField'); this.unbind('.TreeDropdownField');
} }
}); });

View File

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

View File

@ -138,33 +138,50 @@ ss.i18n = {
return stripStr(parts.join(" ")); return stripStr(parts.join(" "));
}, },
/* /**
* printf() * Substitutes %s with parameters
* C-printf like function, which substitutes %s with parameters * given in list. %%s is used to escape %s.
* given in list. %%s is used to escape %s. *
* * @param string S : The string to perform the substitutions on.
* Doesn't work in IE5.0 (splice) * @return string The new string with substitutions made
* */
* @param string S : string to perform printf on.
* @param string L : Array of arguments for printf()
*/
sprintf: function(S) { sprintf: function(S) {
if (arguments.length == 1) return S; if (arguments.length == 1) return S;
var nS = ""; var args = [],
var tS = S.split("%s"); len = arguments.length,
index = 0,
var args = []; regx = new RegExp('(.?)(%s)', 'g'),
for (var i=1, len = arguments.length; i <len; ++i) { result;
for (var i=1; i<len; ++i) {
args.push(arguments[i]); args.push(arguments[i]);
}; };
for(var i=0; i<args.length; i++) { result = S.replace(regx, function(match, subMatch1, subMatch2, offset, string){
if (tS[i].lastIndexOf('%') == tS[i].length-1 && i != args.length-1) if (subMatch1 == '%') return match; // skip %%s
tS[i] += "s"+tS.splice(i+1,1)[0]; return subMatch1 + args[index++];
nS += tS[i] + args[i]; });
}
return nS + tS[tS.length-1]; 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) { sprintf: function(S) {
if (arguments.length == 1) return S; if (arguments.length == 1) return S;
var nS = ""; var args = [],
var tS = S.split("%s"); len = arguments.length,
index = 0,
var args = []; regx = new RegExp('(.?)(%s)', 'g'),
for (var i=1, len = arguments.length; i <len; ++i) { result;
for (var i=1; i<len; ++i) {
args.push(arguments[i]); args.push(arguments[i]);
}; };
for(var i=0; i<args.length; i++) { result = S.replace(regx, function(match, subMatch1, subMatch2, offset, string){
if (tS[i].lastIndexOf('%') == tS[i].length-1 && i != args.length-1) if (subMatch1 == '%') return match; // skip %%s
tS[i] += "s"+tS.splice(i+1,1)[0]; return subMatch1 + args[index++];
nS += tS[i] + args[i]; });
}
return nS + tS[tS.length-1]; 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 // 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'); if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else { } else {
ss.i18n.addDictionary('nl', { ss.i18n.addDictionary('nl', {
"VALIDATOR.FIELDREQUIRED": "Vul het veld \"%s\" in, dit is een verplicht veld.", "VALIDATOR.FIELDREQUIRED": "Vul \"%s\" in, dit is een verplicht veld.",
"HASMANYFILEFIELD.UPLOADING": "Uploading... %s", "HASMANYFILEFIELD.UPLOADING": "Uploaden... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Weet u zeker dat u dit record wilt verwijderen?", "TABLEFIELD.DELETECONFIRMMESSAGE": "Weet u zeker dat u dit record wilt verwijderen?",
"LOADING": "laden...", "LOADING": "laden...",
"UNIQUEFIELD.SUGGESTED": "Waarde gewijzigd naar \"%s\" : %s", "UNIQUEFIELD.SUGGESTED": "Waarde gewijzigd naar \"%s\" : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "U zult een nieuwe waarde voor dit veld moeten invoeren", "UNIQUEFIELD.ENTERNEWVALUE": "U zult een nieuwe waarde voor dit veld moeten invoeren",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dit veld mag niet leeg blijven", "UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dit veld mag niet leeg blijven",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Het karakter \"%s\" mag niet gebruikt worden in dit veld", "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.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": "Het URL is veranderd naar \n\"%s\"", "UPDATEURL.CONFIRMURLCHANGED": "De URL is veranderd naar \n\"%s\"",
"FILEIFRAMEFIELD.DELETEFILE": "Verwijder bestand", "FILEIFRAMEFIELD.DELETEFILE": "Verwijder bestand",
"FILEIFRAMEFIELD.UNATTACHFILE": "Deselecteer bestand", "FILEIFRAMEFIELD.UNATTACHFILE": "Bestand ontkoppelen",
"FILEIFRAMEFIELD.DELETEIMAGE": "Verwijder afbeelding", "FILEIFRAMEFIELD.DELETEIMAGE": "Verwijder afbeelding",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Weet u zeker dat u dit bestand wilt verwijderen?", "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.", "GRIDFIELD.ERRORINTRANSACTION": "Er is een fout opgetreden bij het ophalen van gegevens van de server\n Probeer later opnieuw.",
"HtmlEditorField.SelectAnchor": "Kies een anker", "HtmlEditorField.SelectAnchor": "Kies een anker",
"UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen uit het websitebestand?", "UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen van de server?",
"UploadField.PHP_MAXFILESIZE": "Bestandsgrootte is hoger dan upload_max_filesize (php.ini directive)", "UploadField.PHP_MAXFILESIZE": "Bestand is groter dan upload_max_filesize (limiet in php.ini)",
"UploadField.HTML_MAXFILESIZE": "Bestandsgrootte is hoger danMAX_FILE_SIZE (HTML form directive)", "UploadField.HTML_MAXFILESIZE": "Bestand is groter dan MAX_FILE_SIZE (limiet in HTML formulier)",
"UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geupload", "UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geüpload",
"UploadField.NOFILEUPLOADED": "Geen bestand is geupload", "UploadField.NOFILEUPLOADED": "Er is geen bestand geüpload",
"UploadField.NOTMPFOLDER": "Mist een tijdelijke map", "UploadField.NOTMPFOLDER": "Mist een tijdelijke map",
"UploadField.WRITEFAILED": "Kan bestand niet naar schijf schrijven", "UploadField.WRITEFAILED": "Kan bestand niet naar schijf schrijven",
"UploadField.STOPEDBYEXTENSION": "Bestandsupload gestopt door extensie", "UploadField.STOPEDBYEXTENSION": "Bestandsupload gestopt door extensie",
"UploadField.TOOLARGE": "Bestandsgrootte is te groot", "UploadField.TOOLARGE": "Bestand is te groot",
"UploadField.TOOSMALL": "Bestandsgrootte is te klein", "UploadField.TOOSMALL": "Bestand is te klein",
"UploadField.INVALIDEXTENSION": "Extensie is niet toegestaan", "UploadField.INVALIDEXTENSION": "Extensie is niet toegestaan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal overschreven", "UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal bestanden overschreden",
"UploadField.UPLOADEDBYTES": "Upload overschrijd bestandsgrootte", "UploadField.UPLOADEDBYTES": "Geüploade bytes overschrijden bestandsgrootte",
"UploadField.EMPTYRESULT": "Leeg bestand geupload", "UploadField.EMPTYRESULT": "Leeg bestand geüpload",
"UploadField.LOADING": "Laden ...", "UploadField.LOADING": "Laden...",
"UploadField.Editing": "Bijwerken ...", "UploadField.Editing": "Bijwerken...",
"UploadField.Uploaded": "Geupload", "UploadField.Uploaded": "Geüpload",
"UploadField.OVERWRITEWARNING": "Bestand met dezelfde naam bestaat al", "UploadField.OVERWRITEWARNING": "Bestand met dezelfde naam bestaat al",
"TreeDropdownField.ENTERTOSEARCH": "Druk op enter om te zoeken", "TreeDropdownField.ENTERTOSEARCH": "Druk op enter om te zoeken",
"TreeDropdownField.OpenLink": "Openen", "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.", "VALIDATOR.FIELDREQUIRED": "Vul \"%s\" in, dit is een verplicht veld.",
"HASMANYFILEFIELD.UPLOADING": "Uploading... %s", "HASMANYFILEFIELD.UPLOADING": "Uploaden... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Weet u zeker dat u dit record wilt verwijderen?", "TABLEFIELD.DELETECONFIRMMESSAGE": "Weet u zeker dat u dit record wilt verwijderen?",
"LOADING": "laden...", "LOADING": "laden...",
"UNIQUEFIELD.SUGGESTED": "Waarde gewijzigd naar \"%s\" : %s", "UNIQUEFIELD.SUGGESTED": "Waarde gewijzigd naar \"%s\" : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "U zult een nieuwe waarde voor dit veld moeten invoeren", "UNIQUEFIELD.ENTERNEWVALUE": "U zult een nieuwe waarde voor dit veld moeten invoeren",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dit veld mag niet leeg blijven", "UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dit veld mag niet leeg blijven",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Het karakter \"%s\" mag niet gebruikt worden in dit veld", "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.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": "Het URL is veranderd naar \n\"%s\"", "UPDATEURL.CONFIRMURLCHANGED": "De URL is veranderd naar \n\"%s\"",
"FILEIFRAMEFIELD.DELETEFILE": "Verwijder bestand", "FILEIFRAMEFIELD.DELETEFILE": "Verwijder bestand",
"FILEIFRAMEFIELD.UNATTACHFILE": "Deselecteer bestand", "FILEIFRAMEFIELD.UNATTACHFILE": "Bestand ontkoppelen",
"FILEIFRAMEFIELD.DELETEIMAGE": "Verwijder afbeelding", "FILEIFRAMEFIELD.DELETEIMAGE": "Verwijder afbeelding",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Weet u zeker dat u dit bestand wilt verwijderen?", "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.", "GRIDFIELD.ERRORINTRANSACTION": "Er is een fout opgetreden bij het ophalen van gegevens van de server\n Probeer later opnieuw.",
"HtmlEditorField.SelectAnchor": "Kies een anker", "HtmlEditorField.SelectAnchor": "Kies een anker",
"UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen uit het websitebestand?", "UploadField.ConfirmDelete": "Weet u zeker dat u dit bestand wilt verwijderen van de server?",
"UploadField.PHP_MAXFILESIZE": "Bestandsgrootte is hoger dan upload_max_filesize (php.ini directive)", "UploadField.PHP_MAXFILESIZE": "Bestand is groter dan upload_max_filesize (limiet in php.ini)",
"UploadField.HTML_MAXFILESIZE": "Bestandsgrootte is hoger danMAX_FILE_SIZE (HTML form directive)", "UploadField.HTML_MAXFILESIZE": "Bestand is groter dan MAX_FILE_SIZE (limiet in HTML formulier)",
"UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geupload", "UploadField.ONLYPARTIALUPLOADED": "Bestand is maar gedeeltelijk geüpload",
"UploadField.NOFILEUPLOADED": "Geen bestand is geupload", "UploadField.NOFILEUPLOADED": "Er is geen bestand geüpload",
"UploadField.NOTMPFOLDER": "Mist een tijdelijke map", "UploadField.NOTMPFOLDER": "Mist een tijdelijke map",
"UploadField.WRITEFAILED": "Kan bestand niet naar schijf schrijven", "UploadField.WRITEFAILED": "Kan bestand niet naar schijf schrijven",
"UploadField.STOPEDBYEXTENSION": "Bestandsupload gestopt door extensie", "UploadField.STOPEDBYEXTENSION": "Bestandsupload gestopt door extensie",
"UploadField.TOOLARGE": "Bestandsgrootte is te groot", "UploadField.TOOLARGE": "Bestand is te groot",
"UploadField.TOOSMALL": "Bestandsgrootte is te klein", "UploadField.TOOSMALL": "Bestand is te klein",
"UploadField.INVALIDEXTENSION": "Extensie is niet toegestaan", "UploadField.INVALIDEXTENSION": "Extensie is niet toegestaan",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal overschreven", "UploadField.MAXNUMBEROFFILESSIMPLE": "Maximaal aantal bestanden overschreden",
"UploadField.UPLOADEDBYTES": "Upload overschrijd bestandsgrootte", "UploadField.UPLOADEDBYTES": "Geüploade bytes overschrijden bestandsgrootte",
"UploadField.EMPTYRESULT": "Leeg bestand geupload", "UploadField.EMPTYRESULT": "Leeg bestand geüpload",
"UploadField.LOADING": "Laden ...", "UploadField.LOADING": "Laden...",
"UploadField.Editing": "Bijwerken ...", "UploadField.Editing": "Bijwerken...",
"UploadField.Uploaded": "Geupload", "UploadField.Uploaded": "Geüpload",
"UploadField.OVERWRITEWARNING": "Bestand met dezelfde naam bestaat al", "UploadField.OVERWRITEWARNING": "Bestand met dezelfde naam bestaat al",
"TreeDropdownField.ENTERTOSEARCH": "Druk op enter om te zoeken", "TreeDropdownField.ENTERTOSEARCH": "Druk op enter om te zoeken",
"TreeDropdownField.OpenLink": "Openen", "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: eo:
AssetAdmin:
NEWFOLDER: Nova dosierujo
SHOWALLOWEDEXTS: 'Vidigi permesitajn sufiksojn'
AssetTableField: AssetTableField:
CREATED: 'Unue alŝutita' CREATED: 'Unue alŝutita'
DIM: Dimensioj DIM: Dimensioj
FILENAME: Nomo de dosiero FILENAME: Nomo de dosiero
FOLDER: Dosierujo
LASTEDIT: 'Laste ŝanĝita' LASTEDIT: 'Laste ŝanĝita'
OWNER: Posedanto OWNER: Posedanto
SIZE: 'Grando' SIZE: 'Grando'
TITLE: Titolo TITLE: Titolo
TYPE: 'Tipo' 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: BBCodeParser:
ALIGNEMENT: Ĝisrandigo ALIGNEMENT: Ĝisrandigo
ALIGNEMENTEXAMPLE: 'ĝisrandigita dekstren' ALIGNEMENTEXAMPLE: 'ĝisrandigita dekstren'
@ -33,16 +52,26 @@ eo:
UNORDERED: 'Neordigita listo' UNORDERED: 'Neordigita listo'
UNORDEREDDESCRIPTION: 'Neordigita listo' UNORDEREDDESCRIPTION: 'Neordigita listo'
UNORDEREDEXAMPLE1: 'neordigita ero 1' UNORDEREDEXAMPLE1: 'neordigita ero 1'
BackLink_Button_ss:
Back: Retro
BasicAuth: BasicAuth:
ENTERINFO: 'bonvolu enigi salutnomon kaj pasvorton.' ENTERINFO: 'bonvolu enigi salutnomon kaj pasvorton.'
ERRORNOTADMIN: 'Tiu uzanto ne estas administranto.' ERRORNOTADMIN: 'Tiu uzanto ne estas administranto.'
ERRORNOTREC: 'Kiuj salutnomo / pasvorto ne estas rekonebla' ERRORNOTREC: 'Kiuj salutnomo / pasvorto ne estas rekonebla'
Boolean: Boolean:
ANY: Ajna ANY: Ajna
CMSLoadingScreen_ss:
LOADING: Ŝargas...
REQUIREJS: 'La CMS bezonas ke vi enŝaltis Ĝavaskripton.'
CMSMain: CMSMain:
ACCESS: 'Aliro al sekcio ''{title}'''
ACCESSALLINTERFACES: 'Aliro al ĉiuj interfacoj de CMS' ACCESSALLINTERFACES: 'Aliro al ĉiuj interfacoj de CMS'
ACCESSALLINTERFACESHELP: 'Nuligas pli specifajn alirajn agordojn.' ACCESSALLINTERFACESHELP: 'Nuligas pli specifajn alirajn agordojn.'
SAVE: Konservi SAVE: Konservi
CMSPageHistoryController_versions_ss:
PREVIEW: 'Antaŭvido de retejo'
CMSProfileController:
MENUTITLE: 'Mia agordaro'
ChangePasswordEmail_ss: ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Vi ŝanĝis vian pasvorton por' CHANGEPASSWORDTEXT1: 'Vi ŝanĝis vian pasvorton por'
CHANGEPASSWORDTEXT2: 'Nun vi povas uzi la jenan legitimaĵon por ensaluti:' CHANGEPASSWORDTEXT2: 'Nun vi povas uzi la jenan legitimaĵon por ensaluti:'
@ -50,75 +79,204 @@ eo:
HELLO: Saluton HELLO: Saluton
PASSWORD: Pasvorto PASSWORD: Pasvorto
ConfirmedPasswordField: 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' SHOWONCLICKTITLE: 'Ŝangi Pasvorton'
ContentController:
NOTLOGGEDIN: 'Ne ensalutis'
CreditCardField: CreditCardField:
FIRST: unuan FIRST: unuan
FOURTH: kvaran FOURTH: kvaran
SECOND: duan SECOND: duan
THIRD: trian THIRD: trian
CurrencyField:
CURRENCYSYMBOL: $
DataObject: DataObject:
PLURALNAME: 'Datumaj Objektoj' PLURALNAME: 'Datumaj Objektoj'
SINGULARNAME: 'Datuma Objekto' 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: DateField:
NOTSET: 'ne agordita' NOTSET: 'ne agordita'
TODAY: hodiaŭ 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: DropdownField:
CHOOSE: (Elekti) CHOOSE: (Elekti)
CHOOSESEARCH: '(Elekti aŭ serĉi)'
EmailField:
VALIDATION: 'Bonvolu enigi readreson'
Enum: Enum:
ANY: Ajna ANY: Ajna
File: File:
AviType: 'AVI videa dosiero'
Content: Enhavo Content: Enhavo
CssType: 'CSS-dosiero'
DmgType: 'Apple-diska bildo'
DocType: 'Word-dokumento'
Filename: Dosiernomo 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' NOFILESIZE: 'Dosiero havas nul bajtojn'
NOVALIDUPLOAD: 'Dosiero ne estas valida alŝutaĵo.' NOVALIDUPLOAD: 'Dosiero ne estas valida alŝutaĵo.'
Name: Nomo Name: Nomo
PLURALNAME: Dosieroj PLURALNAME: Dosieroj
PdfType: 'Dosiero de Adobe Acrobat PDF'
PngType: 'PNG-bildo - ĝeneralcela formato'
SINGULARNAME: Dosiero SINGULARNAME: Dosiero
TOOLARGE: 'Dosiero estas tro granda; maksimumo estas {size}'
TOOLARGESHORT: 'Grando de dosiero superas je {size}'
TiffType: 'Markita bildoformato'
Title: Titolo 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: ForgotPasswordEmail_ss:
HELLO: Saluton HELLO: Saluton
TEXT1: 'Jen via' TEXT1: 'Jen via'
TEXT2: 'pasvorta reagorda ligilo' TEXT2: 'pasvorta reagorda ligilo'
TEXT3: por TEXT3: por
Form: 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' VALIDATIONNOTUNIQUE: 'La enirita valoron ne unika'
VALIDATIONPASSWORDSDONTMATCH: 'Pasvortoj ne matĉas' VALIDATIONPASSWORDSDONTMATCH: 'Pasvortoj ne matĉas'
VALIDATIONPASSWORDSNOTEMPTY: 'Pasvortoj ne povas est malplena' VALIDATIONPASSWORDSNOTEMPTY: 'Pasvortoj ne povas est malplena'
VALIDATIONSTRONGPASSWORD: 'Pasvorto devas havi almenaŭ unu signon kaj unu literon.'
VALIDATOR: Validigilo VALIDATOR: Validigilo
VALIDCURRENCY: 'Bonvole enigu validan kurzon'
CSRF_EXPIRED_MESSAGE: 'Via seanco finiĝis. Bonvole resendu la formularon.'
FormField: FormField:
Example: 'ekz. %s'
NONE: neniu 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: Group:
AddRole: 'Aldoni rolon por ĉi tiu grupo'
Code: 'Grupa Kodo' Code: 'Grupa Kodo'
DefaultGroupTitleAdministrators: Administrantoj DefaultGroupTitleAdministrators: Administrantoj
DefaultGroupTitleContentAuthors: 'Enhavaŭtoroj' DefaultGroupTitleContentAuthors: 'Enhavaŭtoroj'
Description: Priskribo 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?' Locked: 'Ŝlosita?'
NoRoles: 'Ne trovis rolon'
PLURALNAME: Grupoj
Parent: 'Patra Grupo' Parent: 'Patra Grupo'
RolesAddEditLink: 'Aldoni/redakti rolojn' RolesAddEditLink: 'Aldoni/redakti rolojn'
SINGULARNAME: Grupo
Sort: 'Ordiga Ordo' Sort: 'Ordiga Ordo'
has_many_Permissions: Permesoj has_many_Permissions: Permesoj
many_many_Members: Membroj many_many_Members: Membroj
GroupImportForm: 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>' 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' ResultDeleted: 'Forigis %d grupojn'
ResultUpdated: 'Aktualigis %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: HtmlEditorField:
ADDURL: 'Aldoni je URL'
ADJUSTDETAILSDIMENSIONS: 'Detaloj kaj dimensioj'
ANCHORVALUE: Ankri ANCHORVALUE: Ankri
BUTTONADDURL: 'Aldoni je url'
BUTTONINSERT: Enmeti
BUTTONINSERTLINK: 'Almeti ligilon' BUTTONINSERTLINK: 'Almeti ligilon'
BUTTONREMOVELINK: 'Forigi ligilon' BUTTONREMOVELINK: 'Forigi ligilon'
BUTTONUpdate: Ĝisdatigi
CAPTIONTEXT: 'Titola teksto' CAPTIONTEXT: 'Titola teksto'
CSSCLASS: 'Ĝisrandigo / stilo' CSSCLASS: 'Ĝisrandigo / stilo'
CSSCLASSCENTER: 'Centrita, sola.' CSSCLASSCENTER: 'Centrita, sola.'
CSSCLASSLEFT: 'Maldekstre, kaj teksto ĉirkaŭfluas.' CSSCLASSLEFT: 'Maldekstre, kaj teksto ĉirkaŭfluas.'
CSSCLASSLEFTALONE: 'Maldekstre sole' CSSCLASSLEFTALONE: 'Maldekstre sole'
CSSCLASSRIGHT: 'Dekstre, kaj teksto ĉirkaŭfluas.' CSSCLASSRIGHT: 'Dekstre, kaj teksto ĉirkaŭfluas.'
DETAILS: Detaloj
EMAIL: 'Retpoŝta adreso' EMAIL: 'Retpoŝta adreso'
FILE: Dosiero FILE: Dosiero
FOLDER: Dosierujo 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 IMAGEDIMENSIONS: Dimensioj
IMAGEHEIGHTPX: Alto IMAGEHEIGHTPX: Alto
IMAGETITLE: 'Titola teksto (ŝpruchelpilo) - por plua informo pri la bildo' 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 IMAGEWIDTHPX: Larĝo
INSERTMEDIA: 'Enmeti memorilon'
LINK: 'Ligilo' LINK: 'Ligilo'
LINKANCHOR: 'Ankri al ĉi tiu paĝo' LINKANCHOR: 'Ankri al ĉi tiu paĝo'
LINKDESCR: 'Ligila priskribo' LINKDESCR: 'Ligila priskribo'
@ -129,34 +287,68 @@ eo:
LINKOPENNEWWIN: 'Malfermi ligilon en nova fenestro?' LINKOPENNEWWIN: 'Malfermi ligilon en nova fenestro?'
LINKTO: 'Ligilo al' LINKTO: 'Ligilo al'
PAGE: Paĝo 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: Image_iframe_ss:
TITLE: 'Iframe por alŝuti bildon' TITLE: 'Iframe por alŝuti bildon'
LeftAndMain: LeftAndMain:
CANT_REORGANISE: 'Vi ne rajtas ŝanĝi supronivelajn paĝojn. Via ŝanĝo ne konserviĝis.'
DELETED: Forigita.
DropdownBatchActionsDefault: Agoj
HELP: Helpo HELP: Helpo
PAGETYPE: 'Tipo de paĝo:' PAGETYPE: 'Tipo de paĝo:'
PERMAGAIN: 'Vin adiaŭis la CMS. Se vi volas denove saluti, enigu salutnomon kaj pasvorton malsupre.' 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' 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.' PERMDEFAULT: 'Enigi vian retadreson kaj pasvorton por aliri al la CMS.'
PLEASESAVE: 'Bonvolu konservi paĝon: Ne eblis ĝisdatigi ĉi tiun paĝon ĉar ĝi ankoraŭ ne estas konservita.' PLEASESAVE: 'Bonvolu konservi paĝon: Ne 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: LoginAttempt:
Email: 'Retadreso' Email: 'Retadreso'
IP: 'IP-Adreso' IP: 'IP-Adreso'
PLURALNAME: 'Provoj ensaluti'
SINGULARNAME: 'Provo ensaluti'
Status: Stato Status: Stato
Member: Member:
BUTTONCHANGEPASSWORD: 'Ŝanĝi Pasvorton' ADDGROUP: 'Aldoni grupon'
BUTTONCHANGEPASSWORD: 'Ŝanĝi pasvorton'
BUTTONLOGIN: 'Ensaluti' BUTTONLOGIN: 'Ensaluti'
BUTTONLOGINOTHER: 'Ensaluti kiel alia homo' BUTTONLOGINOTHER: 'Ensaluti kiel alia homo'
BUTTONLOSTPASSWORD: 'Mi perdis mian pasvorton' BUTTONLOSTPASSWORD: 'Mi perdis mian pasvorton'
CANTEDIT: 'Vi ne rajtas fari tion'
CONFIRMNEWPASSWORD: 'Konfirmu novan pasvorton' CONFIRMNEWPASSWORD: 'Konfirmu novan pasvorton'
CONFIRMPASSWORD: 'Konfirmu pasvorton' CONFIRMPASSWORD: 'Konfirmu pasvorton'
DATEFORMAT: 'Formato de dato'
DefaultAdminFirstname: 'Defaŭlta Administranto' DefaultAdminFirstname: 'Defaŭlta Administranto'
DefaultDateTime: apriora
EMAIL: Retpoŝto EMAIL: Retpoŝto
EMPTYNEWPASSWORD: 'La nova pasvorto ne povas esti nula, bonvole refaru'
ENTEREMAIL: 'Bonvolu enigi retadreson por atingi ligilon por reagordi pasvorton.' 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' ERRORNEWPASSWORD: 'Via ricev enirita vian novan pasvorton malsame, prov denove'
ERRORPASSWORDNOTMATCH: 'Via aktuala pasvorto ne matĉo, bonvolu prov denove' ERRORPASSWORDNOTMATCH: 'Via aktuala pasvorto ne matĉo, bonvolu prov denove'
ERRORWRONGCRED: 'La donitaj detaloj ŝajnas malĝustaj. Bonvole reprovu.'
FIRSTNAME: 'Antaŭnomo' FIRSTNAME: 'Antaŭnomo'
INTERFACELANG: 'Interfaca Lingvo' INTERFACELANG: 'Interfaca Lingvo'
INVALIDNEWPASSWORD: 'Ni ne povis akcepti tiun pasvorton: {password}'
LOGGEDINAS: 'Vi ensalutis kiel {name}.'
NEWPASSWORD: 'Novan pasvorton' NEWPASSWORD: 'Novan pasvorton'
NoPassword: 'Mankas pasvorto por ĉi tiu membro.'
PASSWORD: Pasvorto PASSWORD: Pasvorto
PLURALNAME: Membroj PLURALNAME: Membroj
REMEMBERME: 'Memoru min je la sekva fojo?' REMEMBERME: 'Memoru min je la sekva fojo?'
@ -164,7 +356,10 @@ eo:
SUBJECTPASSWORDCHANGED: 'Via pasvorto estas ŝanĝita' SUBJECTPASSWORDCHANGED: 'Via pasvorto estas ŝanĝita'
SUBJECTPASSWORDRESET: 'Via pasvorto reagordis ligilon' SUBJECTPASSWORDRESET: 'Via pasvorto reagordis ligilon'
SURNAME: Familia nomo SURNAME: Familia nomo
TIMEFORMAT: 'Formato de horo'
VALIDATIONMEMBEREXISTS: 'Jam ekzistas membro kun la sama %s' 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' YOUROLDPASSWORD: 'Vian malnovan pasvorton'
belongs_many_many_Groups: Grupoj belongs_many_many_Groups: Grupoj
db_LastVisited: 'Dato de Lasta Vizito' db_LastVisited: 'Dato de Lasta Vizito'
@ -175,29 +370,98 @@ eo:
db_PasswordExpiry: 'Pasvorta Limdato' db_PasswordExpiry: 'Pasvorta Limdato'
MemberAuthenticator: MemberAuthenticator:
TITLE: 'Retpoŝto &amp; Pasvorto' 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: 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' ResultDeleted: 'Forigis %d membrojn'
ResultNone: 'Neniu ŝanĝo' ResultNone: 'Neniu ŝanĝo'
ResultUpdated: 'Aktualigis {count} membrojn'
MemberPassword:
PLURALNAME: 'Membraj pasvortoj'
SINGULARNAME: 'Membra pasvorto'
MemberTableField:
APPLY_FILTER: 'Apliki filtrilon'
ModelAdmin: ModelAdmin:
DELETE: Forigi DELETE: Forigi
DELETEDRECORDS: 'Forigis {count} rikordojn.'
EMPTYBEFOREIMPORT: 'Anstataŭigi datumojn'
IMPORT: 'Importi el CSV' IMPORT: 'Importi el CSV'
IMPORTEDRECORDS: 'Importis {count} rikordojn.'
NOCSVFILE: 'Bonvolu foliumi por CSV-dosiero importota' NOCSVFILE: 'Bonvolu foliumi por CSV-dosiero importota'
NOIMPORT: 'Nenio 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: MoneyField:
FIELDLABELAMOUNT: Kvanto FIELDLABELAMOUNT: Kvanto
FIELDLABELCURRENCY: Kurzo FIELDLABELCURRENCY: Kurzo
NullableField: NullableField:
IsNullLabel: 'Estas senvalora' 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: Permission:
AdminGroup: Administranto AdminGroup: Administranto
CMS_ACCESS_CATEGORY: 'CMS-aliro' CMS_ACCESS_CATEGORY: 'CMS-aliro'
FULLADMINRIGHTS: 'Ĉiuj administraj rajtoj' FULLADMINRIGHTS: 'Ĉiuj administraj rajtoj'
FULLADMINRIGHTS_HELP: 'Implicas kaj superregas ĉiujn aliajn agorditajn permesojn.' FULLADMINRIGHTS_HELP: 'Implicas kaj superregas ĉiujn aliajn agorditajn permesojn.'
PLURALNAME: Permesoj
SINGULARNAME: Permeso
PermissionCheckboxSetField: 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:
PERMISSIONS_CATEGORY: 'Roloj kaj aliraj permesoj' 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: PhoneNumberField:
VALIDATION: 'Bonvolu enigi validan telefonnumeron' VALIDATION: 'Bonvolu enigi validan telefonnumeron'
Security: Security:
@ -209,8 +473,12 @@ eo:
ERRORPASSWORDPERMISSION: 'Vi devas ensaluti por ŝanĝi vian pasvorton!' ERRORPASSWORDPERMISSION: 'Vi devas ensaluti por ŝanĝi vian pasvorton!'
LOGGEDOUT: 'Vi elsalutis. Se vi volas ensaluti denove, enigu viajn legitimaĵon sube.' LOGGEDOUT: 'Vi elsalutis. Se vi volas ensaluti denove, enigu viajn legitimaĵon sube.'
LOGIN: 'Ensaluti' LOGIN: 'Ensaluti'
LOSTPASSWORDHEADER: 'Perdis pasvorton'
NOTEPAGESECURED: 'Tiu paĝo estas sekurigita. Enigu viajn akreditaĵojn sube kaj vi aliros pluen.' 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' 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: SecurityAdmin:
ACCESS_HELP: 'Permesi vidigi, enmeti kaj redakti uzantojn, aldone al agordi permesojn kaj rolojn al ili.' ACCESS_HELP: 'Permesi vidigi, enmeti kaj redakti uzantojn, aldone al agordi permesojn kaj rolojn al ili.'
APPLY_ROLES: 'Apliki roloj al grupoj' APPLY_ROLES: 'Apliki roloj al grupoj'
@ -218,22 +486,74 @@ eo:
EDITPERMISSIONS: 'Agordi permesojn kaj IP-adresojn ĉe ĉiu grupo' 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''"' EDITPERMISSIONS_HELP: 'Eblo redakti Permesojn kaj IP-adresojn por grupo. Bezonas la permesilon "Aliro al sekcio ''Sekureco''"'
GROUPNAME: 'Grupa Nomo' GROUPNAME: 'Grupa Nomo'
IMPORTGROUPS: 'Importi grupojn'
IMPORTUSERS: 'Importi uzulojn'
MEMBERS: Membroj MEMBERS: Membroj
MENUTITLE: Sekureco
MemberListCaution: 'Averto: forigi membrojn el ĉi tiu listo forigos ilin el ĉiuj grupoj kaj la datumbazo.' MemberListCaution: 'Averto: forigi membrojn el ĉi tiu listo forigos ilin el ĉiuj grupoj kaj la datumbazo.'
NEWGROUP: 'Nova Grupo' NEWGROUP: 'Nova Grupo'
PERMISSIONS: Permesoj PERMISSIONS: Permesoj
ROLES: Roloj 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' 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 TABROLES: Roloj
Users: Uzuloj
SecurityAdmin_MemberImportForm: SecurityAdmin_MemberImportForm:
BtnImport: 'Importi' BtnImport: 'Importi'
FileFieldLabel: 'CSV-dosiero <small>(Permesitaj sufiksoj: *.csv)</small>' 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: SiteTree:
TABMAIN: Ĉefaj TABMAIN: Ĉefaj
TableListField: TableListField:
CSVEXPORT: 'Eksporti al CSV' CSVEXPORT: 'Eksporti al CSV'
Print: Presi
TableListField_PageControls_ss:
OF: de
TimeField:
VALIDATEFORMAT: 'Bonvole enigu validan horan formaton ({format})'
ToggleField: ToggleField:
LESS: malpli LESS: malpli
MORE: pli 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: Versioned:
has_many_Versions: Versioj has_many_Versions: Versioj

View File

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

View File

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

View File

@ -234,9 +234,9 @@ class DB {
$dbClass = $databaseConfig['type']; $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 // which may or may not map to explicit objects
$conn = Injector::inst()->get($dbClass); $conn = Injector::inst()->create($dbClass);
$conn->connect($databaseConfig); $conn->connect($databaseConfig);
self::set_conn($conn, $label); self::set_conn($conn, $label);

View File

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

View File

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

View File

@ -73,11 +73,11 @@ class Image extends File {
private static $force_resample = false; private static $force_resample = false;
public static function set_backend($backend) { public static function set_backend($backend) {
self::$backend = $backend; self::config()->backend = $backend;
} }
public static function get_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); $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, Director::baseFolder()."/" . $this->Filename,
$args $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 */ /* 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); $words = preg_split('/\s+/', $paragraph);
foreach ($words as $i => $word) { 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)); 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. * Caution: Not XML/HTML-safe - does not respect closing tags.
*/ */
public function FirstSentence() { public function FirstSentence() {
$data = Convert::xml2raw( $this->value ); $paragraph = Convert::xml2raw( $this->value );
if( !$data ) return ""; if( !$paragraph ) return "";
$words = preg_split('/\s+/', $paragraph);
$sentences = explode( '.', $data ); foreach ($words as $i => $word) {
if (preg_match('/(!|\?|\.)$/', $word) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word)) {
if( count( $sentences ) ) return implode(' ', array_slice($words, 0, $i+1));
return $sentences[0] . '.'; }
else }
return $this->Summary(20);
} /* 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. * 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']) . ")"; $filter = "(" . implode(") AND (", $join['filter']) . ")";
} }
$table = strpos(strtoupper($join['table']), 'SELECT') ? $join['table'] : "\"" . $join['table'] . "\"";
$aliasClause = ($alias != $join['table']) ? " AS \"$alias\"" : ""; $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); text-shadow: 0px 1px 0px rgba(#fff, 0.5);
&.ui-state-error-text { &.ui-state-error-text {
max-width:70%;
position:absolute;
right:5px;
text-shadow: 0px 1px 0px rgba(#fff, 0.6); text-shadow: 0px 1px 0px rgba(#fff, 0.6);
color: darken($color-button-destructive, 10%); color: darken($color-button-destructive, 10%);
} }

View File

@ -13,7 +13,11 @@
.middleColumn { .middleColumn {
// TODO .middleColumn styling should probably be theme specific (eg cms ui will look different than blackcandy) // 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 // 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; padding: 0;
background: #fff; background: #fff;
border: 1px solid lighten($color-medium-separator, 20%); 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$/ * @When /^I click the "([^"]*)" CMS tab$/
*/ */

View File

@ -96,7 +96,7 @@ class DirectorTest extends SapphireTest {
$rootURL = Director::protocolAndHost(); $rootURL = Director::protocolAndHost();
$_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/"; $_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/";
Config::inst()->update('Director', 'alternate_base_url', '/mysite/'); Config::inst()->update('Director', 'alternate_base_url', '/mysite/');
// Test already absolute url // Test already absolute url
$this->assertEquals($rootURL, Director::absoluteURL($rootURL)); $this->assertEquals($rootURL, Director::absoluteURL($rootURL));
$this->assertEquals($rootURL, Director::absoluteURL($rootURL, true)); $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 String $html [description]
* @param array $nodes Breadcrumb path as array * @param array $nodes Breadcrumb path as array
@ -417,6 +508,25 @@ class HierarchyTest extends SapphireTest {
self::assertThat((bool)$match, self::isFalse(), $message); 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 { class HierarchyTest_Object extends DataObject implements TestOnly {
@ -428,4 +538,8 @@ class HierarchyTest_Object extends DataObject implements TestOnly {
'Hierarchy', 'Hierarchy',
"Versioned('Stage', 'Live')", "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() { public function testSetWhereAny() {
$query = new SQLSelect(); $query = new SQLSelect();
$query->setFrom('MyTable'); $query->setFrom('MyTable');

View File

@ -87,7 +87,28 @@ class TextTest extends SapphireTest {
$this->assertEquals($expectedValue, $textObj->LimitSentences(2)); $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()} * Test {@link Text->BigSummary()}
*/ */