Merging refactored Translatable from trunk, and related changes to CMSMain

------------------------------------------------------------------------
r69959 | ischommer | 2009-01-11 01:15:30 +1300 (Sun, 11 Jan 2009) | 18 lines

Merged, debugged and enhanced Translatable patches from branches/translatable at r64523, r64523, 64523, thanks wakeless!
API CHANGE Changed Translatable schema from auxilliary tables (SiteTree_lang, SiteTree_lang_Live) to automatically filtered records on the original table (SiteTree, SiteTree_Live), using $Lang and $OriginalID properties. Incompatible update to old schema, migration script is in the works.
API CHANGE Removed Translatable::get_one(), Translatable::write()
ENHANCEMENT Simplified Translatable tree generation by using getSiteTreeFor() in CMSMain->createtranslation()
ENHANCEMENT Added AllChildrenIncludingDeleted(), augmentNumChildrenCountQuery(),  augmentAllChildrenIncludingDeleted(), augmentStageChildren() to Translatable class to allow for more stable tree generation.
ENHANCEMENT Moved definition of Translatable schema from augmentDatabase() to Translatable->extraStatics()
ENHANCEMENT Changes to the CMS language selection refresh the whole admin interface instead of the tree only. This way we can add a URL parameter ?lang=<lang> to /admin, which makes the specific language bookmarkable and reloadable. Changes to LangSelector.js
ENHANCEMENT Added fallback to ModelAsController->getNestedController() to fetch page with matching URLSegment but different language in case no page is found in the current language.
ENHANCEMENT Added helper methods to Translatable: getTranslation(), hasTranslation(), isTranslation(), findOriginalIDs()
ENHANCEMENT Getters and setters for Translatable->getOriginalPage() etc.
ENHANCEMENT Hooking Translatable into ModelAsController and ContentController initialization in order to call choose_site_lang()
ENHANCEMENT Simplified Translatable->augmentSQL(), augmentWrite() by not using auxilliary tables
ENHANCEMENT Showing clickable links for Translations in Translatable->updateCMSFields()
BUGFIX Modifying Hierarchy/SiteTree Children getters to accept optional "context" which can be used to set a language explicitly through the $Lang property, rather than implicitly reyling on the static Translatable::current_lang()
BUGFIX Fixed TranslatableTest to work with new datamodel
BUGFIX Temporarily disabled cookie/session selection in Translatable::choose_site_lang() until we have a good test suite for the side effects.
MINOR Added "untranslated" CSS styles to tree nodes and marking them as inactive/grey

------------------------------------------------------------------------
r70307 | ischommer | 2009-01-16 17:16:19 +1300 (Fri, 16 Jan 2009) | 2 lines

ENHANCEMENT Simplifying creation logic of new languages in CMS by reloading complete interface, rather than refreshing partial interface, language dropdown etc.
API CHANGE Removed CMSMain->switchlanguage()
------------------------------------------------------------------------
r70308 | ischommer | 2009-01-16 17:17:37 +1300 (Fri, 16 Jan 2009) | 2 lines

ENHANCEMENT Moved language selector in CMS above tree for better visibility, added padding and adjusted dropdown width
ENHANCEMENT Removed "Translating mode" status message above edit form - should be clear by the language dropdown above the CMS tree now
------------------------------------------------------------------------
r70322 | ischommer | 2009-01-19 13:09:55 +1300 (Mon, 19 Jan 2009) | 1 line

BUGFIX Ensuring that new pages can't be created when in translation mode by disabling the "create..." tree action
------------------------------------------------------------------------
r70323 | ischommer | 2009-01-19 13:11:08 +1300 (Mon, 19 Jan 2009) | 1 line

BUGFIX Fixed expanded/unexpanded flags on new tree items - was showing expanded styling (plus icon) with newly created pages
------------------------------------------------------------------------
r70324 | ischommer | 2009-01-19 13:26:02 +1300 (Mon, 19 Jan 2009) | 1 line

BUGFIX Making sure that LeftAndMain->CurrentPage() respects language settings - was returning pages in different language from session after switching between languages in cms
------------------------------------------------------------------------
r71297 | sharvey | 2009-02-03 18:12:42 +1300 (Tue, 03 Feb 2009) | 1 line

