How to persist changes to content function property values when cloning a page

Vote:
 

Hi all,

I'm quite new to EPiServer, and have just inherited the job of maintaining and developing an existing EPiServer site. I'm using Composer 4 with EPiServer 6 R2.

We have a custom "Add language" tab next to our "Preview" and "Edit" tabs when viewing a page in Edit Mode. On the "Add language" tab the CMS user selects the following:

- A target language (e.g. German, French, Spanish...)
- Whether to process just the current page or all nested pages
- Whether to skip any pages already added for the selected target language
- Whether to publish the resulting page(s) or just save them.

The CMS user then clicks "Go" and the code creates a copy of each selected page. At this point, although the language of the new page is set to (e.g.) German, the text on the new version of each page is still in English. A human translator then edits the new version of the page and enters the German translations.

All of the above is working fine.

I have now been asked to extend the functionality of the "Add language" tab such that it gives the CMS user an additional "Translate?" option. If selected, this causes the code to use Microsoft Translator to automatically generate an initial draft of the translation. (Let's ignore the question of whether this is a good idea. It's not my decision!) I am therefore trying to extend the code such that it will examine all the content functions within each new page version, and translate them if necessary. The translation itself works fine, but I have so far failed to work out how to actually save the content functions such that the translated data is persisted into the database. I have seen this article...

http://labs.dropit.se/blogs/post/2009/03/25/Updating-an-existing-EPiServer-Composer-function-in-code-behind.aspx

...which appears to be relevant, but whatever I do I am still seeing the original (English) versions of the content function properties when I view the newly-saved foreign-language version of each page. I've tried moving my new code around so that the content function properties are modified *before* the page itself is saved, but that didn't help. Using SQL Profiler I can see that I have sometimes been able to get EPiServer to insert the translated values *somewhere* in the database, but presumably they weren't attached to the correct version of the page or content function, as the translated values never appear on the website.

I won't post all the different approaches I've tried to get this to work, but I've included one sample below. If anyone can point me in the right direction, I would be most grateful.

