def update(self): self.portal_state = getMultiAdapter((self.context, self.request), name=u'plone_portal_state') self.site_url = self.portal_state.portal_url() ls = LanguageSelector(self.context, self.request, self.view, self.manager) ls.update() results = ls.languages() translatable = ITranslatable(self.context, None) if translatable is not None: translations = translatable.getTranslations() else: translations = [] for data in results: data['translated'] = data['code'] in translations if data['translated']: trans = translations[data['code']][0] state = getMultiAdapter((trans, self.request), name='plone_context_state') data['url'] = state.view_url() + '?set_language=' + data['code'] else: state = getMultiAdapter((self.context, self.request), name='plone_context_state') try: data['url'] = state.view_url() + '?set_language=' + data['code'] except AttributeError: data['url'] = self.context.absolute_url() + '?set_language=' + data['code']
def can_translate(context, language): """ Check if required parent translations are in place so that we can translate this item :return: True if the item can be translated """ parent = aq_parent(context) if ISiteRoot.providedBy(parent): return True # Parent is a language base folder at plone site root if INavigationRoot.providedBy(parent): return True if ITranslatable.providedBy(parent): translatable = ITranslatable(parent) else: from logging import getLogger log = getLogger("silvuple.views.can_translate") log.info("Parent is not translatable: %s" % parent.absolute_url()) return False translation = translatable.getTranslation(language) return translation is not None
def findLocationForTranslation(self, language): parent = aq_parent(aq_inner(self.context)) trans_parent = ITranslatable(parent, None) if trans_parent is None: return parent return trans_parent.getTranslation(language) or parent
def languages(self): current_language = unicode(getMultiAdapter((self.context, self.request), name='plone_portal_state').language()) languages = LanguageSelector.languages(self) translatable = ITranslatable(self.context, None) if translatable is not None: translations = translatable.getTranslations() else: translations = [] results = [] for data in languages: data['translated'] = data['code'] in translations if data['code'] == current_language or not data['translated']: continue if data['translated']: trans = translations[data['code']][0] state = getMultiAdapter((trans, self.request), name='plone_context_state') data['url'] = state.view_url() + '?set_language=' + data['code'] else: state = getMultiAdapter((self.context, self.request), name='plone_context_state') try: data['url'] = state.view_url() + '?set_language=' + data['code'] except AttributeError: data['url'] = self.context.absolute_url() + '?set_language=' + data['code'] results.append(data) return results
def get_additional_translations(self): """ Return translations for the current context which aren't available from the language selection drop down {"tr":("Turkish", "http://url",) ...} """ context = self.context portal_langs = [i["code"] for i in self.languages()] translatable = ITranslatable(context, None) if translatable is not None: translations = translatable.getTranslations() else: translations = {} additional_translations = {} portal_state = queryMultiAdapter((context, self.request), name=u'plone_portal_state') lang_names = portal_state.locale().displayNames.languages lang_codes = [i for i in translations.keys() if i not in portal_langs and i != ""] can_edit = getSecurityManager().checkPermission( permissions.ModifyPortalContent, context ) for lang_code in lang_codes: is_published = translations[lang_code][1] == "published" if can_edit or is_published: additional_translations[lang_code] = \ (lang_names[lang_code], translations[lang_code][0].absolute_url(),) return additional_translations
def languages(self): results = LanguageSelector.languages(self) # On the main portal, we want to be able to filter out unwanted # languages needes for subsites oshaview = getMultiAdapter((self.context, self.request), name='oshaview') subsite_path = oshaview.subsiteRootPath() potential_subsite = self.context.restrictedTraverse(subsite_path) # get the potentially present question- and country-id # Due to apache rewriting, we cannot use PATH_INFO, but must contruct the path manually # first, get the URL and snip the SERVER_URL off path = self.request.get('URL')[len(self.request.get('SERVER_URL'))+1:] # then pre-pend VirtualURL components if present elems = [x for x in self.request.get('VirtualRootPhysicalPath', [''])] + path.split('/') # join it to a path virtual_path = '/'.join(elems) # get the context's path context_path = '/'.join(self.context.getPhysicalPath()) # just an extra test, as the following expression should always be true if virtual_path.startswith(context_path): question_path = virtual_path[len(context_path):] else: question_path = '' group_by = 'group_by' in self.request and '&group_by=' + \ self.request.get('group_by') or '' # for translatable content, directly link to the translated objects translatable = ITranslatable(self.context, None) if translatable is not None: translations = translatable.getTranslations() else: translations = [] for data in results: data['translated'] = data['code'] in translations if data['translated']: trans = translations[data['code']][0] state = getMultiAdapter((trans, self.request), name='plone_context_state') data['url'] = state.view_url() + question_path + \ '?set_language=' + data['code'] + group_by else: state = getMultiAdapter((self.context, self.request), name='plone_context_state') try: data['url'] = state.view_url() + '/not_available_lang?set_language=' + data['code'] except AttributeError: data['url'] = self.context.absolute_url() + '/not_available_lang?set_language=' + data['code'] return results
def index_object(self, documentId, obj, treshold=None): """Index the object""" if not ITranslatable.providedBy(obj): if IIndexableObjectWrapper.providedBy(obj): # wrapped object in `plone.indexer` wrapped = getattr(obj, '_IndexableObjectWrapper__object', None) # XXX: the rest can probably go now... # Wrapper doesn't proxy __implements__ if wrapped is None: wrapped = getattr(obj, '_IndexableObjectWrapper__ob', None) # Older CMFPlone if wrapped is None: wrapped = getattr(obj, '_obj', None) if wrapped is None: return 0 obj = wrapped try: language = obj.Language if callable(language): language = language() except AttributeError: return 0 if ITranslatable.providedBy(obj): canonical = obj.getCanonical() # Gracefully deal with broken references if canonical is None: return 0 cid = canonical.UID() else: # Also index non-translatable content, otherwise # LinguaPlone only shows translatable content. # This assumes a catalog documentId will never # be equal to a UID. cid = documentId if documentId not in self._unindex: self._length.change(1) else: self._remove(self._unindex[documentId]) main, sub = splitLanguage(language) entry = IndexEntry(documentId, main, sub, cid) self._insert(entry) self._unindex[documentId] = entry self._sortindex[documentId] = str(entry) return 1
def processForm(self, data=1, metadata=0, REQUEST=None, values=None): """Process the schema looking for data in the form.""" is_new_object = self.checkCreationFlag() BaseObject.processForm(self, data, metadata, REQUEST, values) if config.AUTO_NOTIFY_CANONICAL_UPDATE: if self.isCanonical(): self.invalidateTranslations() if self._at_rename_after_creation and is_new_object: new_id = self._renameAfterCreation( check_auto_id=not not self.REQUEST.form.get('id')) else: new_id = None if shasattr(self, '_lp_default_page'): delattr(self, '_lp_default_page') language = self.getLanguage() canonical = self.getCanonical() parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): if not parent.hasTranslation(language): parent.addTranslation(language) translation_parent = parent.getTranslation(language) translation_parent.processForm(values=dict(title=self.Title())) translation_parent.setDescription(self.Description()) parent = translation_parent if ISelectableBrowserDefault.providedBy(parent) and new_id: parent.setDefaultPage(new_id) if shasattr(self, '_lp_outdated'): delattr(self, '_lp_outdated')
def _get_trans(self, ob): if not HAVE_LP or not ITranslatable.providedBy(ob): return '' can = ob.getCanonical() if can != ob: return can.UID() return ''
def setLanguage(self, value, **kwargs): """Sets the language code. When changing the language in a translated folder structure, we try to move the content to the existing language tree. """ translation = self.getTranslation(value) if self.hasTranslation(value): if translation == self: return else: raise AlreadyTranslated, translation.absolute_url() self.getField('language').set(self, value, **kwargs) # If we are called during a schema update we should not be deleting # any language relations. req = getattr(self, 'REQUEST', None) if shasattr(req, 'get'): if req.get('SCHEMA_UPDATE', None) is not None: return if not value: self.deleteReferences(config.RELATIONSHIP) parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): new_parent = parent.getTranslation(value) or parent if new_parent != parent: info = parent.manage_cutObjects([self.getId()]) new_parent.manage_pasteObjects(info) self.reindexObject() self.invalidateTranslationCache()
def content(self): """ Returns the content object or None if it does not exist. """ if not self.data.content: return None portal_path = getToolByName(self.context, 'portal_url').getPortalPath() item = self.context.unrestrictedTraverse( str(portal_path + self.data.content), None) if item is None: return None if LINGUAPLONE_SUPPORT: tool = getToolByName(self.context, 'portal_languages', None) if tool is not None and ITranslatable.providedBy(item): lang = tool.getLanguageBindings()[0] xlate_item = item.getTranslation(lang) if xlate_item is not None: item = xlate_item if not getSecurityManager().checkPermission('View', item): return None return item
def processForm(self, data=1, metadata=0, REQUEST=None, values=None): """Process the schema looking for data in the form.""" is_new_object = self.checkCreationFlag() BaseObject.processForm(self, data, metadata, REQUEST, values) if config.AUTO_NOTIFY_CANONICAL_UPDATE: if self.isCanonical(): self.invalidateTranslations() if self._at_rename_after_creation and is_new_object: new_id = self._renameAfterCreation(check_auto_id=not not self.REQUEST.form.get('id')) else: new_id = None if shasattr(self, '_lp_default_page'): delattr(self, '_lp_default_page') language = self.getLanguage() canonical = self.getCanonical() parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): if not parent.hasTranslation(language): parent.addTranslation(language) translation_parent = parent.getTranslation(language) translation_parent.processForm( values=dict(title=self.Title())) translation_parent.setDescription(self.Description()) parent = translation_parent if ISelectableBrowserDefault.providedBy(parent) and new_id: parent.setDefaultPage(new_id) if shasattr(self, '_lp_outdated'): delattr(self, '_lp_outdated')
def items(self): if not ITranslatable.providedBy(self.context): def plain_context(info): info["has_translation"] = False updater = plain_context else: def translatable_context(info): trans = self.context.getTranslation(info["code"]) if trans is None: info["has_translation"] = False else: info["has_translation"] = True state = getMultiAdapter((trans, self.request), name='plone_context_state') info["url"] = state.view_url() updater = translatable_context for lang in self.languages: updater(lang) return self.languages
def content(self): """ Returns the content object or None if it does not exist. """ if not self.data.content: return None portal_path = getToolByName(self.context, 'portal_url').getPortalPath() item = self.context.unrestrictedTraverse( str(portal_path + self.data.content), None ) if item is None: return None if LINGUAPLONE_SUPPORT: tool = getToolByName(self.context, 'portal_languages', None) if tool is not None and ITranslatable.providedBy(item): lang = tool.getLanguageBindings()[0] xlate_item = item.getTranslation(lang) if xlate_item is not None: item = xlate_item if not getSecurityManager().checkPermission('View', item): return None return item
def find_content_with_wrong_language(content): """log non-default content with different languages than their parents Used to make sure we cleaned up everything. In part stolen and adapted from plone.app.multilingual.browser.migrator.moveContentToProperRLF.findContent """ # only handle portal content from plone.dexterity.interfaces import IDexterityContent from Products.Archetypes.interfaces import IBaseObject from Acquisition import aq_base from Acquisition import aq_parent try: from Products.LinguaPlone.interfaces import ITranslatable except ImportError: from plone.app.multilingual.interfaces import ITranslatable if not IDexterityContent.providedBy(content)\ and not IBaseObject.providedBy(content)\ and not IPloneSiteRoot.providedBy(content): return if hasattr(aq_base(content), 'objectIds'): for id in content.objectIds(): find_content_with_wrong_language(getattr(content, id)) if ITranslatable.providedBy(content): # The content parent has the same language? if not IPloneSiteRoot.providedBy(aq_parent(content)) \ and aq_parent(content).Language() != content.Language(): log.info('Obj %s (%s) not same language as parent (%s)' % (content.absolute_url_path(), content.Language(), aq_parent(content).Language())) # noqa: E501
def find_endpoint(self, obj, lang, types_to_list): if not (IFolder.providedBy(obj) or IPloneSiteRoot.providedBy(obj)): return obj contents = obj.contentIds() # TODO: make list reversable for cid in contents: child = obj[cid] # try to get translation if LinguaPlone is installed, child # is translatable and child language does not match lang if LINGUA_PLONE_INSTALLED: if ITranslatable.providedBy(child): child_lang = child.Language() # child lang can be empty string, only try to # translate if explicit lang if child_lang and child_lang != lang: translation = child.getTranslation(lang) if not translation: continue # ...with next obj in folder child = translation # only traverse to allowed objects allowed = self.permitted(context=child, permission=VIEW_PERMISSION) if not allowed: continue # only traverse to objects listed in typesToList if child.portal_type not in types_to_list: continue # we've found a published object, which can be used as # possible endpoint, except it has 'traverse_view' enabled obj = child if child.defaultView() == "traverse_view": obj = self.find_endpoint(child, lang, types_to_list) break return obj
def test_implementsTranslateable(self): # lingua plone is adding the ITranslatable interface to all types if not HAS_LINGUA_PLONE: return else: from Products.LinguaPlone.interfaces import ITranslatable self.assertTrue(ITranslatable.providedBy(self._ATCT)) self.assertTrue(verifyObject(ITranslatable, self._ATCT))
def _update_deferred(self): for path, canonicalpath, language in self.deferred: obj = self._traverse(path) if obj is None: continue canonical = self._traverse(canonicalpath) if canonical is None: continue if (ITranslatable.providedBy(obj) and ITranslatable.providedBy(canonical)): try: obj.addTranslationReference(canonical) except AlreadyTranslated: from logging import getLogger log = getLogger(__name__) log.info("Object already translated: {}".format(obj.absolute_url()))
def __call__(self, context): self.context = context # return all items in the current folder that are translatable terms = [SimpleTerm(id, title=u'%s (%s)' % (unicode(obj.Title(), 'utf-8'), id)) for id, obj in context.objectItems() if ITranslatable.providedBy(obj)] return SimpleVocabulary(terms)
def addTranslation(self, language, *args, **kwargs): """Adds a translation.""" canonical = self.getCanonical() parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): parent = parent.getTranslation(language) or parent if self.hasTranslation(language): translation = self.getTranslation(language) raise AlreadyTranslated, translation.absolute_url() id = canonical.getId() while not parent.checkIdAvailable(id): id = '%s-%s' % (id, language) kwargs[config.KWARGS_TRANSLATION_KEY] = canonical if kwargs.get('language', None) != language: kwargs['language'] = language o = _createObjectByType(self.portal_type, parent, id, *args, **kwargs) # If there is a custom factory method that doesn't add the # translation relationship, make sure it is done now. if o.getCanonical() != canonical: o.addTranslationReference(canonical) self.invalidateTranslationCache() # Copy over the language independent fields schema = canonical.Schema() independent_fields = schema.filterFields(languageIndependent=True) for field in independent_fields: accessor = field.getEditAccessor(canonical) if not accessor: accessor = field.getAccessor(canonical) data = accessor() translation_mutator = getattr(o, field.translation_mutator) translation_mutator(data) # If this is a folder, move translated subobjects aswell. if self.isPrincipiaFolderish: moveids = [] for obj in self.objectValues(): if ITranslatable.providedBy(obj) and \ obj.getLanguage() == language: lockable = ILockable(obj, None) if lockable is not None and lockable.can_safely_unlock(): lockable.unlock() moveids.append(obj.getId()) if moveids: o.manage_pasteObjects(self.manage_cutObjects(moveids)) o.reindexObject() if isDefaultPage(canonical, self.REQUEST): o._lp_default_page = True
def _translations(self, missing): # Figure out the "closest" translation in the parent chain of the # context. We stop at both an INavigationRoot or an ISiteRoot to look # for translations. # Exceptions: 1) If the object does not implement ITranslatable (= not # LP-aware) or # 2) if the object is set to be neutral # then return this object and don't look for a translation. context = aq_inner(self.context) translations = {} chain = aq_chain(context) first_pass = True _checkPermission = getSecurityManager().checkPermission for item in chain: if ISiteRoot.providedBy(item) \ or not ITranslatable.providedBy(item) \ or not item.Language(): # We have a site root, which works as a fallback has_view_permission = bool(_checkPermission('View', item)) for c in missing: translations[c] = (item, first_pass, has_view_permission) break translatable = ITranslatable(item, None) if translatable is None: continue item_trans = item.getTranslations(review_state=False) for code, trans in item_trans.items(): code = str(code) if code not in translations: # make a link to a translation only if the user # has view permission has_view_permission = bool(_checkPermission('View', trans)) if (not INavigationRoot.providedBy(item) and not has_view_permission): continue # If we don't yet have a translation for this language # add it and mark it as found translations[code] = (trans, first_pass, has_view_permission) missing = missing - set((code, )) if len(missing) <= 0: # We have translations for all break if INavigationRoot.providedBy(item): # Don't break out of the navigation root jail has_view_permission = bool(_checkPermission('View', item)) for c in missing: translations[c] = (item, False, has_view_permission) break first_pass = False # return a dict of language code to tuple. the first tuple element is # the translated object, the second argument indicates wether the # translation is a direct translation of the context or something from # higher up the translation chain return translations
def alternate_i18n(self): """ Return available translations if LinguaPlone is available """ if LINGUA_PLONE and ITranslatable.providedBy(self.context): translations = self.context.getTranslations(review_state=False) if self.context.Language() in translations: del translations[self.context.Language()] return translations else: return []
def __call__(self): obj = self.event.object if hasLinguaPloneInstalled and ITranslatable.providedBy(obj): translations = obj.getTranslations() if len(translations) > 1: for trans, _state in translations.values(): logging.info("*** PURGING VARNISH CACHE: %s", trans.absolute_url()) notify(Purge(trans))
def isDefaultPage(self, obj): default_page = super(DefaultPage, self).getDefaultPage() if obj.getId() == default_page: return True if ITranslatable.providedBy(obj): for translation in obj.getTranslations(review_state=False).values(): if translation.getId() == default_page: return True return False
def _translations(self, missing): # Figure out the "closest" translation in the parent chain of the # context. We stop at both an INavigationRoot or an ISiteRoot to look # for translations. We do want to find something that is definitely # in the language the user asked for. context = aq_inner(self.context) translations = {} chain = aq_chain(context) first_pass = True _checkPermission = getSecurityManager().checkPermission for item in chain: if ISiteRoot.providedBy(item): # We have a site root, which works as a fallback has_view_permission = bool(_checkPermission('View', item)) for c in missing: translations[c] = (item, first_pass, has_view_permission) break translatable = ITranslatable(item, None) if translatable is None: continue item_trans = item.getTranslations(review_state=False) for code, trans in item_trans.items(): code = str(code) if code not in translations: # make a link to a translation only if the user # has view permission has_view_permission = bool(_checkPermission('View', trans)) if (not INavigationRoot.providedBy(item) and not has_view_permission): continue # If we don't yet have a translation for this language # add it and mark it as found translations[code] = (trans, first_pass, has_view_permission) missing = missing - set((code, )) if len(missing) <= 0: # We have translations for all break if INavigationRoot.providedBy(item): # Don't break out of the navigation root jail has_view_permission = bool(_checkPermission('View', item)) for c in missing: translations[c] = (item, False, has_view_permission) break first_pass = False # return a dict of language code to tuple. the first tuple element is # the translated object, the second argument indicates wether the # translation is a direct translation of the context or something from # higher up the translation chain return translations
def conversationCanonicalAdapterFactory(content): # pragma: no cover """Adapter factory to fetch the default conversation from annotations. Will create the conversation if it does not exist. This adapter will fetch and store all comments on the canonical object, so that comments will be shared across all translations. """ if ITranslatable.providedBy(content): canonical = content.getCanonical() if canonical is not None: return conversationAdapterFactory(canonical) return conversationAdapterFactory(content)
def isDefaultPage(self, obj): default_page = super(DefaultPage, self).getDefaultPage() if obj.getId() == default_page: return True if ITranslatable.providedBy(obj): for translation in obj.getTranslations( review_state=False).values(): if translation.getId() == default_page: return True return False
def can_translate(context, language): """ Check if required parent translations are in place so that we can translate this item :return: True if the item can be translated """ parent = context.aq_parent if ISiteRoot.providedBy(parent): return True # Parent is a language base folder at plone site root if INavigationRoot.providedBy(parent): return True if ITranslatable.providedBy(parent): translatable = ITranslatable(parent) else: raise RuntimeError("Not translatable parent: %s" % parent) translation = translatable.getTranslation(language) return translation is not None
def createTranslation(self, container, language, *args, **kwargs): context = aq_inner(self.context) canonical = context.getCanonical() portal_type = self.getTranslationPortalType(container, language) new_id = kwargs.pop( 'id', self.generateId(container, canonical.getId(), language)) kwargs["language"] = language translation = _createObjectByType(portal_type, container, new_id, *args, **kwargs) # If there is a custom factory method that doesn't add the # translation relationship, make sure it is done now. if translation.getCanonical() != canonical: translation.addTranslationReference(canonical) # THIS IS THE LINE WE NEED TO CUSTOMIZE OSHALanguageIndependentFields(canonical).copyFields(translation) if isDefaultPage(aq_parent(aq_inner(canonical)), canonical): translation._lp_default_page = True # If this is a folder, move translated subobjects aswell. if context.isPrincipiaFolderish: moveids = [] for obj in context.values(): translator = ITranslatable(obj, None) if translator is not None \ and translator.getLanguage() == language: lockable = ILockable(obj, None) if lockable is not None and lockable.can_safely_unlock(): lockable.unlock() moveids.append(obj.getId()) if moveids: info = context.manage_cutObjects(moveids) translation.manage_pasteObjects(info) return translation
def processForm(self, data=1, metadata=0, REQUEST=None, values=None): """Process the schema looking for data in the form.""" is_new_object = self.checkCreationFlag() BaseObject.processForm(self, data=data, metadata=metadata, REQUEST=REQUEST, values=values) # LP specific bits if config.AUTO_NOTIFY_CANONICAL_UPDATE: if self.isCanonical(): self.invalidateTranslations() # Check if an explicit id has been passed explicit_id = False if REQUEST is None: REQUEST = getattr(self, 'REQUEST', None) if REQUEST is not None: if 'id' in REQUEST.form and REQUEST.form.get('id'): explicit_id = True if values is not None: if 'id' in values and values.get('id'): explicit_id = True if (is_new_object and not explicit_id and self._at_rename_after_creation): # Renames an object like its normalized title self._renameAfterCreation(check_auto_id=True) if shasattr(self, '_lp_default_page'): delattr(self, '_lp_default_page') language = self.Language() parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent) and parent.Language() != '': if not parent.hasTranslation(language): parent.addTranslation(language) translation_parent = parent.getTranslation(language) translation_parent.processForm(values=dict( title=self.Title())) translation_parent.setDescription(self.Description()) parent = translation_parent if ISelectableBrowserDefault.providedBy(parent): parent.setDefaultPage(self.getId()) if shasattr(self, '_lp_outdated'): delattr(self, '_lp_outdated')
def can_translate(context, language): """ Check if required parent translations are in place so that we can translate this item :return: True if the item can be translated """ assert context is not None parent = aq_parent(context) if ISiteRoot.providedBy(parent): return True # Parent is a language base folder at plone site root if INavigationRoot.providedBy(parent): return True if ITranslatable.providedBy(parent): translatable = ITranslatable(parent) else: from logging import getLogger log = getLogger('silvuple.views.can_translate') if parent: log.info('Parent is not translatable: %s' % parent.absolute_url()) else: log.error( 'Cannot translate language: %s, no parent for %s' % (language, context.absolute_url())) return False translation = translatable.getTranslation(language) return translation is not None
def defaultLanguage(self): """Returns the initial default language.""" parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): language = parent.Language() if language: return parent.Language() language_tool = getToolByName(self, 'portal_languages', None) if language_tool: if language_tool.startNeutral(): return '' else: return language_tool.getPreferredLanguage() else: return LANGUAGE_DEFAULT
def defaultLanguage(self): """Returns the initial default language.""" parent = aq_parent(aq_inner(self)) if getattr(parent, 'portal_type', None) == 'TempFolder': # We have factory tool parent = aq_parent(aq_parent(parent)) if ITranslatable.providedBy(parent): return parent.Language() language_tool = getToolByName(self, 'portal_languages', None) if language_tool: if language_tool.startNeutral(): return '' else: return language_tool.getPreferredLanguage() else: return LANGUAGE_DEFAULT
def processForm(self, data=1, metadata=0, REQUEST=None, values=None): """Process the schema looking for data in the form.""" is_new_object = self.checkCreationFlag() BaseObject.processForm(self, data=data, metadata=metadata, REQUEST=REQUEST, values=values) # LP specific bits if config.AUTO_NOTIFY_CANONICAL_UPDATE: if self.isCanonical(): self.invalidateTranslations() # Check if an explicit id has been passed explicit_id = False if REQUEST is None: REQUEST = getattr(self, 'REQUEST', None) if REQUEST is not None: if 'id' in REQUEST.form and REQUEST.form.get('id'): explicit_id = True if values is not None: if 'id' in values and values.get('id'): explicit_id = True if (is_new_object and not explicit_id and self._at_rename_after_creation): # Renames an object like its normalized title self._renameAfterCreation(check_auto_id=True) if shasattr(self, '_lp_default_page'): delattr(self, '_lp_default_page') language = self.Language() parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent) and parent.Language() != '': if not parent.hasTranslation(language): parent.addTranslation(language) translation_parent = parent.getTranslation(language) translation_parent.processForm( values=dict(title=self.Title())) translation_parent.setDescription(self.Description()) parent = translation_parent if ISelectableBrowserDefault.providedBy(parent): parent.setDefaultPage(self.getId()) if shasattr(self, '_lp_outdated'): delattr(self, '_lp_outdated')
def findContent(self, content, depth): # only handle portal content if not getattr(content, "portal_type", None): logger.warning("SKIP non-portal content %s (%s)" % (content.absolute_url(), content.meta_type)) return if hasattr(aq_base(content), "objectIds") and content.portal_type not in self.blacklist: for id in content.objectIds(): self.findContent(getattr(content, id), depth + 1) while len(self.content_tree) < depth + 1: self.content_tree.append([]) if ITranslatable.providedBy(content): # The content parent has the same language? if ( not IPloneSiteRoot.providedBy(aq_parent(content)) and aq_parent(content).Language() != content.Language() ): self.content_tree[depth].append(content)
def content(self): """ Returns the content object or None if it does not exist. """ if not self.data.content: return None portal_path = getToolByName(self.context, "portal_url").getPortalPath() item = self.context.restrictedTraverse(str(portal_path + self.data.content), None) if LINGUAPLONE_SUPPORT: tool = getToolByName(self.context, "portal_languages", None) if tool is not None and ITranslatable.providedBy(item): lang = tool.getLanguageBindings()[0] item = item.getTranslation(lang) or item return item
def findContent(self, content, depth): # only handle portal content if not IDexterityContent.providedBy(content)\ and not IBaseObject.providedBy(content): logger.warning('SKIP non-portal content %s (%s)' % ( content.absolute_url(), content.meta_type)) return if hasattr(aq_base(content), 'objectIds')\ and aq_base(content).portal_type not in self.blacklist: for id in content.objectIds(): self.findContent(getattr(content, id), depth + 1) while len(self.content_tree) < depth + 1: self.content_tree.append([]) if ITranslatable.providedBy(content): # The content parent has the same language? if not IPloneSiteRoot.providedBy(aq_parent(content)) \ and aq_parent(content).Language() != content.Language(): self.content_tree[depth].append(content)
def getSortedKeys(self, instance=None): """ returns a list of keys sorted accordingly to the selected sort method (may be unsorted if method = no sort) """ sortMethod = self.getSortMethod() context = self if self.isLinguaPloneInstalled(): from Products.LinguaPlone.interfaces import ITranslatable langtool = getToolByName(self, 'portal_languages') if instance and ITranslatable.providedBy(instance): lang = instance.getLanguage() or langtool.getPreferredLanguage( ) else: lang = langtool.getPreferredLanguage() context = context.getTranslation(lang) or self keys = [term.getVocabularyKey() for term in context.contentValues()] if not hasattr(self, 'sortMethod'): # smooth upgrade from previous releases return keys if sortMethod == SORT_METHOD_LEXICO_KEYS: keys.sort() return keys if sortMethod == SORT_METHOD_LEXICO_VALUES: # returns keys sorted by lexicogarphic order of VALUES terms = context.contentValues() terms.sort(lambda x, y: cmp(x.getTermValue(), y.getTermValue())) return [term.getVocabularyKey() for term in terms] if sortMethod == SORT_METHOD_FOLDER_ORDER: try: contentListing = getMultiAdapter((context, context.REQUEST), name=u'folderListing')() except ComponentLookupError: # still Plone 3 compatible contentListing = context.getFolderContents() return [term.getObject().getTermKey() for term in contentListing] # fallback return keys
def testRelocatorOfMisplacedContent(self): self.a_en = makeContent(self.portal, 'Folder', id='a_en') self.a_en.edit(language='en') self.a_1_1_ca = makeContent(self.a_en, 'Folder', id='a_1_1_ca') self.a_1_1_ca.edit(language='ca') self.a_2_1_en = makeContent(self.a_1_1_ca, 'Document', id='a_2_1_en') self.a_2_1_en.edit(language='en') self.a_2_2_ca = makeContent(self.a_1_1_ca, 'Document', id='a_2_2_ca') self.a_2_2_ca.edit(language='ca') self.a_1_2_en = makeContent(self.a_en, 'Folder', id='a_1_2_en') self.a_1_2_en.edit(language='en') self.b_ca = makeContent(self.portal, 'Folder', id='b_ca') self.b_ca.edit(language='ca') self.b_1_1_en = makeContent(self.b_ca, 'Folder', id='b_1_1_en') self.b_1_1_en.edit(language='en') self.b_2_1_en = makeContent(self.b_1_1_en, 'Document', id='b_2_1_en') self.b_2_1_en.edit(language='en') self.b_2_1_ca = makeTranslation(self.b_2_1_en, 'ca') transaction.commit() self.b_2_1_ca.edit(id='b_2_1_ca', language='ca') relocator_view = getMultiAdapter((self.portal, self.request), name='relocate-content') relocator_view.step1andstep2() self.assertTrue(getattr(self.portal, 'b_1_1_en', False)) self.assertTrue(getattr(self.portal, 'a_1_1_ca', False)) self.assertEqual(self.a_en.objectIds(), ['a_1_2_en', 'a_2_1_en']) self.assertEqual(self.a_en.b_1_1_en.objectIds(), ['b_2_1_en']) self.assertEqual(self.b_ca.objectIds(), ['b_2_1_ca']) self.assertEqual(self.b_ca.a_1_1_ca.objectIds(), ['a_2_2_ca']) workflowTool = getToolByName(self.portal, "portal_workflow") workflowTool.setDefaultChain('simple_publication_workflow') setupTool = SetupMultilingualSite() setupTool.setupSite(self.portal) relocator_view.step3() # Test that the resultant Plone site is 'clean' rlfs = [rlf for rlf in self.portal.objectIds() if ITranslatable.providedBy(getattr(self.portal, rlf))] self.assertEqual(rlfs, ['en', 'ca', 'shared'])
def defaultLanguage(self): """Returns the initial default language.""" parent = aq_parent(aq_inner(self)) if ITranslatable.providedBy(parent): language = parent.Language() if language: return parent.Language() oshaiew = self.restrictedTraverse('@@oshaview') site_url = oshaiew.subsiteRootPath() site = self.restrictedTraverse(site_url) language_tool = getToolByName(site, 'portal_languages', None) if language_tool: if language_tool.startNeutral(): return '' else: return language_tool.getPreferredLanguage() else: return LANGUAGE_DEFAULT
def _sync_withids(self, collection, criterion, value, source_value): source_coll = aq_parent(self.context) # Filter out simple dots as in '.././foo' and normalize backslashes norm_value = source_value.replace('\\', '/').split('/') norm_value = '/'.join([n for n in norm_value if n != '.']) try: source_obj = source_coll.unrestrictedTraverse(norm_value) except (AttributeError, KeyError): # The source object couldn't be found, keep the value criterion.setRelativePath(source_value) return language = collection.Language() obj = None if ITranslatable.providedBy(source_obj): obj = source_obj.getTranslation(language) if obj is not None: obj_path = obj.getPhysicalPath() source_path = source_obj.getPhysicalPath() target_value = source_value.split('/') # What follows assumes that the distance between the source # collection and source object is the same as the one in the # translated language ppath = zip(source_path, obj_path) len_ppath = len(ppath) for pos, (s, t) in enumerate(ppath): reverse_pos = pos - len_ppath if s == t: # skip the common root elements continue elif target_value[reverse_pos] == s: # replace the id with the translated id target_value[reverse_pos] = t else: # Mismatching ids covered by a '..' pass target_value = '/'.join(target_value) criterion.setRelativePath(target_value) return # If we cannot figure things out, we set the source criterion.setRelativePath(source_value)
def translated_references(context, language, sources): """Convert the given sources into a list of targets in the given language, should those exist.""" if not sources: return sources if not isinstance(sources, (list, tuple)): sources = [sources] result = [] catalog = getToolByName(context, 'uid_catalog') for source in sources: if not source: continue if isinstance(source, basestring): # if we get a uid, lookup the object brains = catalog(UID=source) new = None for brain in brains: # gracefully deal with multiple objects per uid try: new = brain.getObject() except AttributeError: # pragma: no cover pass if new is not None: source = new break target = source if ITranslatable.providedBy(source): canonical = source.getCanonical() brains = canonical.getTranslationBackReferences() found = [b for b in brains if b.Language == language] if found: target = found[0].sourceUID else: target = canonical.UID() result.append(target) return result
def __call__(self): event = self.event service_to_ping = self.element.service_to_ping obj = self.event.object container = obj.getParentNode() noasync_msg = 'No instance for async operations was defined.' def pingCRSDS(service_to_ping, obj_url, create): """ Ping the CR/SDS service """ if async_service is None: logger.warn("Can't pingCRSDS, plone.app.async not installed!") return options = {} options['service_to_ping'] = service_to_ping options['obj_url'] = self.sanitize_url(obj_url) options['create'] = create queue = async_service.getQueues()[''] try: async_service.queueJobInQueue(queue, ('rdf', ), ping_CRSDS, self.context, options) except ComponentLookupError: logger.info(noasync_msg) def pingCRSDS_backrel(service_to_ping, obj, create): """ Ping backward relations """ back_relations = obj.getBRefs('relatesTo') for rel in back_relations: if rel is not None: obj_url = "%s/@@rdf" % rel.absolute_url() pingCRSDS(service_to_ping, obj_url, create) def pingCRSDS_children(service_to_ping, obj, create): """ Ping all sub-objects """ if obj.portal_type == "Discussion Item": # 22047 skip object if it's of type Discussion Item return for child in obj.objectIds(): child_obj = obj.get(child) if not child_obj: logger.info("Couldn't retrieve child id %s for %s", child, obj.absolute_url()) continue obj_url = "%s/@@rdf" % child_obj.absolute_url() pingCRSDS(service_to_ping, obj_url, create) pingCRSDS_children(service_to_ping, child_obj, create) # When no request the task is called from a async task, see #19830 request = getattr(obj, 'REQUEST', None) # Detect special object used to force acquisition, see #18904 if isinstance(request, str): request = None create = IObjectAddedEvent.providedBy(event) if service_to_ping == "": return if hasVersionsInstalled and IVersionEnhanced.providedBy(obj) \ and request: obj_versions = IGetVersions(obj).versions() else: obj_versions = [obj] async_service = queryUtility(IAsyncService) # If object has translations if hasLinguaPloneInstalled and ITranslatable.providedBy(obj): if obj.isCanonical(): # Ping all translations for trans in obj.getTranslations().items(): if trans[0] != 'en': trans_obj = trans[1][0] obj_url = trans_obj.absolute_url() pingCRSDS(service_to_ping, obj_url, create) else: # Ping only canonical can_obj = obj.getCanonical() obj_url = can_obj.absolute_url() pingCRSDS(service_to_ping, obj_url, create) # If object was deleted if IObjectRemovedEvent.providedBy(event): # Ping backward relations pingCRSDS_backrel(service_to_ping, obj, create) # Ping all sub-objects pingCRSDS_children(service_to_ping, obj, create) # If object was moved/renamed first ping with the old object's URL if IObjectMovedOrRenamedEvent.providedBy(event): obj_url = "%s/%s/@@rdf" % (event.oldParent.absolute_url(), event.oldName) pingCRSDS(service_to_ping, obj_url, False) # then ping with the container of the old object obj_url = "%s/@@rdf" % event.oldParent.absolute_url() pingCRSDS(service_to_ping, obj_url, False) # Ping backward relations pingCRSDS_backrel(service_to_ping, obj, create) # Ping all sub-objects pingCRSDS_children(service_to_ping, obj, create) # Ping each version for obj in obj_versions: obj_url = "%s/@@rdf" % obj.absolute_url() pingCRSDS(service_to_ping, obj_url, create) # If no Aquisition there is no container, see #18904 if container: obj_url = "%s/@@rdf" % container.absolute_url() pingCRSDS(service_to_ping, obj_url, False) return True
def getBreadcrumbs(self, path=None): """Get breadcrumbs""" result = [] portal_state = self.context.restrictedTraverse('@@plone_portal_state') root = getNavigationRootObject(self.context, portal_state.portal()) root_url = root.absolute_url() if path is not None: path = path.replace(root_url, '', 1).strip('/') root = aq_inner(root.restrictedTraverse(path)) relative = aq_inner( self.context).getPhysicalPath()[len(root.getPhysicalPath()):] if path is None: # Add siteroot if IPloneSiteRoot.providedBy(root): icon = self.root_icon else: icon = self.folder_icon result.append({ 'title': translate(MessageFactory('plone')('Home'), context=self.context.REQUEST), 'url': root_url, 'icon': '<img src="%s" width="16" height="16" />' % icon, }) for i in range(len(relative)): now = relative[:i + 1] obj = aq_inner(root.restrictedTraverse(now)) if IFolderish.providedBy(obj): if not now[-1] == 'talkback': result.append({ 'title': obj.title_or_id(), 'url': root_url + '/' + '/'.join(now), 'icon': '<img src="%s" width="16" height="16" />' % self.folder_icon, }) plone_root = IPloneSiteRoot(self.context, portal_state.portal()) plone_root_url = plone_root.absolute_url() root_relative = aq_inner( self.context).getPhysicalPath()[len(plone_root.getPhysicalPath()):] for i in range(len(root_relative)): now = root_relative[:i + 1] obj = aq_inner(plone_root.restrictedTraverse(now)) getUrlForLanguageCode = '' getTitleForLanguageCode = '' if IFolderish.providedBy(obj): if ITranslatable.providedBy(obj): allTranslations = obj.getTranslations().keys() currentLanguage = obj.Language() if (currentLanguage == 'de') and (obj.hasTranslation('en')): getUrlForLanguageCode = obj.getTranslation( 'en').absolute_url() if (currentLanguage == 'en') and (obj.hasTranslation('de')): getUrlForLanguageCode = obj.getTranslation( 'de').absolute_url() if not now[-1] == 'talkback': result.append({ 'getUrlForLanguageCode': getUrlForLanguageCode, 'getTitleForLanguageCode': translate(MessageFactory('linguaplone')( "legend_existing_translations", default="Existing translations"), context=self.context.REQUEST), 'currentLanguage': currentLanguage, 'getParentFolderURL': self.context.aq_inner.getParentNode().absolute_url(), 'getParentFolderTitle': translate(MessageFactory('plone')( 'go_to_parent_url', default="Up one level"), context=self.context.REQUEST), }) return result
def getContentByCanonical(self): """ Gets a list of entries where content is arranged by its canonical language version. Each entry is a dictionary like: [ ... [ { language : "en", available : true, title : "Foobar", canonical : True, url : "http://" }, { language : "fi", available : true, title : "Barbar", canonical : False, ... }, { language : "ru", available : false } ] ] Not available languages won't get any entries. """ settings = self.getSettings() context = aq_inner(self.context) tools = getMultiAdapter((context, self.request), name="plone_tools") portal_catalog = tools.catalog() all_content = [] if ITranslatable.providedBy(context): for lang, item in context.getTranslations( review_state=False).items(): all_content += portal_catalog( Language="all", path='/'.join(item.getPhysicalPath()), portal_type=settings.contentTypes) else: all_content = portal_catalog(Language="all", path='/'.join( context.getPhysicalPath()), portal_type=settings.contentTypes) # List of UUID -> entry data mappings result = OrderedDict() # Create a dictionary entry populated with default languages, # so that languages come always in the same order # E.g. {en:{"available":False}} langs = self.getLanguages() def get_base_entry(): """ Item row for all languages, everything set off by default """ base_entry = OrderedDict() for lang in langs: base_entry[lang] = dict(available=False, language=lang, canTranslate=False) return base_entry def get_or_create_handle(translatable): """ Get or create entry in the result listing. This is a dict of language -> data pairs """ canonical = translatable.getCanonical() uuid = IUUID(canonical, None) if not uuid: raise RuntimeError("Object missing UUID: %s" % context) # Get the existing canonical entry # for the listing, or create a new empty # populated entry otherwise if uuid in result: return result[uuid] else: entry = get_base_entry() result[uuid] = entry return entry def can_translate(context, language): """ Check if required parent translations are in place so that we can translate this item :return: True if the item can be translated """ assert context is not None parent = aq_parent(context) if ISiteRoot.providedBy(parent): return True # Parent is a language base folder at plone site root if INavigationRoot.providedBy(parent): return True if ITranslatable.providedBy(parent): translatable = ITranslatable(parent) else: from logging import getLogger log = getLogger('silvuple.views.can_translate') if parent: log.info('Parent is not translatable: %s' % parent.absolute_url()) else: log.error( 'Cannot translate language: %s, no parent for %s' % (language, context.absolute_url())) return False translation = translatable.getTranslation(language) return translation is not None for brain in all_content: if not self.shouldTranslate(brain): continue try: context = brain.getObject() except Exception as e: logger.error("Could not load brain %s" % brain.getURL()) logger.exception(e) continue if ITranslatable.providedBy(context): translatable = ITranslatable(context) else: from logging import getLogger log = getLogger('silvuple.views.can_translate') log.info('Content is not translatable: %s' % context.absolute_url()) continue try: entry = get_or_create_handle(translatable) except RuntimeError: from logging import getLogger log = getLogger('silvuple.views.getContentByCanonical') log.info('Item has no UUID: %s' & translatable.absolute_url()) continue # Data exported to JSON + context object needed for post-processing data = dict(canonical=translatable.isCanonical(), title=context.title_or_id(), url=context.absolute_url(), lang=map_language_id(context.Language()), available=True, path=context.absolute_url_path(), context=context) entry[map_language_id(context.Language())] = data # Fill in items which can be translated for entry in result.values(): canonical = None # First snatch canonical item for this content for lang in entry.values(): if lang.get("canonical", False): canonical = lang["context"] # Do not leak out to JSON serialization lang["context"] = None if not canonical: logger.warn("No canonical content for %s" % entry) continue # Then populate untranslated languages for lang in entry.values(): if not lang["available"]: lang_code = lang["language"] lang["canTranslate"] = can_translate(canonical, lang_code) if canonical: # Point to LinguaPlone Translate into... form lang["url"] = "%s/@@translate?newlanguage=%s" % ( canonical.absolute_url(), lang_code) else: lang["url"] = "#" # Convert pre content entries to lists, so that we can guarantee # the order of langauges when passing thru JSON result = [entry.values() for entry in result.values()] self.sortContentListing(result) return result
def testMultiValuedReferenceFields(self): english = makeContent(self.folder, 'SimpleType', 'doc') english.setLanguage('en') german = makeTranslation(english, 'de') target = makeContent(self.folder, 'SimpleType', 'target') target.setLanguage('en') target_german = makeTranslation(target, 'de') target2 = makeContent(self.folder, 'SimpleType', 'target2') target2.setLanguage('en') target2_german = makeTranslation(target2, 'de') # untranslated target target3 = makeContent(self.folder, 'SimpleType', 'target3') target3.setLanguage('en') # targets with different language than sources target4 = makeContent(self.folder, 'SimpleType', 'target4') target4.setLanguage('fr') target4_italian = makeTranslation(target4, 'it') # untranslatable target (non-LP aware) target5 = makeContent(self.folder, 'UntranslatableType', 'target5') self.assertEqual(ITranslatable.providedBy(target5), False) # Test single valued english.setReferenceMulti(target.UID()) self.assertEqual(english.getReferenceMulti()[0].UID(), target.UID()) self.assertEqual(german.getReferenceMulti()[0].UID(), target_german.UID()) # Test multi-valued english.setReferenceMulti([target.UID(), target2.UID()]) self.assertEqual(set(english.getReferenceMulti()), set([target, target2])) self.assertEqual(set(german.getReferenceMulti()), set([target_german, target2_german])) # Test multi-valued from tuple english.setReferenceMulti((target.UID(), target2.UID())) self.assertEqual(set(english.getReferenceMulti()), set([target, target2])) self.assertEqual(set(german.getReferenceMulti()), set([target_german, target2_german])) # test reduce references english.setReferenceMulti([target.UID()]) self.assertEqual(len(english.getReferenceMulti()), 1) # test delete references english.setReferenceMulti([]) self.assertEqual(len(english.getReferenceMulti()), 0) # test with untranslated target, german points to only canonical target english.setReferenceMulti([target3.UID()]) self.assertEqual(english.getReferenceMulti()[0].UID(), target3.UID()) self.assertEqual(german.getReferenceMulti()[0].UID(), target3.UID()) # test with an untranslatable target english.setReferenceMulti([target5.UID()]) self.assertEqual(english.getReferenceMulti()[0].UID(), target5.UID()) self.assertEqual(german.getReferenceMulti()[0].UID(), target5.UID()) # test with untranslatable and translatable mixed targets english.setReferenceMulti([target.UID(), target5.UID()]) self.assertEqual(set(english.getReferenceMulti()), set([target, target5])) self.assertEqual(set(german.getReferenceMulti()), set([target_german, target5])) # test remove translatable from the list english.setReferenceMulti([target5.UID()]) self.assertEqual(english.getReferenceMulti()[0].UID(), target5.UID()) self.assertEqual(german.getReferenceMulti()[0].UID(), target5.UID()) # test with different language on targets, "fr" is canonical english.setReferenceMulti([target4.UID()]) self.assertEqual(english.getReferenceMulti()[0].UID(), target4.UID()) self.assertEqual(german.getReferenceMulti()[0].UID(), target4.UID()) # after adding an italian content, it must point to italian target italian = makeTranslation(english, 'it') self.assertEqual(italian.getReferenceMulti()[0].UID(), target4_italian.UID()) # test edge cases, can we use None to delete references? english.setReferenceMulti(None) # can we delete things via an empty string? english.setReferenceMulti([target2]) english.setReferenceMulti('') self.failUnless(len(english.getReferenceMulti()) == 0) english.setReferenceMulti([target2]) english.setReferenceMulti(['']) self.failUnless(len(english.getReferenceMulti()) == 0) # can we use a content object instead of its UID? english.setReferenceMulti([target2]) self.assertEqual(english.getReferenceMulti()[0].UID(), target2.UID()) self.assertEqual( german.getReferenceMulti()[0].UID(), target2_german.UID())
def mutator(value, **kw): if (not ITranslatable.providedBy(instance) or not self.languageIndependent): return self.getTranslationMutator(instance)(value, **kw) # Use the generatedMutator from LinguaPlone return generatedMutator(instance, value, **kw)
def isTranslatable(content): """Detects translatable objects when LP installed""" # FIXME: Should we check that LP is installed in this site too ? if HAS_LINGUAPLONE: return ITranslatable.providedBy(content)