BUGFIX Only show the LangSelector dropdown if there's multiple languages available on the site
------------------------------------------------------------------------
r73343 | ischommer | 2009-03-19 06:14:02 +1300 (Thu, 19 Mar 2009) | 1 line

BUGFIX Changed CSS selector for TranslationTab javascript behaviour to be less specific
------------------------------------------------------------------------
r73344 | ischommer | 2009-03-19 06:14:59 +1300 (Thu, 19 Mar 2009) | 1 line

BUGFIX Checking for existence of original before trying to get translation in LeftAndMain->currentPage()
------------------------------------------------------------------------
r73345 | ischommer | 2009-03-19 06:18:52 +1300 (Thu, 19 Mar 2009) | 3 lines

ENHANCEMENT Showing all available languages in language selector above sitetree in CMS - you can now create new pages in completely new languages without any relation to an "original"
ENHANCEMENT Added CMSMain->IsTranslatableEnabled
API CHANGE Removed CMSMain->switchlanguage() - createTranslation() is sufficient for new, ajax refreshing of CMS state got way too complicated for switching languages, we now just reload the entire CMS with a different ?lang GET parameter
------------------------------------------------------------------------
r73469 | ischommer | 2009-03-20 21:49:27 +1300 (Fri, 20 Mar 2009) | 1 line

ENHANCEMENT Adjusted CMSMain and LeftAndMain to use locales instead of short lang codes when reading and writing translations. See r73468 for details on the underlying Translatable datamodel change
------------------------------------------------------------------------
r74071 | ischommer | 2009-04-04 10:24:59 +1300 (Sat, 04 Apr 2009) | 1 line

BUGFIX Fixed Form_EditForm_Locale reference in LeftAndMain_right.js (used to be Form_EditForm_Lang)
------------------------------------------------------------------------
r74072 | ischommer | 2009-04-04 10:26:51 +1300 (Sat, 04 Apr 2009) | 1 line

MINOR Using Translatable::choose_site_locale() instead of choose_site_lang() in LeftAndMain->init()
------------------------------------------------------------------------
r74951 | ischommer | 2009-04-22 16:23:56 +1200 (Wed, 22 Apr 2009) | 1 line

BUGFIX Fixed CMSMainTest->testThatGetCMSFieldsWorksOnEveryPageType() - was comparing a string $class with instanceof() instead of comparing the actually created instance
------------------------------------------------------------------------



git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/branches/2.3@74988 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-04-23 01:46:49 +00:00 committed by Sam Minnee
parent 33ce4d8824
commit 74fad6a1d0
10 changed files with 124 additions and 181 deletions

View File