Adrian

    [GuiPlugIn(Description = "", DisplayName = "Add language", Area = PlugInArea.EditPanel, Url = "~/Custom/Codebase/Plugins/MyTranslateTool/MyTranslateTool.ascx")]
    public partial class MyTranslateTool : SystemUserControlBase
    {
        private int _numberOfTotalPages;
        private int _numberOfProcessedPages;

  ...
  //Code omitted for brevity.
  //Basically, when the CMS user clicks on a "Go" button, it calls AddLanguage() below.
  ...
  
        protected void AddLanguage()
        {
            _numberOfProcessedPages = _numberOfTotalPages = 0;
            bool recursive = (c_RecursiveOption.SelectedValue == "1"); //Determines whether to create new-language versions of nested pages, or just the current page.
            bool publish = c_PublishOption.Checked; //Determines whether to publish the new-language page once it's created.
            bool overwrite = !c_SkipOption.Checked; //Determines whether to overwrite a page if it already exists for the selected target language.

            ProcessTree(CurrentPage.PageLink, CurrentPage.LanguageBranch, NewLanguage.SelectedLanguage, recursive, publish, overwrite);
            c_result.Text = "<p>Processed " + _numberOfProcessedPages + " of " + _numberOfTotalPages + " page(s)</p>";
            c_result.Visible = true;
        }

        private void ProcessTree(PageReference startPage, string oldLanguage, string newLanguage, bool recursive, bool publish, bool overwrite)
        {
            if (oldLanguage == newLanguage)
            {
                return;
            }

            PageListDB _pageListDB = new PageListDB();
            IList<PageReference> pagesToProcess;
            if (recursive)
            {
                pagesToProcess = _pageListDB.ListAll(startPage, oldLanguage);
            }
            else
            {
                pagesToProcess = new List<PageReference>(1);
            }

            if (DataFactory.Instance.GetPage(startPage, new LanguageSelector(oldLanguage)) != null)
            {
                pagesToProcess.Add(startPage);
            }

            int[] processedPages = new int[pagesToProcess.Count];

            foreach (PageReference descendant in pagesToProcess)
            {
                try
                {
                    if (ProcessPage(descendant, oldLanguage, newLanguage, publish, overwrite) != PageReference.EmptyReference)
                    {
                        processedPages[_numberOfProcessedPages++] = descendant.ID;
                    }
                }
                catch (Exception e)
                {
                    c_result.Text = "<p>Failed to add language.</p>";
                    c_result.Visible = true;
                    return;
                }
            }
            _numberOfTotalPages = pagesToProcess.Count;
            UpdatePageCache(pagesToProcess);

            int[] pagesToReindex = new int[_numberOfTotalPages];
            processedPages.CopyTo(pagesToReindex, 0);
            ReindexPages(pagesToReindex);
        }

        private PageReference ProcessPage(PageReference startPage, string oldLanguage, string newLanguage, bool publish, bool overwrite)
        {
            PageData startPageData = DataFactory.Instance.GetPage(startPage, LanguageSelector.Fallback(oldLanguage, false));
            PageReference startPageReference = PageDataManager.ResolvePageVersion(startPage, oldLanguage);
          
            PageData destinationPageData = DataFactory.Instance.GetPage(startPage, LanguageSelector.Fallback(newLanguage, false));
            PageReference destinationPageReference;

            if ((destinationPageData == null) || (!destinationPageData.PageLanguages.Contains(newLanguage.ToLower())))
            {
                destinationPageData = DataFactory.Instance.CreateLanguageBranch(startPage, new LanguageSelector(newLanguage));
                destinationPageReference = DataFactory.Instance.Save(destinationPageData, SaveAction.Save, EPiServer.Security.AccessLevel.NoAccess);
            }
            else
            {
                if (!overwrite)
                {
                    // A page already exists in this language, and we should not overwrite it.
                    return PageReference.EmptyReference;
                }

                destinationPageReference = PageDataManager.ResolvePageVersion(destinationPageData.PageLink, newLanguage);
            }

            if ((startPageData != null) && (destinationPageData != null))
            {
                PageDataManager.CopyPageStructAndFunctions(startPageReference, destinationPageReference);
                if (destinationPageData.IsReadOnly)
                {
                    destinationPageData = destinationPageData.CreateWritableClone();
                }

                foreach (PropertyData property in startPageData.Property)
                {
                    if (!property.IsLanguageSpecific || property.IsMetaData || property.IsDynamicProperty)
                    {
                        continue;
                    }
                    destinationPageData[property.Name] = property.Value;
                }

                destinationPageData.PageName = startPageData.PageName;
                destinationPageData.URLSegment = startPageData.URLSegment;
                destinationPageData["PageShortcutType"] = startPageData["PageShortcutType"];
                destinationPageData["PageShortcutLink"] = startPageData["PageShortcutLink"];
                destinationPageData["PageTargetFrame"] = startPageData["PageTargetFrame"];
                destinationPageData["PageExternalURL"] = startPageData["PageExternalURL"];

                destinationPageData["PagePendingPublish"] = startPageData["PagePendingPublish"];
                destinationPageData["PageStartPublish"] = startPageData["PageStartPublish"];
                destinationPageData["PageStopPublish"] = startPageData["PageStopPublish"];
                destinationPageData["PageDelayedPublish"] = startPageData["PageDelayedPublish"];


                destinationPageReference = DataFactory.Instance.Save(destinationPageData, SaveAction.Save, EPiServer.Security.AccessLevel.NoAccess);
                if (publish)
                {
                    var publishedMasterLanguageVersion = PageVersion.LoadPublishedVersion(destinationPageReference, destinationPageData.MasterLanguageBranch);
                    if (publishedMasterLanguageVersion != null || destinationPageData.IsMasterLanguageBranch)
                    {
                        destinationPageReference = DataFactory.Instance.Save(destinationPageData, SaveAction.Publish, EPiServer.Security.AccessLevel.NoAccess);
                    }
                }

    //START OF MY NEW CODE//
    //Here I want to loop through all the content functions in the page (including any nested content functions)
    //and translate various property values into the new language.
                var allInnerContentFunctions = Dropit.Extension.Global.GetAllInnerContentFunctions(destinationPageData.PageLink);
                foreach (var contentFunction in allInnerContentFunctions)
                {
                    var functionPage = GetPage(contentFunction.ContentFunctionLink);
                    var functionPageClone = functionPage.CreateWritableClone();
                    foreach (var property in functionPageClone.Property)
                    {
                        if (!property.IsLanguageSpecific || property.IsMetaData || property.IsDynamicProperty || !property.DisplayEditUI || property.Name == "ExternalComponent")
                        {
                            continue;
                        }
                        property.Value = DetermineNewPropertyValue(property, oldLanguage, newLanguage);
      //The above line works fine. I have used breakpoints to verify that the correct new value is applied.
                    }

     //Here is the part that is definitely not working.
     //I want to save the modified content function so that the translated property values are visible
     //when I view the new-language version of the page, whether the page is published or just saved
     //(depending on the value of the "publish" parameter).
                    DataFactory.Instance.Save(functionPageClone, SaveAction.Publish | SaveAction.ForceCurrentVersion, AccessLevel.NoAccess);
                }
    //END OF MY NEW CODE//
    
                return destinationPageReference;
            }

            return PageReference.EmptyReference;
        }

        private object DetermineNewPropertyValue(PropertyData p, string oldLanguage, string newLanguage)
        {
            object ret;

   ...
   //Code omitted for brevity.
   //Translates the property value into the new language. This bit works fine.
   ...

            return ret;
        }

        protected static void ReindexPages(int[] pageIDs)
        {
            IndexPageJob indexJob = new IndexPageJob(pageIDs);
            indexJob.Execute();
        }

        protected static void UpdatePageCache(IList<PageReference> pagesToCopy)
        {
            DataFactoryCache.RemovePages(pagesToCopy);
            foreach (PageReference pageLink in pagesToCopy)
            {
                DataFactoryCache.RemoveListing(pageLink);
            }
        }
    }

