Logo Search packages:      
Sourcecode: zope-linguaplone version File versions

I18NBaseObject.py

# Plone Solutions AS <info@plonesolutions.com>
# http://www.plonesolutions.com

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

"""
Baseclass for multilingual content.
"""

from Globals import InitializeClass
from AccessControl import ClassSecurityInfo

from Products.Archetypes.public import *
from Products.Archetypes.debug import log
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.CMFCorePermissions import ModifyPortalContent, AddPortalContent, View

from Products.LinguaPlone.config import *

from Acquisition import Implicit
from Acquisition import aq_base, aq_inner, aq_parent
from OFS.ObjectManager import BeforeDeleteException

try:
    from Products.CMFPlone.interfaces.Translatable import ITranslatable
except ImportError:
    from Products.PloneLanguageTool.interfaces import ITranslatable
    
try:
    True
except NameError:
    True=1
    False=0


00048 class I18NBaseObject(Implicit):
    """ Base class for translatable objects """
    __implements__ = (ITranslatable,)

    security = ClassSecurityInfo()

    security.declareProtected(View, 'isTranslation')
00055     def isTranslation(self):
        """Tells whether this object is used in a i18n context"""
        return self.getReferenceImpl(RELATIONSHIP) or self.getBackReferenceImpl(RELATIONSHIP) and self.getLanguage() or False

    security.declareProtected(AddPortalContent, 'addTranslation')
00060     def addTranslation(self, language, *args, **kwargs):
        """Add a translation"""
        parent = aq_parent(aq_inner(self))
        if ITranslatable.isImplementedBy(parent):
            parent = parent.getTranslation(language) or parent
        canonical = self.getCanonical()
        id = canonical.getId()
        if not parent.checkIdAvailable(id):
            id = '%s-%s'%(canonical.getId(), language)
        if kwargs.get('language', None) != language:
            kwargs['language'] = language
        kwargs[KWARGS_TRANSLATION_KEY] = canonical
        parent.invokeFactory(self.portal_type, id, *args, **kwargs)
        o = getattr(parent, id)
        # If there is a custom factory method that doesn't add the
        # translation relationship, make sure it is done now.
        if o.getCanonical() != self:
            o.addReference(canonical, RELATIONSHIP)
        self.invalidateTranslationCache()
        # If this is a folder, move translated subobjects aswell.
        if self.isPrincipiaFolderish:
            moveids = []
            for obj in self.objectValues():
                if ITranslatable.isImplementedBy(obj) and obj.getLanguage() == language:
                    moveids.append(obj.getId())
            if moveids:
                o.manage_pasteObjects(self.manage_cutObjects(moveids))
        o.reindexObject()

    security.declareProtected(ModifyPortalContent, 'removeTranslation')
00090     def removeTranslation(self, language):
        """Remove a translation, pass on to layer"""
        translation = self.getTranslation(language)
        if translation.isCanonical():
            # Update translations to point to this one
            self.setCanonical()
        translationparent = aq_parent(aq_inner(translation))
        translationparent.manage_delObjects([translation.getId(),])
        self.invalidateTranslationCache()

    security.declareProtected(View, 'getTranslation')
00101     def getTranslation(self, language=None):
        """Get a translation, pass on to layer"""
        if language is None:
            # Get currently selected language
            langtool = getToolByName(self, 'portal_languages', None)
            if langtool is not None:
                language = langtool.getPreferredLanguage()
            else:
                return self
        l = self.getTranslations().get(language, None)
        return l and l[0] or l

    security.declareProtected(View, 'getTranslationLanguages')
00114     def getTranslationLanguages(self):
        """Return a list of language codes, pass on to layer"""
        return self.getTranslations().keys()

    security.declareProtected(View, 'getTranslations')
00119     def getTranslations(self):
        """Return a dict of {lang : [object, wf_state]}, pass on to layer"""
        if self.isCanonical():

            if CACHE_TRANSLATIONS and getattr(self, '_v_translations', None):
                return self._v_translations

            result = {}
            wftool = getToolByName(self, 'portal_workflow')
            result[self.getLanguage()] = [self, wftool.getInfoFor(self, 'review_state', None)]
            for obj in self.getBRefs(RELATIONSHIP):
                lang = obj.getLanguage()
                result[lang] = [obj, wftool.getInfoFor(obj, 'review_state')]

            if CACHE_TRANSLATIONS:
                self._v_translations = result

            return result
        else:
            return self.getCanonical().getTranslations()
        
    security.declareProtected(View, 'getNonCanonicalTranslations')
