def reply(self): # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) data = json_body(self.request) id_ = data.get("id", None) if id_ is None: self.request.response.setStatus(400) return dict(error=dict(type="BadRequest", message="Missing content id to link to")) target = self.get_object(id_) if target is None: self.request.response.setStatus(400) return dict(error=dict(type="BadRequest", message="Content does not exist")) target_language = ILanguage(target).get_language() manager = ITranslationManager(self.context) current_translation = manager.get_translation(target_language) if current_translation is not None: self.request.response.setStatus(400) return dict(error=dict( type="BadRequest", message="Already translated into language {}".format( target_language), )) manager.register_translation(target_language, target) self.request.response.setStatus(201) self.request.response.setHeader("Location", self.context.absolute_url()) return {}
def __call__(self): pc = getToolByName(self.context, 'portal_catalog') pl = getToolByName(self.context, 'portal_languages') self.results = [] for language_supported in pl.getSupportedLanguages(): translated_objects = pc.searchResults( object_provides=LP_TRANSLATABLE, Language=language_supported) for brain in translated_objects: obj = brain.getObject() if obj.isCanonical(): translations = obj.getTranslations(include_canonical=False) manager = ITranslationManager(obj) if translations: for language in translations.keys(): try: manager.register_translation( language, translations[language][0]) except KeyError: logger.info( '%s already translated to %s: %s' % (obj.id, language, str(manager.get_translations()))) self.results.append(str(manager.get_translations())) logger.info('Finished with transferring catalog information') return self.template()
def set_translation(self, original_path, translation_path, target_language): """ Set position of container object given by `id` and `position` """ from plone.protect.interfaces import IDisableCSRFProtection from zope.interface import alsoProvides from plone.app.multilingual.interfaces import ITranslationManager alsoProvides(self.request, IDisableCSRFProtection) source_obj = self.context.restrictedTraverse(original_path, None) if source_obj is None: raise ValueError("No object found at {}".format(original_path)) translated_obj = self.context.restrictedTraverse( translation_path, None) if translated_obj is None: raise ValueError("No object found at {}".format(translation_path)) translated_obj.language = target_language translated_obj.reindexObject() manager = ITranslationManager(source_obj) manager.register_translation(translated_obj.language, translated_obj) self.request.response.setStatus(200)
def set_translation_map(self): """ Set position of container object given by `id` and `position` """ from plone.protect.interfaces import IDisableCSRFProtection from zope.interface import alsoProvides from plone.app.multilingual.interfaces import ITranslationManager alsoProvides(self.request, IDisableCSRFProtection) translation_map = json.loads(self.request.BODY) for translations in translation_map: if "en" not in translations or "de" not in translations: continue source_obj = self.context.restrictedTraverse( str(translations["de"]["path"]), None) translated_obj = self.context.restrictedTraverse( str(translations["en"]["path"]), None) if source_obj is not None and translated_obj is not None: print(translations) manager = ITranslationManager(source_obj) try: manager.register_translation(translated_obj.language, translated_obj) except KeyError as e: print(e) self.request.response.setStatus(200)
def reply(self): # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) data = json_body(self.request) id_ = data.get("id", None) if id_ is None: self.request.response.setStatus(400) return dict(error=dict(type="BadRequest", message="Missing content id to link to")) target = self.get_object(id_) if target is None: self.request.response.setStatus(400) return dict(error=dict(type="BadRequest", message="Content does not exist")) elif target.portal_type == "LRF": self.request.response.setStatus(400) return dict(error=dict( type="BadRequest", message= "Language Root Folders can only be linked between each other", )) target_language = ILanguage(target).get_language() manager = ITranslationManager(self.context) current_translation = manager.get_translation(target_language) target_manager = ITranslationManager(target) target_translation = target_manager.get_translation( self.context.language) if current_translation is not None: self.request.response.setStatus(400) return dict(error=dict( type="BadRequest", message="Source already translated into language {}".format( target_language), )) if target_translation is not None: self.request.response.setStatus(400) return dict(error=dict( type="BadRequest", message="Target already translated into language {}".format( target_language), )) manager.register_translation(target_language, target) # We want to leave a log in the transaction that the link has been executed ts = transaction.get() ts.note( f'Linked translation {"/".join(self.context.getPhysicalPath())} ({self.context.language}) -> {"/".join(target.getPhysicalPath())} ({target_language})' ) self.request.response.setStatus(201) self.request.response.setHeader("Location", self.context.absolute_url()) return {}
def link_translations(items): """ Links the translations with the declared items with the form: [(obj1, lang1), (obj2, lang2), ...] assuming that the first element is the 'canonical' (in PAM there is no such thing). """ # Grab the first item object and get its canonical handler canonical = ITranslationManager(items[0][0]) for obj, language in items: if not canonical.has_translation(language): canonical.register_translation(language, obj)
def handle_add(self, action): data, errors = self.extractData() if not errors: content = data['content'] language = data['language'] ILanguage(content).set_language(language) itm = ITranslationManager(self.context) # the 'register_translation'-method takes content OR # UUID as second parameter. We need to use the UUID # here because otherwise the catalog can't be acquired # and the translation index is not updated itm.register_translation(language, IUUID(content)) return self.request.response.redirect(self.context.absolute_url() + '/modify_translations')
def recursively_bind_translation(source, target, language, keep_workflow_states): if ITranslatable.providedBy(source): tm_source = ITranslationManager(source) tm_source.register_translation(language=language, content=target) if keep_workflow_states: try: api.content.transition(target, to_state=api.content.get_state(source)) except WorkflowException: pass if IFolderish.providedBy(source) and IFolderish.providedBy(target): for source_obj_id, source_obj in source.objectItems(): target_obj = target[source_obj_id] recursively_bind_translation(source_obj, target_obj, language, keep_workflow_states)
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: translation_group = IMutableTG(canonical).get() IMutableTG(obj).set(translation_group) manager = ITranslationManager(obj) manager.register_translation(language, obj) except KeyError: # put obj in a separate translation group when registration # fails IMutableTG(obj).set(self.uuid_generator())
def linkTranslations(self): """Links the translations of the default language Folders """ doneSomething = False try: canonical = ITranslationManager(self.folders[self.defaultLanguage]) except TypeError as e: raise TypeError(str(e) + u' Are your folders ITranslatable?') for language in self.languages: if language == self.defaultLanguage: continue if not canonical.has_translation(language): language_folder = self.folders[language] canonical.register_translation(language, language_folder) doneSomething = True if doneSomething: logger.info(u'Translations linked.') return doneSomething
def __iter__(self): for item in self.previous: pathkey = self.pathkey(*item.keys())[0] if not pathkey: yield item continue path = item[pathkey] if isinstance(path, unicode): path = path.encode('ascii') obj = self._traverse(path) if obj is None: yield item continue canonicalkey = self.canonicalkey(*item.keys())[0] translationkey = self.translationkey(*item.keys())[0] if not canonicalkey: yield item continue canonicalpath = item[translationkey] if isinstance(canonicalpath, unicode): canonicalpath = canonicalpath.encode('ascii') language = item['language'] if ITranslatable.providedBy(obj): ILanguage(obj).set_language(language) if item[canonicalkey]: IMutableTG(obj).set(self.uuid_generator()) manager = ITranslationManager(obj) manager.register_translation(language, obj) else: self.deferred.append((path, canonicalpath, language)) yield item self._update_deferred()
def reply(self): data = json_body(self.request) type_ = data.get("@type", None) id_ = data.get("id", None) title = data.get("title", None) translation_of = data.get("translation_of", None) language = data.get("language", None) uid = data.get("UID", None) if not type_: raise BadRequest("Property '@type' is required") # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) sm = getSecurityManager() # ManagePortal is required to set the uid of an object during creation if uid and not sm.checkPermission(ManagePortal, self.context): self.request.response.setStatus(403) msg = "Setting UID of an object requires Manage Portal permission" return dict(error=dict(type="Forbidden", message=msg)) try: obj = create(self.context, type_, id_=id_, title=title) except Unauthorized as exc: self.request.response.setStatus(403) return dict(error=dict(type="Forbidden", message=str(exc))) except BadRequest as exc: self.request.response.setStatus(400) return dict(error=dict(type="Bad Request", message=str(exc))) # Acquisition wrap temporarily to satisfy things like vocabularies # depending on tools temporarily_wrapped = False if IAcquirer.providedBy(obj) and not safe_hasattr(obj, "aq_base"): obj = obj.__of__(self.context) temporarily_wrapped = True # Update fields deserializer = queryMultiAdapter((obj, self.request), IDeserializeFromJson) if deserializer is None: self.request.response.setStatus(501) return dict(error=dict( message=f"Cannot deserialize type {obj.portal_type}")) try: deserializer(validate_all=True, create=True) except DeserializationError as e: self.request.response.setStatus(400) return dict( error=dict(type="DeserializationError", message=str(e))) if temporarily_wrapped: obj = aq_base(obj) if uid: setattr(obj, "_plone.uuid", uid) if not getattr(deserializer, "notifies_create", False): notify(ObjectCreatedEvent(obj)) obj = add(self.context, obj, rename=not bool(id_)) # Link translation given the translation_of property if (IPloneAppMultilingualInstalled.providedBy(self.request) and translation_of and language): source = self.get_object(translation_of) if source: manager = ITranslationManager(source) manager.register_translation(language, obj) self.request.response.setStatus(201) self.request.response.setHeader("Location", obj.absolute_url()) serializer = queryMultiAdapter((obj, self.request), ISerializeToJson) serialized_obj = serializer() # HypermediaBatch can't determine the correct canonical URL for # objects that have just been created via POST - so we make sure # to set it here serialized_obj["@id"] = obj.absolute_url() return serialized_obj
def do_reply(self): """This is the reply method from plone.restapi, overrided to manage returning the fullobject after creation or the summary.""" # imio.restapi, nothing changed until next comment "imio.restapi ..." comment data = json_body(self.request) type_ = data.get("@type", None) id_ = data.get("id", None) title = data.get("title", None) translation_of = data.get("translation_of", None) language = data.get("language", None) if not type_: raise BadRequest("Property '@type' is required") # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) try: obj = create(self.context, type_, id_=id_, title=title) except Unauthorized as exc: self.request.response.setStatus(403) return dict(error=dict(type="Forbidden", message=str(exc))) except BadRequest as exc: self.request.response.setStatus(400) return dict(error=dict(type="Bad Request", message=str(exc))) # Acquisition wrap temporarily to satisfy things like vocabularies # depending on tools temporarily_wrapped = False if IAcquirer.providedBy(obj) and not safe_hasattr(obj, "aq_base"): obj = obj.__of__(self.context) temporarily_wrapped = True # Update fields deserializer = queryMultiAdapter((obj, self.request), IDeserializeFromJson) if deserializer is None: self.request.response.setStatus(501) return dict( error=dict(message="Cannot deserialize type {}".format(obj.portal_type)) ) try: deserializer(validate_all=True, create=True) except DeserializationError as e: self.request.response.setStatus(400) return dict(error=dict(type="DeserializationError", message=str(e))) if temporarily_wrapped: obj = aq_base(obj) if not getattr(deserializer, "notifies_create", False): notify(ObjectCreatedEvent(obj)) obj = add(self.context, obj, rename=not bool(id_)) # Link translation given the translation_of property if PAM_INSTALLED and PLONE5: from plone.app.multilingual.interfaces import ( IPloneAppMultilingualInstalled, ) # noqa from plone.app.multilingual.interfaces import ITranslationManager if ( IPloneAppMultilingualInstalled.providedBy(self.request) and translation_of and language ): source = self.get_object(translation_of) if source: manager = ITranslationManager(source) manager.register_translation(language, obj) self.request.response.setStatus(201) self.request.response.setHeader("Location", obj.absolute_url()) # imio.restapi, begin changes, manage returning full object or summary return_full_object = data.get( "return_fullobject", get_return_fullobject_after_creation_default()) if return_full_object: serializer = queryMultiAdapter((obj, self.request), ISerializeToJson) else: serializer = queryMultiAdapter((obj, self.request), ISerializeToJsonSummary) serialized_obj = serializer() # imio.restapi, end changes, manage returning full object or summary # HypermediaBatch can't determine the correct canonical URL for # objects that have just been created via POST - so we make sure # to set it here serialized_obj["@id"] = obj.absolute_url() return serialized_obj
def update(self, obj, data): ILanguage(obj).set_language(data['language']) IMutableTG(obj).set(data['translation_group_uuid']) manager = ITranslationManager(obj) manager.register_translation(data['language'], obj)
def import_item(self, dirpath, *filenames): __traceback_info__ = dirpath logger.info("Importing item at %s", dirpath) all_info = {} blobs = {} for name in filenames: filepath = os.path.join(dirpath, name) key, ext = os.path.splitext(name) if ext != ".json": logger.info("Found non json file, will use as blob, at %s", filepath) blobs[key] = filepath continue logger.info("Reading %s", filepath) with open(filepath) as myfile: content = json.loads(myfile.read()) all_info[key] = content # Get meta info. We might also get some of this from default.json. # Maybe we need less in meta. meta = all_info["meta"] # if "front-page" in meta.get("path", ""): # import pdb; pdb.set_trace() # See if the object already exists. obj = self.get_object(**meta) if obj is None: # We need to get some parent, either from default["parent"] or meta. path = meta["path"] parent_path = path.rpartition("/")[0] parent_obj = self.get_object(path=parent_path) if parent_obj is None: logger.warning( "Parent object not found, cannot create content for %s", path) return default = all_info["default"] # Code taken from plone.restapi add.py FolderPost.reply. # It would be nice if we could call that method directly, but it does too much. # We would need to split it a bit, especially: # don't get json from the request body, and don't change the response. type_ = default.get("@type", None) id_ = default.get("id", None) title = default.get("title", None) translation_of = default.get("translation_of", None) language = default.get("language", None) # except Unauthorized / BadRequest obj = create(parent_obj, type_, id_=id_, title=title) # Acquisition wrap temporarily to satisfy things like vocabularies # depending on tools temporarily_wrapped = False if IAcquirer.providedBy(obj) and not safe_hasattr(obj, "aq_base"): obj = obj.__of__(self.context) temporarily_wrapped = True deserializer = queryMultiAdapter((obj, self.request), IDeserializeFromJson) if deserializer is None: logger.error("Cannot deserialize type %s", obj.portal_type) return if blobs: for fieldname, path in blobs.items(): if fieldname in default: with open(path, "rb") as myfile: default[fieldname]["data"] = myfile.read() # except DeserializationError as e: deserializer(validate_all=True, data=default, create=True) if temporarily_wrapped: obj = aq_base(obj) if not getattr(deserializer, "notifies_create", False): notify(ObjectCreatedEvent(obj)) obj = add(parent_obj, obj, rename=not bool(id_)) obj_path = "/".join(obj.getPhysicalPath()) logger.info("Created %s at %s", type_, obj_path) # Link translation given the translation_of property if PAM_INSTALLED: # Note: untested. from plone.app.multilingual.interfaces import ( IPloneAppMultilingualInstalled, ) # noqa from plone.app.multilingual.interfaces import ITranslationManager if (IPloneAppMultilingualInstalled.providedBy(self.request) and translation_of and language): source = self.get_object(translation_of) if source: manager = ITranslationManager(source) manager.register_translation(language, obj) # TODO: call other, named deserializers, but they do not work currently anyway. else: obj_path = "/".join(obj.getPhysicalPath()) if is_locked(obj, self.request): # TODO: We could throw an error, but we should probably just unlock. logger.warning("Content is locked: %s", obj_path) logger.info("Updating existing content at %s", obj_path) deserializers = getAdapters((obj, self.request), IDeserializeFromJson) if not deserializers: logger.error("Cannot deserialize type %s", obj.portal_type) return for name, deserializer in deserializers: if not name: name = "default" # XXX This traceback info overrides the previous. # When done in a separate method, it should be fine. # __traceback_info__ = name __traceback_info__ = dirpath, name content = all_info[name] if name == "local_roles": # TODO Fix this in plone.restapi. logger.info( "Ignoring local_roles deserializer for now, as it does not accept a content parameter." ) continue if name == "default" and blobs: for fieldname, path in blobs.items(): if fieldname in content: with open(path, "rb") as myfile: content[fieldname]["data"] = myfile.read() try: deserializer(data=content) except TypeError: # Happens for site root. But I want to fix it there too, if that is acceptable. logger.info( "TypeError, likely because deserializer does not accept data keyword argument: %s", deserializer) # TODO: maybe try / except DeserializationError # Report back that we made an import. return True