diff --git a/README.md b/README.md index 377bdae..c24c7d5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # Translatable module for SilverStripe CMS # -[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-translatable.png)](http://travis-ci.org/silverstripe/silverstripe-translatable) +[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-translatable.png?branch=2.1)](http://travis-ci.org/silverstripe/silverstripe-translatable) ## Introduction ## Allows translation of DataObject and SiteTree records into multiple languages. -Note: This module was originally part of the SilverStripe CMS 2.x codebase. ## Usage diff --git a/code/model/Translatable.php b/code/model/Translatable.php index a5f5194..f9569f3 100755 --- a/code/model/Translatable.php +++ b/code/model/Translatable.php @@ -561,10 +561,10 @@ class Translatable extends DataExtension implements PermissionProvider { if($this->owner && $this->translatableFields === null) { $this->translatableFields = array_merge( array_keys($this->owner->db()), - array_keys($this->owner->has_many()), - array_keys($this->owner->many_many()) + array_keys($this->owner->hasMany()), + array_keys($this->owner->manyMany()) ); - foreach (array_keys($this->owner->has_one()) as $fieldname) { + foreach (array_keys($this->owner->hasOne()) as $fieldname) { $this->translatableFields[] = $fieldname.'ID'; } } @@ -582,6 +582,30 @@ class Translatable extends DataExtension implements PermissionProvider { return $config; } + /** + * Check if a given SQLQuery filters on the Locale field + * + * @param SQLQuery $query + * @return boolean + */ + protected function filtersOnLocale($query) { + foreach($query->getWhere() as $condition) { + // Compat for 3.1/3.2 where syntax + if(is_array($condition)) { + // In >=3.2 each $condition is a single length array('condition' => array('params')) + reset($condition); + $condition = key($condition); + } + + // >=3.2 allows conditions to be expressed as evaluatable objects + if(interface_exists('SQLConditionGroup') && ($condition instanceof SQLConditionGroup)) { + $condition = $condition->conditionSQL($params); + } + + if(preg_match('/("|\'|`)Locale("|\'|`)/', $condition)) return true; + } + } + /** * Changes any SELECT query thats not filtering on an ID * to limit by the current language defined in {@link get_current_locale()}. @@ -666,7 +690,7 @@ class Translatable extends DataExtension implements PermissionProvider { /** * @todo Find more appropriate place to hook into database building */ - function requireDefaultRecords() { + public function requireDefaultRecords() { // @todo This relies on the Locale attribute being on the base data class, and not any subclasses if($this->owner->class != ClassInfo::baseDataClass($this->owner->class)) return false; @@ -699,7 +723,7 @@ class Translatable extends DataExtension implements PermissionProvider { ))->column(); if(!$idsWithoutLocale) return; - if(class_exists('SiteTree') && $this->owner->class == 'SiteTree') { + if(class_exists('SiteTree') && $this->owner->class == 'SiteTree') { foreach(array('Stage', 'Live') as $stage) { foreach($idsWithoutLocale as $id) { $obj = Versioned::get_one_by_stage( @@ -707,7 +731,7 @@ class Translatable extends DataExtension implements PermissionProvider { $stage, sprintf('"SiteTree"."ID" = %d', $id) ); - if(!$obj) continue; + if(!$obj || $obj->ObsoleteClassName) continue; $obj->Locale = Translatable::default_locale(); @@ -724,7 +748,7 @@ class Translatable extends DataExtension implements PermissionProvider { } else { foreach($idsWithoutLocale as $id) { $obj = DataObject::get_by_id($this->owner->class, $id); - if(!$obj) continue; + if(!$obj || $obj->ObsoleteClassName) continue; $obj->Locale = Translatable::default_locale(); $obj->write(); @@ -1446,6 +1470,7 @@ class Translatable extends DataExtension implements PermissionProvider { $newTranslation->ID = 0; $newTranslation->Locale = $locale; + $newTranslation->Version = 0; $originalPage = $this->getTranslation(self::default_locale()); if ($originalPage) { @@ -1458,7 +1483,7 @@ class Translatable extends DataExtension implements PermissionProvider { if(Config::inst()->get('Translatable', 'enforce_global_unique_urls')) { $newTranslation->URLSegment = $urlSegment . '-' . i18n::convert_rfc1766($locale); } - + // hacky way to set an existing translation group in onAfterWrite() $translationGroupID = $this->getTranslationGroup(); $newTranslation->_TranslationGroupID = $translationGroupID ? $translationGroupID : $this->owner->ID; @@ -1810,11 +1835,13 @@ class Translatable extends DataExtension implements PermissionProvider { $IDFilter = ($this->owner->ID) ? "AND \"SiteTree\".\"ID\" <> {$this->owner->ID}" : null; $parentFilter = null; + if (Config::inst()->get('SiteTree', 'nested_urls')) { if($this->owner->ParentID) { $parentFilter = " AND \"SiteTree\".\"ParentID\" = {$this->owner->ParentID}"; } else { $parentFilter = ' AND "SiteTree"."ParentID" = 0'; } + } $existingPage = SiteTree::get() // disable get_one cache, as this otherwise may pick up results from when locale_filter was on diff --git a/docs/en/index.md b/docs/en/index.md index 9712fd3..62398e1 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -128,6 +128,37 @@ See http://www.w3.org/International/articles/language-tags/ for a detailed descr To ensure that your template declares the correct content language, please see [i18n](i18n#declaring_the_content_language_in_html). +### User Permissions + +Permissions to view and create translations are managed through the CMS, based on security groups +defined in the "Security" section (`admin/security`). By default, all CMS users with rights to create and edit pages +can also create translations. This can be restricted by removing the "Translate into all available languages" permission, +and replacing it with language specific permissions. + +You can further restrict viewing and editing rights on a specific language through the "Settings" section (`admin/settings`). +Each language has its own configuration "translation", and you can configure access to groups there. + +Here's an example setup which allows content authors to write only English master content, +while translators can only write German translations, but still see readonly versions of the English master content. + +Group: Administrator + + * Has "Full administrative rights" permission + +Group: Content Author English + + * Has "View language dropdown" permission + * Has "Translate into English" permission + * Is part of "Who can edit pages?" in "Settings" for "English" + * Is part of "Who can create pages?" in "Settings" for "English" + +Group: Translator German + + * Has "View language dropdown" permission + * Has "Translate into German" permission + * Is part of "Who can edit pages?" in "Settings" for "German" + * Is part of "Who can create pages?" in "Settings" for "German" + ### Usage Getting a translation for an existing instance: diff --git a/lang/eo.yml b/lang/eo.yml new file mode 100644 index 0000000..539792f --- /dev/null +++ b/lang/eo.yml @@ -0,0 +1,22 @@ +eo: + CMSMain: + LANGUAGEDROPDOWNLABEL: Lingvo + CMSMain_left: + GO: Ek + Form: + LANGAOTHER: 'Aliaj lingvoj' + LANGAVAIL: 'Disponeblaj lingvoj' + Translatable: + ALLCREATED: 'Kreis ĉiujn permesitajn tradukojn.' + CREATE: 'Krei novan tradukon' + CREATEBUTTON: Krei + EXISTING: 'Ekzistantaj tradukoj' + NEWLANGUAGE: 'Nova lingvo' + NOTICENEWPAGE: 'Bonvole konservu ĉi tiun paĝon antaŭ ol krei tradukon' + TRANSLATEALLPERMISSION: 'Traduki en ĉiujn disponeblajn lingvojn' + TRANSLATEPERMISSION: 'Tradukti je %s' + TRANSLATEVIEWLANGS: 'Vidigi lingvan falliston' + TRANSLATIONS: Tradukoj + Translatable_Transform: + OriginalCheckboxLabel: 'Origina: {value}' + OriginalFieldLabel: 'Origina: {title}' diff --git a/lang/id.yml b/lang/id.yml new file mode 100644 index 0000000..1936b8f --- /dev/null +++ b/lang/id.yml @@ -0,0 +1,14 @@ +id: + CMSMain: + LANGUAGEDROPDOWNLABEL: Bahasa + CMSMain_left: + GO: Lanjut + Form: + LANGAOTHER: 'Bahasa lain' + LANGAVAIL: 'Bahasa yang tersedia' + Translatable: + CREATE: 'Buat terjemahan baru' + CREATEBUTTON: Buat + EXISTING: 'Terjemahan yang ada' + NEWLANGUAGE: 'Bahasa baru' + TRANSLATEPERMISSION: 'Terjemahan %s' diff --git a/lang/nl.yml b/lang/nl.yml index 71cc131..ef108e7 100644 --- a/lang/nl.yml +++ b/lang/nl.yml @@ -5,13 +5,18 @@ nl: GO: Gaan Form: LANGAOTHER: 'Overige talen' + LANGAVAIL: 'Beschikbare talen' Translatable: - ALLCREATED: 'Alle beschikbare vertalingen zijn aangemaakt' + ALLCREATED: 'Alle toegelaten vertalingen zijn aangemaakt. ' CREATE: 'Voeg nieuwe vertaling toe' CREATEBUTTON: Toevoegen EXISTING: 'Bestaande vertalingen' NEWLANGUAGE: 'Nieuwe taal' - NOTICENEWPAGE: 'Sla deze pagina op voordat u een nieuwe vertaling toevoegd' + NOTICENEWPAGE: 'Sla deze pagina op voor u een nieuwe vertaling toevoegt' TRANSLATEALLPERMISSION: 'Vertaal in alle beschikbare talen' TRANSLATEPERMISSION: 'Vertaal %s' + TRANSLATEVIEWLANGS: 'Toon dropdown met talen' TRANSLATIONS: Vertalingen + Translatable_Transform: + OriginalCheckboxLabel: 'Origineel: {value}' + OriginalFieldLabel: 'Originele {title}' diff --git a/lang/pl.yml b/lang/pl.yml new file mode 100644 index 0000000..736abc3 --- /dev/null +++ b/lang/pl.yml @@ -0,0 +1,20 @@ +pl: + CMSMain: + LANGUAGEDROPDOWNLABEL: Język + CMSMain_left: + GO: Przejdź + Form: + LANGAOTHER: 'Inne języki' + LANGAVAIL: 'Dostępne języki' + Translatable: + ALLCREATED: 'Wszystkie dozwolone tłumaczenia zostały stworzone.' + CREATE: 'Stwórz nowe tłumaczenie' + CREATEBUTTON: Stwórz + EXISTING: 'Istniejące tłumaczenia' + NEWLANGUAGE: 'Nowy język' + NOTICENEWPAGE: 'Zapisz tą stronę zanim ją przetłumaczysz' + TRANSLATEPERMISSION: 'Tłumaczenie %s' + TRANSLATIONS: Tłumaczenia + Translatable_Transform: + OriginalCheckboxLabel: 'Oryginalny: {value}' + OriginalFieldLabel: 'Oryginalny: {title}' diff --git a/lang/pl_PL.yml b/lang/pl_PL.yml index 66344a7..064f58f 100644 --- a/lang/pl_PL.yml +++ b/lang/pl_PL.yml @@ -12,7 +12,11 @@ pl_PL: CREATEBUTTON: Stwórz EXISTING: 'Istniejące tłumaczenia' NEWLANGUAGE: 'Nowy język' - NOTICENEWPAGE: 'Zapisz stronę przet stworzeniem tłumaczenia' + NOTICENEWPAGE: 'Zapisz stronę przed stworzeniem tłumaczenia' TRANSLATEALLPERMISSION: 'Przetłumacz na wszystkie dostępne języki' TRANSLATEPERMISSION: 'Tłumacz %s' + TRANSLATEVIEWLANGS: 'Pokaż listę rozwijaną języka' TRANSLATIONS: Tłumaczenia + Translatable_Transform: + OriginalCheckboxLabel: 'Oryginalny: {value}' + OriginalFieldLabel: 'Oryginalny {title}' diff --git a/lang/sr@latin.yml b/lang/sr@latin.yml deleted file mode 100644 index 9e1cafe..0000000 --- a/lang/sr@latin.yml +++ /dev/null @@ -1,22 +0,0 @@ -sr@latin: - CMSMain: - LANGUAGEDROPDOWNLABEL: Jezik - CMSMain_left: - GO: Idi - Form: - LANGAOTHER: 'Drugi jezici' - LANGAVAIL: 'Raspoloživi jezici' - Translatable: - ALLCREATED: 'Svi dozvoljeni prevodi su kreirani.' - CREATE: 'Kreiraj novi prevod' - CREATEBUTTON: Kreiraj - EXISTING: 'Postojeći prevodi' - NEWLANGUAGE: 'Novi jezik' - NOTICENEWPAGE: 'Molimo Vas da pre kreiranja prevoda snimite stranicu' - TRANSLATEALLPERMISSION: 'Prevedi na sve raspoložive jezike' - TRANSLATEPERMISSION: 'Prevedi %s' - TRANSLATEVIEWLANGS: 'Pogledaj padajući meni za izbor jezika' - TRANSLATIONS: Prevodi - Translatable_Transform: - OriginalCheckboxLabel: 'Original: {value}' - OriginalFieldLabel: 'Original {title}'