00141     def getNonCanonicalTranslations(self):
        """Return a dict of {lang : [object, wf_state]}"""
        translations = self.getTranslations()
        non_canonical = {}
        for lang in translations.keys():
            if not translations[lang][0].isCanonical():
                non_canonical[lang] = translations[lang]
        return non_canonical

    security.declareProtected(View, 'isCanonical')
00151     def isCanonical(self):
        """Tells whether this is the canonical translation"""
        # This is canonical if there are no translationOf references
        try:
            return not self.getReferenceImpl(RELATIONSHIP) and True or False
        except AttributeError:
            return True

    security.declareProtected(ModifyPortalContent, 'setCanonical')
00160     def setCanonical(self):
        """Sets the canonical attribute"""
        # This requires changing references
        if not self.isCanonical():
            translations = self.getTranslations()
            # deleteReferences deletes all references
            # so we iterate twice to make sure we don't
            # delete new and updated references.
            for obj,wfstate in translations.values():
                obj.deleteReferences(RELATIONSHIP)
            for obj,wfstate in translations.values():
                if obj != self:
                    obj.addReference(self, RELATIONSHIP)
            self.invalidateTranslationCache()

    security.declareProtected(View, 'getCanonicalLanguage')
00176     def getCanonicalLanguage(self):
        """Returns the language code for the canonical language"""
        return self.getCanonical().getLanguage()

    security.declareProtected(View, 'getCanonical')
00181     def getCanonical(self):
        """Returns the canonical translation"""
        if CACHE_TRANSLATIONS and getattr(self, '_v_canonical', None):
            return self._v_canonical
        ret = None

        if self.isCanonical():
            ret = self
        else:
            refs = self.getRefs(RELATIONSHIP)
            ret = refs and refs[0] or None

        if CACHE_TRANSLATIONS:
            self._v_canonical = ret
        return ret
   
    security.declareProtected(View, 'getLanguage')
00198     def getLanguage(self):
        """Returns the language"""
        # Use DC method.
        return self.Language()

    security.declarePrivate('invalidateTranslationCache')
    def invalidateTranslationCache(self):
        if CACHE_TRANSLATIONS:
            if hasattr(self, '_v_canonical'):delattr(self, '_v_canonical')
            if self.isCanonical():
                if hasattr(self, '_v_translations'):delattr(self, '_v_translations')
            else:
                self.getCanonical().invalidateTranslationCache()

    # Proof of concept of notification to translations when the canonical
    # translation changes.
    security.declareProtected(ModifyPortalContent, 'processForm')
00215     def processForm(self, data=1, metadata=0, REQUEST=None, values=None):
        """Process the schema looking for data in the form"""
        self._processForm(data=data, metadata=metadata,
                          REQUEST=REQUEST, values=values)
        if AUTO_NOTIFY_CANONICAL_UPDATE:
            # Notify the translations
            if self.isCanonical():
                self.invalidateTranslations()
        if hasattr(self, '_outOfDate'):
            delattr(self, '_outOfDate')

    security.declarePrivate('invalidateTranslations')
00227     def invalidateTranslations(self):
        """Outdate all translations except the canonical one."""
        translations = self.getNonCanonicalTranslations()
        [translations[lang][0].notifyCanonicalUpdate() for lang in translations.keys()]
        self.invalidateTranslationCache()

    security.declarePrivate('notifyCanonicalUpdate')
    def notifyCanonicalUpdate(self):
        self._outOfDate = 1
    
    security.declareProtected(View, 'outdated')
    def outdated(self):
        return getattr(self, '_outOfDate', 0)

    # Initialize language field
    security.declarePrivate('defaultLanguage')
00243     def defaultLanguage(self):
        """Return the current language"""
        langtool = getToolByName(self, 'portal_languages', None)
        if langtool is not None:
            return langtool.getPreferredLanguage()
        else:
            default = self.getField('language').default
            try:
                properties = getToolByName(self, 'portal_properties')
                return getattr(properties.site_properties, 'default_language', default)
            except AttributeError:
                return default

    security.declarePrivate('manage_beforeDelete')
    def manage_beforeDelete(self, item, container):
        # Called from manage_beforeDelete() of subclasses to 
        # veto deletion of the canonical translation object.
        if CANONICAL_DELETE_PROTECTION:
            if self.isCanonical() and self.getNonCanonicalTranslations():
                raise BeforeDeleteException, 'Please delete translations first.'


InitializeClass(I18NBaseObject)


Generated by  Doxygen 1.6.0   Back to index