How to duplicate new page to all other languages programmatically using ContentService?
Hi,
I have been tasked with duplicating newly created 'News' content to all other languages on the site.
The site owner wants to create a new News page in the default language (en-US) and instead of translating the page to all other languages, have the page
and all property values duplicated (yes, in english) to other languages. I'm guessing that they would rather have the pages available in english, rather than not having them at all in other languages... meh!
I'm attempting to do this using the ContentService component, but not having any luck.
Here is the basis of what I've got...
private void ContentService_Saving(Umbraco.Core.Services.IContentService sender, Umbraco.Core.Events.ContentSavingEventArgs e)
{
foreach (IContent content in e.SavedEntities)
{
// If this content is a new page AND is in the default language AND is either a newsArticle or event doctype
// then we want to copy it to other languages.
var isNew = content.HasIdentity == false;
var isDefaultLanguage = e.IsSavingCulture(content, defaultLanguage.IsoCode);
var isNewsOrEventType = (content.ContentType.Alias == "newsArticle" || content.ContentType.Alias == "event");
if (isNew && isDefaultLanguage && isNewsOrEventType && (!e.Cancel))
{
try
{
// How do I duplicate this page and all it's properties to all other languages?
}
catch (Exception ex)
{
e.Cancel = true;
e.Messages.Add(new Umbraco.Core.Events.EventMessage("Saving Cancelled", ex.Message, Umbraco.Core.Events.EventMessageType.Error));
}
}
}
}
Assuming you mean copy the content to a different language tree (if you mean copy the content to another language on the same node .. see the bottom)
Copy to another tree
We do this in the bowels of Translation manager when we create the translation jobs, the thing you might have to first decide on is how you will work out where the other sites are in your tree.
for Translation manager we use Relations for this, so we would find the child relations of the 'news' page and then that would give us the content id's of the news pages in each language (assuming that they have been created with the relation in place (Linked pages package would let you see and manipulate this)
e.g :
var relations = relationService.GetByParentId(newsParentPage.Id);
var newsParents = relations.Where(x => x.ChildId != x.ParentId)
.Select(x => x.ChildId)
.ToArray();
here newsParents would be the "news" parent page on each language.
then from there you can in theory perform a 'copy' in the Umbraco api.
foreach(var parentId in newsParents)
{
contentService.Copy(content.Id, parentId, true, false);
}
(so content.Id) is the content id from your save loop.
Copy to same node diffrent culture
So if you mean copying content to another language on the same content item.
Firstly : don't do this unless you have to!!
Instead think about using the fallback property when getting the content on your site,
That way if the language content is blank, the page will automatically get the English version and you don't need to do anything. Translations if they are done will override the english and everyone will be happy.
* but if you have to *
again Translation manager has a button for this... but ..
it looks something like this :
foreach (var property in content
.Properties
.Where(x => x.PropertyType.VariesByCulture()))
{
var source = property.GetValue(sourceLanguage);
if (source != null)
{
property.SetValue(source, targetLanguage);
}
}
contentService.Save(content);
Finally.
Make sure you have something in there to stop this code running if the page has already been translated or you will just wipe the translations with the english version (which is why fallback is a better route)
Copy to same node different culture is what I need.
I hear what you say about overriding existing translated content, but this should not be an issue in this case because I only want this to happen on newly created pages... i.e. when the editor creates a new page in english, it should also create the same page accross all other languages (with the english content too). The logic for detecting this is already in place and works well (see my original code posted above).
From the editors perspective, when they create a new page in English, they don't want to have to go into every language and re-create the page again (with all properties) as this is a very onerous task for them. They said it would be very helpful if the system could automatically create the page in the other languages and pre-populate the property values i.e. duplicate the English page. If then a translation is needed for a particular page in a particular language, then they will manually do it adhoc.
Let me try your code above and see where I get to with it...
I think this is on the right track, but there is something weird going on. The pages are created in all other languages (together with the property values), but they are all reported as 'Not created' and greyed out. However, when you switch to that language, it then shows as published!
Here is the English (default) version of the parent page....
and here is on of the other language of the same page...
Notice the status of these child pages?
Here is the code I have so far:
private void ContentService_Saving(IContentService sender, Umbraco.Core.Events.ContentSavingEventArgs e)
{
foreach (IContent content in e.SavedEntities)
{
// If this content is a new page AND is in the default language AND is either a newsArticle or event doctype
// then we want to copy it to other languages.
var isNew = content.HasIdentity == false;
var isDefaultLanguage = e.IsSavingCulture(content, defaultLanguage.IsoCode);
var isNewsOrEventType = (content.ContentType.Alias.InvariantEquals("newsArticle") || content.ContentType.Alias.InvariantEquals("event"));
if (isNew && isDefaultLanguage && isNewsOrEventType && (!e.Cancel))
{
try
{
IContentType defaultContentDocType = contentTypeService.Get(content.ContentTypeId);
foreach (ILanguage language in languages.Where(x => x.IsoCode != defaultLanguage.IsoCode).ToList())
{
// get the default page name
var pageTitle = $"{content.Name ?? defaultContentDocType.Name} - {language.IsoCode}";
foreach (var property in content.Properties.Where(x => x.PropertyType.VariesByCulture()))
{
var source = property.GetValue(defaultLanguage.IsoCode);
if (source != null)
{
property.SetValue(source, language.IsoCode);
}
}
var newPage = new Content(pageTitle, content.ParentId, defaultContentDocType, content.Properties, language.IsoCode);
var result = sender.SaveAndPublish(newPage, language.IsoCode, -1, false);
}
}
catch (Exception ex)
{
e.Cancel = true;
e.Messages.Add(new Umbraco.Core.Events.EventMessage("Saving Cancelled", ex.Message, Umbraco.Core.Events.EventMessageType.Error));
}
}
}
}
If I create a page manually and also fill in the property values for another language, then it doesn't show up like this.
I think what is needed is a way to programatically do what occurs when the editor fills in the property values (say in split view) on the model BEFORE saving and publishing... this way, the editor can see and verify that the page will be created (and published) in all variants, the same way we get on this screen...
What are your thoughts on this Kevin? Any reason you can see about why the nodes are reported as 'Not Created'? Could it be something to do with how I am creating the new variant pages in code?
How to duplicate new page to all other languages programmatically using ContentService?
Hi,
I have been tasked with duplicating newly created 'News' content to all other languages on the site.
The site owner wants to create a new News page in the default language (en-US) and instead of translating the page to all other languages, have the page and all property values duplicated (yes, in english) to other languages. I'm guessing that they would rather have the pages available in english, rather than not having them at all in other languages... meh!
I'm attempting to do this using the ContentService component, but not having any luck.
Here is the basis of what I've got...
Anyone know how to do this?
Anyone?
Hi Louis,
Assuming you mean copy the content to a different language tree (if you mean copy the content to another language on the same node .. see the bottom)
Copy to another tree
We do this in the bowels of Translation manager when we create the translation jobs, the thing you might have to first decide on is how you will work out where the other sites are in your tree.
for Translation manager we use Relations for this, so we would find the child relations of the 'news' page and then that would give us the content id's of the news pages in each language (assuming that they have been created with the relation in place (Linked pages package would let you see and manipulate this)
e.g :
here
newsParents
would be the "news" parent page on each language.then from there you can in theory perform a 'copy' in the Umbraco api.
(so content.Id) is the content id from your save loop.
Copy to same node diffrent culture
So if you mean copying content to another language on the same content item.
Firstly : don't do this unless you have to!!
Instead think about using the fallback property when getting the content on your site,
That way if the language content is blank, the page will automatically get the English version and you don't need to do anything. Translations if they are done will override the english and everyone will be happy.
* but if you have to * again Translation manager has a button for this... but ..
it looks something like this :
Finally.
Make sure you have something in there to stop this code running if the page has already been translated or you will just wipe the translations with the english version (which is why fallback is a better route)
Hi Kevin,
Thank you for for replying!
Copy to same node different culture is what I need.
I hear what you say about overriding existing translated content, but this should not be an issue in this case because I only want this to happen on newly created pages... i.e. when the editor creates a new page in english, it should also create the same page accross all other languages (with the english content too). The logic for detecting this is already in place and works well (see my original code posted above).
From the editors perspective, when they create a new page in English, they don't want to have to go into every language and re-create the page again (with all properties) as this is a very onerous task for them. They said it would be very helpful if the system could automatically create the page in the other languages and pre-populate the property values i.e. duplicate the English page. If then a translation is needed for a particular page in a particular language, then they will manually do it adhoc.
Let me try your code above and see where I get to with it...
Thank you again for your response Kev!
Ok, so made some progress....
I think this is on the right track, but there is something weird going on. The pages are created in all other languages (together with the property values), but they are all reported as 'Not created' and greyed out. However, when you switch to that language, it then shows as published!
Here is the English (default) version of the parent page....
and here is on of the other language of the same page...
Notice the status of these child pages?
Here is the code I have so far:
If I create a page manually and also fill in the property values for another language, then it doesn't show up like this.
I think what is needed is a way to programatically do what occurs when the editor fills in the property values (say in split view) on the model BEFORE saving and publishing... this way, the editor can see and verify that the page will be created (and published) in all variants, the same way we get on this screen...
I found this article that would be a great benefit in achieving this... I wish this functionality was in there today (big hint ;) https://github.com/umbraco/Umbraco-CMS/issues/5551
What are your thoughts on this Kevin? Any reason you can see about why the nodes are reported as 'Not Created'? Could it be something to do with how I am creating the new variant pages in code?
Hi,
I haven't looked in detail but you have
is that last 'false' on the "RaiseEvents" parameter ?
if you don't raise the events i don't think all of the nu caching etc will get triggerd so if that is raise events try it with true.
Yes it is... my thinking is that I don't want to get in a saving loop.
Let me try it out.....
Hi Kevin,
No change with that param set to true...
Still creates all language pages as before.
is working on a reply...