#61611
Sep 25, 2012 10:40
Vote:
 

Adrian, have you managed to make it work? I'm trying to implement same page translation and experiencing problems. Any information will be useful. Thanks

#64819
Jan 10, 2013 16:11
Vote:
 

Flipby,

Sorry, I made no further progress on this. There seems to be very little documentation for Composer, and I couldn't find anything relevant to my problem. In the absence of any replies to my question, I had to tell my customer that I was unable to get EPiServer to do what they wanted. It's possible that they might ask me to try again sometime in the future, in which case I'll update this thread if I find out anything. Similarly, if you find out anything yourself, I'd be most grateful if you post it here. Good luck!

#64821
Jan 10, 2013 16:37
Vote:
 

Adrian, i finally made it work as we want :)

#65774
Edited, Feb 08, 2013 11:44
Vote:
 

That's great! Are you able to post some sample code or any tips? If it's easier to email, I'm adrian[dot]stovold[at]rbi[dot]co[dot]uk

#65775
Feb 08, 2013 12:02
Vote:
 

I'll post it here, maybe someone else will also need such information

How i work with pages: User selects page which he wants to translate and selects target language. After that, page contents are collected into xml file and translated on translation server. Then xml is returned to EPiServer and new page version is created on target language branch.

I will describe only main steps:

1. Each area is a page. To get PageData for each area, i do:

List<ContentFunctionData> cfd = Dropit.Extension.Global.GetAllInnerContentFunctions(pageReference);
foreach (ContentFunctionData adata in cfd)
{
ContentFunctionReference cfr = new ContentFunctionReference(adata.FunctionID, adata.WorkID);
PageData pdCurrent = EPiServer.DataFactory.Instance.GetPage(cfr);

With contents i also save pageLink of each area to have access to it later

2. When creating target page, it's important to save target page areas before creating/saving target page itself

So, i start with areas:

PageData newPage = Dropit.Extension.Controllers.PageDataManager.ResolvePageVersion(areaPageLink)

newPage = newPage.CreateWritableClone();

...

pr = EPiServer.DataFactory.Instance.Save(newPage, EPiServer.DataAccess.SaveAction.Publish, EPiServer.Security.AccessLevel.NoAccess);

After that i update properties on target page and save it:

PageReference pr = EPiServer.DataFactory.Instance.Save(pagecolwrite, EPiServer.DataAccess.SaveAction.Publish, EPiServer.Security.AccessLevel.NoAccess);
Dropit.Extension.Controllers.PageDataManager.CreateLanguageBranch(PageReference.Parse(originalID),sourceLanguage, pr, targetLanguage, true);

Looks like that`s all. If it still dont work for you, ping me, i will check what i could miss

#65777
Feb 08, 2013 12:33
Vote:
 

Many thanks. It might be a while before I get to try this again myself, but I'm very grateful to you for taking the time to share your solution. All the best.

#65779
Feb 08, 2013 13:18
Vote:
 

Your sample code was starting point for my implementation, so thanks to you also :)

#65780
Feb 08, 2013 13:27
This thread is locked and should be used for reference only. Please use the Legacy add-ons forum to open new discussions.