From 74fad6a1d03d1ccd949c9f45cbd76c7a434dc04a Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Thu, 23 Apr 2009 01:46:49 +0000 Subject: [PATCH] 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= 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 --- code/CMSMain.php | 208 +++++++++++----------------- code/LeftAndMain.php | 26 ++-- css/cms_left.css | 14 +- css/layout.css | 4 + javascript/LangSelector.js | 18 +-- javascript/LeftAndMain_right.js | 5 +- javascript/TranslationTab.js | 5 +- templates/Includes/CMSMain_left.ss | 21 ++- templates/Includes/CMSMain_right.ss | 2 - tests/CMSMainTest.php | 2 +- 10 files changed, 124 insertions(+), 181 deletions(-) diff --git a/code/CMSMain.php b/code/CMSMain.php index 84478a60..b20f4009 100644 --- a/code/CMSMain.php +++ b/code/CMSMain.php @@ -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("", ' - "
  • ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" . - "Link(),0,-1), "show", $child->ID) . "\" " . (($child->canEdit() || $child->canAddChildren()) ? "" : "class=\"disabled\"") . " title=\"' . _t('LeftAndMain.PAGETYPE') . '".$child->class."\" >" . - (Convert::raw2js($child->TreeTitle())) . - "" -' - ,$this, true); - $rootLink = $this->Link() . '0'; - $siteTree = "
  • " . - _t('LeftAndMain.SITECONTENT') . "" - . $siteTree . "
  • "; - 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 = <<getRecord($originalLangID); - $temporalID = "new-$record->RecordClassName-$record->ParentID-$originalLangID"; - Session::set($temporalID . '_originalLangID',$originalLangID); - $tree = $this->switchlanguage($langCode, $originalLangID); - FormResponse::add(<<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; } diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index d8d02d5d..5f7807ee 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -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; + } } } diff --git a/css/cms_left.css b/css/cms_left.css index 24baae3f..911e7fab 100644 --- a/css/cms_left.css +++ b/css/cms_left.css @@ -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; } diff --git a/css/layout.css b/css/layout.css index 4decc024..c6832029 100644 --- a/css/layout.css +++ b/css/layout.css @@ -32,6 +32,10 @@ body { #TreeActions li { float: left; } + + #TreeActions button.disabled { + color: #aaa; + } .ajaxActions input, .actions input, diff --git a/javascript/LangSelector.js b/javascript/LangSelector.js index caad1ea5..36353082 100755 --- a/javascript/LangSelector.js +++ b/javascript/LangSelector.js @@ -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=' 
    -
    -
    - +
    <% control AddPageOptionsForm %>
    @@ -110,9 +112,6 @@
    -
    <% else %> class="onelang"<% end_if %>> - Language: $LangSelector -