@ -49,7 +49,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
'rollback',
'sidereport',
'submit',
'switchlanguage',
'unpublish',
'versions',
'EditForm',
@ -79,17 +78,27 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
public function init() {
parent::init();
// Locale" attribute is either explicitly added by LeftAndMain Javascript logic,
// or implied on a translated record (see {@link Translatable->updateCMSFields()}).
if(Translatable::is_enabled()) {
$this->Lang = $this->requestParams["lang"] ? $this->requestParams["lang"] : Translatable::default_lang();
Translatable::set_reading_lang($this->Lang);
// $Lang serves as a "context" which can be inspected by Translatable - hence it
// has the same name as the database property on Translatable.
if($this->getRequest()->requestVar("Locale")) {
$this->Locale = $this->getRequest()->requestVar("Locale");
} elseif($this->getRequest()->requestVar("locale")) {
$this->Locale = $this->getRequest()->requestVar("locale");
} else {
$this->Locale = Translatable::default_locale();
}
Translatable::set_reading_locale($this->Locale);
}
// collect languages for TinyMCE spellchecker plugin
if(Translatable::is_enabled()) {
$spellcheckLangs = Translatable::get_existing_content_languages();
} else {
$defaultLang = Translatable::default_lang();
$spellcheckLangs = array($defaultLang => i18n::get_language_name($defaultLang));
$defaultLang = Translatable::default_locale();
$spellcheckLangs = array($defaultLang => i18n::get_locale_name($defaultLang));
}
$spellcheckSpec = array();
foreach($spellcheckLangs as $lang => $title) $spellcheckSpec[] = "{$title}={$lang}";
@ -348,9 +357,9 @@ JS;
}
/**
* Get a databsae record to be managed by the CMS
* Get a database record to be managed by the CMS
*/
public function getRecord($id) {
public function getRecord($id) {
$treeClass = $this->stat('tree_class');
@ -453,6 +462,7 @@ JS;
if(!singleton($className)->canCreate()) return Security::permissionFailure($this);
$p = $this->getNewItem("new-$className-$parent".$suffix, false);
$p->Locale = $_REQUEST['Locale'];
$p->write();
return $this->returnItemToUser($p);
@ -464,18 +474,6 @@ JS;
public function getNewItem($id, $setID = true) {
list($dummy, $className, $parentID, $suffix) = array_pad(explode('-',$id),4,null);
if(Translatable::is_enabled()) {
if (!Translatable::is_default_lang()) {
$originalItem = Translatable::get_original($className,Session::get("{$id}_originalLangID"));
if ($setID) $originalItem->ID = $id;
else {
$originalItem->ID = null;
Translatable::creating_from(Session::get($id.'_originalLangID'));
}
return $originalItem;
}
}
$newItem = new $className();
if( !$suffix ) {
@ -1099,14 +1097,18 @@ JS;
foreach( $this->PageTypes() as $arrayData ) {
$pageTypes[$arrayData->getField('ClassName')] = $arrayData->getField('AddAction');
}
return new Form($this, "AddPageOptionsForm", new FieldSet(
$fields = new FieldSet(
new HiddenField("ParentID"),
new HiddenField("Locale", 'Locale', Translatable::current_locale()),
new DropdownField("PageType", "", $pageTypes, 'Page')
),
new FieldSet(
);
$actions = new FieldSet(
new FormAction("addpage", _t('CMSMain.GO',"Go"))
));
);
return new Form($this, "AddPageOptionsForm", $fields, $actions);
}
/**
@ -1236,107 +1238,35 @@ JS;
}
}
/**
* Switch the cms language and reload the site tree
*
*/
function switchlanguage($lang, $donotcreate = null) {
//is it's a clean switch (to an existing language deselect the current page)
if (is_string($lang)) $dontunloadPage = true;
$lang = (is_string($lang) ? $lang : urldecode($this->urlParams['ID']));
if ($lang != Translatable::default_lang()) {
Translatable::set_reading_lang(Translatable::default_lang());
$tree_class = $this->stat('tree_class');
$obj = new $tree_class;
$allIDs = $obj->getDescendantIDList();
$allChildren = $obj->AllChildren();
$classesMap = $allChildren->map('ID','ClassName');
$titlesMap = $allChildren->map();
Translatable::set_reading_lang($lang);
$obj = new $tree_class;
$languageIDs = $obj->getDescendantIDList();
$notcreatedlist = array_diff($allIDs,$languageIDs);
FormResponse::add("$('addpage').getElementsByTagName('button')[0].disabled=true;");
//FormResponse::add("$('Form_AddPageOptionsForm').getElementsByTagName('div')[1].getElementsByTagName('input')[0].disabled=true;");
FormResponse::add("$('Translating_Message').innerHTML = 'Translating mode - ".i18n::get_language_name($lang)."';");
FormResponse::add("Element.removeClassName('Translating_Message','nonTranslating');");
} else {
Translatable::set_reading_lang($lang);
FormResponse::add("$('addpage').getElementsByTagName('button')[0].disabled=false;");
//FormResponse::add("$('Form_AddPageOptionsForm').getElementsByTagName('div')[1].getElementsByTagName('input')[0].disabled=false;");
FormResponse::add("Element.addClassName('Translating_Message','nonTranslating');");
}
$obj = singleton($this->stat('tree_class'));
$obj->markPartialTree();
$siteTree = $obj->getChildrenAsUL("", '
"<li id=\"record-$child->ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" .
"<a href=\"" . Director::link(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" " . (($child->canEdit() || $child->canAddChildren()) ? "" : "class=\"disabled\"") . " title=\"' . _t('LeftAndMain.PAGETYPE') . '".$child->class."\" >" .
(Convert::raw2js($child->TreeTitle())) .
"</a>"
'
,$this, true);
$rootLink = $this->Link() . '0';
$siteTree = "<li id=\"record-0\" class=\"Root nodelete\"><a href=\"$rootLink\">" .
_t('LeftAndMain.SITECONTENT') . "</a>"
. $siteTree . "</li></ul>";
FormResponse::add("$('sitetree').innerHTML ='". ereg_replace("[\n]","\\\n",$siteTree) ."';");
FormResponse::add("SiteTree.applyTo('#sitetree');");
if (isset($notcreatedlist)) {
foreach ($notcreatedlist as $notcreated) {
if ($notcreated == $donotcreate) continue;
$id = "new-{$classesMap[$notcreated]}-0-$notcreated";
Session::set($id . '_originalLangID',$notcreated);
$treeTitle = Convert::raw2js($titlesMap[$notcreated]);
$response = <<<JS
var tree = $('sitetree');
var newNode = tree.createTreeNode("$id", "$treeTitle", "$classesMap[$notcreated] (untranslated)");
addClass(newNode, 'untranslated');
node = tree.getTreeNodeByIdx(0);
node.open();
node.appendTreeNode(newNode);
JS;
FormResponse::add($response);
}
}
if (!isset($dontunloadPage)) FormResponse::add("node = $('sitetree').getTreeNodeByIdx(0); node.selectTreeNode();");
return FormResponse::respond();
}
/**
* Create a new translation from an existing item, switch to this language and reload the tree.
*/
function createtranslation () {
if(!Director::is_ajax()) {
Director::redirectBack();
return;
}
$langCode = $_REQUEST['newlang'];
$langName = i18n::get_language_name($langCode);
$originalLangID = $_REQUEST['ID'];
$langCode = Convert::raw2sql($_REQUEST['newlang']);
$originalLangID = (int)$_REQUEST['ID'];
$record = $this->getRecord($originalLangID);
$temporalID = "new-$record->RecordClassName-$record->ParentID-$originalLangID";
Session::set($temporalID . '_originalLangID',$originalLangID);
$tree = $this->switchlanguage($langCode, $originalLangID);
FormResponse::add(<<<JS
if (Element.hasClassName('LangSelector_holder','onelang')) {
Element.removeClassName('LangSelector_holder','onelang');
$('treepanes').resize();
}
if ($('LangSelector').options['$langCode'] == undefined) {
var option = document.createElement("option");
option.text = '$langName';
option.value = '$langCode';
$('LangSelector').options.add(option);
}
JS
);
FormResponse::add("$('LangSelector').selectValue('$langCode');");
$newrecord = clone $record;
$newrecord->ID = $temporalID;
$this->Locale = $langCode;
Translatable::set_reading_locale($langCode);
// Create a new record in the database - this is different
// to the usual "create page" pattern of storing the record
// in-memory until a "save" is performed by the user, mainly
// to simplify things a bit.
// @todo Allow in-memory creation of translations that don't persist in the database before the user requests it
$translatedRecord = $record->createTranslation($langCode);
return $this->returnItemToUser($newrecord);
$url = sprintf(
"%s/%d/?locale=%s",
$this->Link('show'),
$translatedRecord->ID,
$langCode
);
FormResponse::add(sprintf('window.location.href = "%s";', $url));
return FormResponse::respond();
}
/**
@ -1367,16 +1297,35 @@ JS
}
/**
* Return a dropdown with existing languages
*/
function LangSelector() {
$langs = Translatable::get_existing_content_languages('SiteTree');
return new DropdownField("LangSelector","Language",$langs,Translatable::current_lang());
}
* Returns all languages with languages already used appearing first.
* Called by the SSViewer when rendering the template.
*/
function LangSelector() {
$member = Member::currentUser(); //check to see if the current user can switch langs or not
if(Permission::checkMember($member, 'VIEW_LANGS')) {
$allKey = _t('Form.LANGAOTHER', "Other languages");
$all = i18n::get_common_locales(); //all languages
$used = Translatable::get_existing_content_languages(); //languages currently in use
if( $used && count($used) ) {
foreach($used as $index => $code) {
if(!$code) continue;
$available[$index] = $all[$index];
unset($all[$index]);
}
$langs[ _t('Form.LANGAVAIL', "Available languages") ] = (isset( $available )) ? $available : array();
}
$langs[ _t('Form.LANGAOTHER', "Other languages") ] = $all;
return new GroupedDropdownField('LangSelector', 'Language', $langs, Translatable::current_locale());
}
//user doesn't have permission to switch langs so just show a string displaying current language
return i18n::get_locale_name( Translatable::current_locale() );
}
/**
* Determine if there are more than one languages in our site tree
* Determine if there are more than one languages in our site tree.
*
* @return boolean
*/
function MultipleLanguages() {
$langs = Translatable::get_existing_content_languages('SiteTree');
@ -1384,12 +1333,19 @@ JS
return (count($langs) > 1);
}
/**
* @return boolean
*/
function IsTranslatableEnabled() {
return Translatable::is_enabled();
}
/**
* Get the name of the language that we are translating in
*/
function EditingLang() {
if(!Translatable::is_default_lang()) {
return i18n::get_language_name(Translatable::current_lang());
if(!Translatable::is_default_locale()) {
return i18n::get_locale_name(Translatable::current_locale());
} else {
return false;
}

View File

@ -116,7 +116,7 @@ class LeftAndMain extends Controller {
// set reading lang
if(Translatable::is_enabled() && !Director::is_ajax()) {
Translatable::choose_site_lang(array_keys(Translatable::get_existing_content_languages('SiteTree')));
Translatable::choose_site_locale(array_keys(Translatable::get_existing_content_languages('SiteTree')));
}
// Allow customisation of the access check by a decorator
@ -844,16 +844,17 @@ JS;
}
public function EditForm() {
$id = isset($_REQUEST['ID']) ? $_REQUEST['ID'] : $this->currentPageID();
if(!$id) return false;
if(is_numeric($id)) {
$record = DataObject::get_by_id($this->stat('tree_class'), $id);
if($record && !$record->canView()) return Security::permissionFailure($this);
if(isset($_REQUEST['ID'])) {
$record = DataObject::get_by_id($this->stat('tree_class'), $_REQUEST['ID']);
} else {
$record = $this->CurrentPage();
}
if(!$record) return false;
if($record && !$record->canView()) return Security::permissionFailure($this);
return $this->getEditForm($id);
return $this->getEditForm($record->ID);
}
public function myprofile() {
@ -900,7 +901,12 @@ JS;
public function currentPage() {
$id = $this->currentPageID();
if($id && is_numeric($id)) {
return DataObject::get_by_id($this->stat('tree_class'), $id);
$page = DataObject::get_by_id($this->stat('tree_class'), $id);
if($page && Translatable::is_enabled() && $page->Locale && $page->Locale != Translatable::current_locale()) {
return false;
} else {
return $page;
}
}
}

View File

@ -358,9 +358,9 @@ ul.contextMenu li a:hover {
text-decoration: none;
}
ul.tree li.untranslated a:link,
ul.tree li.untranslated a:hover,
ul.tree li.untranslated a:visited {
ul.tree span.untranslated a:link,
ul.tree span.untranslated a:hover,
ul.tree span.untranslated a:visited {
color: #ccc
}
@ -458,11 +458,5 @@ ul.tree li.untranslated a:visited {
background-color: #EEEEEE;
border-bottom: 1px solid #CCCCCC;
border-top: 1px solid #CCCCCC;
padding: 5px;
}
#LangSelector_holder select {
width: 70%;
}
#LangSelector_holder.onelang {
display: none;
padding: 3px 0 3px 7px;
}

View File

@ -32,6 +32,10 @@ body {
#TreeActions li {
float: left;
}
#TreeActions button.disabled {
color: #aaa;
}
.ajaxActions input,
.actions input,

View File

@ -8,19 +8,14 @@ LangSelectorClass.prototype = {
initialize: function() {
if(this.selectedIndex != 0) {
this.showlangtree();
_TRANSLATING_LANG = this.value;
}
},
onshow: function() {
if(this.value) this.showlangtree();
},
onchange: function(e, val) {
if(this.value != _TRANSLATING_LANG) {
_TRANSLATING_LANG = this.value;
this.showlangtree();
document.location = 'admin/?locale=' + this.value;
}
},
@ -37,17 +32,6 @@ LangSelectorClass.prototype = {
}
this.value = lang;
}
},
showlangtree: function() {
if(this.value) {
$('sitetree').innerHTML='&nbsp;<img src="cms/images/network-save.gif>&nbsp;loading...';
new Ajax.Request('admin/switchlanguage/' + this.value, {
method : 'post',
onSuccess: Ajax.Evaluator,
onFailure : Ajax.Evaluator
});
}
}
};
LangSelectorClass.applyTo('#LangSelector');

View File

@ -300,7 +300,7 @@ CMSRightForm.prototype = {
// used to set language in CMSMain->init()
var lang = $('LangSelector') ? $F('LangSelector') : null;
if(lang) {
url += '&lang='+lang;
url += '&locale='+lang;
}
statusMessage("loading...");
@ -423,6 +423,9 @@ StageLink.prototype = {
} else {
if(this.id != 'viewArchivedSite') this.style.display = 'none';
}
if($('Form_EditForm_Locale')) {
this.href += "&locale=" + $('Form_EditForm_Locale').value;
}
},
onclick : function() {
var w = window.open(this.href, windowName('site'));

View File

@ -1,7 +1,6 @@
Behaviour.register({
'div.inlineformaction input#Form_EditForm_createtranslation': {
onclick: function() {
'input#Form_EditForm_createtranslation': {
onclick: function(e) {
var st = $('sitetree');
var originalID = st.getIdxOf(st.firstSelected());
if(originalID && originalID.substr(0,3) == 'new') {

View File

@ -6,19 +6,17 @@
<% _t('SITECONTENT TITLE','Site Content and Structure',PR_HIGH) %>
</h2>
<div id="sitetree_holder">
<div id="TreeTools">
<ul id="TreeActions">
<li class="action" id="addpage"><button><% _t('CREATE','Create',PR_HIGH) %></button></li>
<li class="action" id="search"><button><% _t('SEARCH','Search',PR_HIGH) %></button></li>
<li class="action" id="batchactions"><button><% _t('BATCHACTIONS','Batch Actions',PR_HIGH) %></button></li>
</ul>
<div style="clear:both;"></div>
<% control AddPageOptionsForm %>
<form class="actionparams" id="$FormName" style="display: none" action="$FormAction">
<% control Fields %>
$FieldHolder
$FieldHolder
<% end_control %>
<div>
<input class="action" type="submit" value="<% _t('GO','Go') %>" />
@ -94,11 +92,15 @@
<div id="SortItems">
<input type="checkbox" id="sortitems" /> <label for="sortitems"><% _t('ENABLEDRAGGING','Allow drag &amp; drop reordering', PR_HIGH) %></label>
</div>
</div>
<div id="sitetree_ul">
$SiteTreeAsUL
<% if IsTranslatableEnabled %>
<div id="LangSelector_holder">
Language: $LangSelector
</div>
<% end_if %>
</div>
<div id="sitetree_ul">
$SiteTreeAsUL
</div>
</div>
<div id="publication_key">
@ -110,9 +112,6 @@
</div>
<div id="LangSelector_holder" <% if MultipleLanguages %><% else %> class="onelang"<% end_if %>>
Language: $LangSelector
</div>
<!--
<div id="search_holder" style="display:none">
<h2>Search</h2>

View File

@ -1,5 +1,3 @@
<span id="Translating_Message" class="translatingMessage <% if EditingLang %><% else %>nonTranslating<% end_if %>"></span>
<div id="form_actions_right" class="ajaxActions">
</div>

View File

@ -72,7 +72,7 @@ class CMSMainTest extends SapphireTest {
foreach($classes as $class) {
$page = new $class();
if($class instanceof TestOnly) continue;
if($page instanceof TestOnly) continue;
$page->Title = "Test $class page";
$page->write();