Migrating Wiki Pages Remotely – Part 05

Note, this series starts at blogs.msdn.com/dwinter/archive/2008/06/28/migrating-wiki-pages-remotely-part-01.aspx

Once you have gotten the source data in memory, the next step is to get it into the destination server. This is where most folks run into problems. In my exploration, I found a few different approaches—which can work better in different scenarios. If you try the obvious method of adding a new item, you may find some difficulty. Let’s walk through what some of the more obvious ways are and show what happens:

string strBatch = "<Method ID='1' Cmd='New'>" +

    "<Field Name='ID'>New</Field>" +

    "<Field Name='WikiField'><![CDATA[" + modifiedData + "]]></Field>" +

    "</Method>";

if (chkDebugFull.Checked)

{

    Trace.WriteLine("***** " + itemName + " ***** MANUAL COPY *****");

    Trace.WriteLine(strBatch);

}

XmlDocument xmlDoc = new System.Xml.XmlDocument();

System.Xml.XmlElement elBatch = xmlDoc.CreateElement("Batch");

elBatch.SetAttribute("OnError", "Continue");

elBatch.InnerXml = strBatch;

XmlNode ndReturn = s2L.UpdateListItems(txt_SelectedWiki2.Text, elBatch);

if (ndReturn != null)

{

    if (ndReturn.InnerText != "0x00000000")

    {

        Trace.WriteLine("ErrorCode - " + ndReturn.InnerText);

    }

The problem is (and place where most people will find failure until they give up) is that you’ll fall into an error code (the 0x00000000 is a success) the moment you start adding other fields that would be necessary for a wiki. I can successfully create a batch that creates a new Wiki Page using UpdateListItems with the batch method above. However when I start adding other required fields like Title:

string strBatch = "<Method ID='1' Cmd='New'>" +

"<Field Name='ID'>New</Field>" +

"<Field Name='Title'>"+itemName.Split(".".ToCharArray())[0]+"</Field>" +

//"<Field Name='FileLeafRef'>"+itemName+"</Field>" +

"<Field Name='WikiField'><![CDATA[" + modifiedData + "]]></Field>" +

"</Method>";

I would experience nasty errors back from the web service every time. I did some exploring on existing fields on the source location that had the title/etc in them, and found that:

Content Type = won’t set
Title = won’t set
FileRef = won’t set
FileLeafRef = won’t set
LinkFilename = yes it will set
LinkFilenameNoMenu = yes it will set
WikiField = yes it will set

Again though, I was met with a problem—since I couldn’t get the title or any of the base fields containing the title or derivations of the title to set while creating the item—the new Wiki Pages would be something like <ID#>._ for the title/etc. Since the extension effectively was _, it wouldn’t be linked to the Wiki Pages content type or be recognized as an aspx/etc, so the resulting files are useless. I talked with some of the internal folks who were in charge of the codebase and they noted that if I wanted to create a Wiki Page from scratch like this through a web service, I would likely need to implement my own web service. That was out of spec for me. Needless to say, following a series of failures around that code I re-evaluated my initial approach. The first successful method I found of relocating Wiki Pages was using the copy.asmx Web Service instead of lists.asmx. That was actually the first time I played with the copy.asmx—even though I’ve already shown source above using it. It is quite simple to use, but it does pose a few difficulties which I will discuss later. Here is my code for using the copy.asmx to do the actual copy:

if ((txt_SelectedWiki.Text.Length > 0) && (txt_SelectedWiki2.Text.Length > 0))

{

    Server1WS.Lists s1L = new WikiMigrator.Server1WS.Lists();

    s1L.Url = txt_SiteName.Text.Trim().TrimEnd("/".ToCharArray()) + "/_vti_bin/lists.asmx";

    s1L.Credentials = System.Net.CredentialCache.DefaultCredentials;

    Server2WS.Lists s2L = new WikiMigrator.Server2WS.Lists();

    s2L.Url = txt_SiteName2.Text.Trim().TrimEnd("/".ToCharArray()) + "/_vti_bin/lists.asmx";

    s2L.Credentials = System.Net.CredentialCache.DefaultCredentials;

    try

    {

        // Need to provide a large number or we will restrict at the default 100 items if null

        XmlNode ndListItems = s1L.GetListItems(txt_SelectedWiki.Text, null, null, null, txtNumberRows.Text, null, null);

        XmlNode ndListItemDetail = ndListItems.ChildNodes[1];

        foreach (XmlNode item in ndListItemDetail.ChildNodes)

        {

            try

            {

                if (item.Attributes != null)

                {

                    string itemName = item.Attributes["ows_LinkFilename"].Value;

                    Trace.WriteLine("Copying: " + itemName);

                    if (!string.IsNullOrEmpty(itemName))

                    {

                        Server1CopyWS.Copy myCopyService = new WikiMigrator.Server1CopyWS.Copy();

                        myCopyService.Url = txt_SiteName.Text.Trim().TrimEnd("/".ToCharArray()) + "/_vti_bin/copy.asmx";

                        myCopyService.Credentials = System.Net.CredentialCache.DefaultCredentials;

                        Server2CopyWS.Copy myCopyService2 = new WikiMigrator.Server2CopyWS.Copy();

                        myCopyService2.Url = txt_SiteName2.Text.Trim().TrimEnd("/".ToCharArray()) + "/_vti_bin/copy.asmx";

                        myCopyService2.Credentials = System.Net.CredentialCache.DefaultCredentials;

                        string copySource = txt_SiteName.Text + "/" + txt_SelectedWiki.Text + "/" + itemName;

                        string[] copyDest = { txt_SiteName2.Text + "/" + txt_SelectedWiki2.Text + "/" + itemName };

                        WikiMigrator.Server1CopyWS.FieldInformation myFieldInfo = new WikiMigrator.Server1CopyWS.FieldInformation();

                        WikiMigrator.Server1CopyWS.FieldInformation[] myFieldInfoArray = { myFieldInfo };

                        byte[] myByteArray;

                        uint myGetUint = myCopyService.GetItem(copySource, out myFieldInfoArray, out myByteArray);

                        WikiMigrator.Server2CopyWS.FieldInformation[] myFieldInfoArray2 = new WikiMigrator.Server2CopyWS.FieldInformation[myFieldInfoArray.Length];

                        string sourceWikiField = string.Empty;

                        for (int x = 0; x < myFieldInfoArray.Length; x++)

                        {

                            if (myFieldInfoArray[x].InternalName == "WikiField")

                            {

                                sourceWikiField = myFieldInfoArray[x].Value;

                            }

            if (chkDebugFull.Checked)

                            {

                                Trace.WriteLine("SourceProp: " + myFieldInfoArray[x].InternalName + " = " + myFieldInfoArray[x].Value);

                            }

                  WikiMigrator.Server2CopyWS.FieldInformation myFieldInfo2 = new WikiMigrator.Server2CopyWS.FieldInformation();

                            myFieldInfo2.DisplayName = myFieldInfoArray[x].DisplayName;

                            myFieldInfo2.Id = myFieldInfoArray[x].Id;

                            myFieldInfo2.InternalName = myFieldInfoArray[x].InternalName;

                            myFieldInfo2.Type = (Server2CopyWS.FieldType)myFieldInfoArray[x].Type;

                            myFieldInfo2.Value = myFieldInfoArray[x].Value;

                            myFieldInfoArray2[x] = myFieldInfo2;

                        }

                        // This wont work, and you can't cast the FieldInfo[] object between webservices--hence the above loop

                        //myFieldInfoArray.CopyTo(myFieldInfoArray2, 0);

                        // I do some image work here described later in detail

                        // Are we local OM or remote WS?

                        if (!chk_DestLocal.Checked)

                        {

                            WikiMigrator.Server2CopyWS.CopyResult myCopyResult1 = new WikiMigrator.Server2CopyWS.CopyResult();

                            WikiMigrator.Server2CopyWS.CopyResult myCopyResult2 = new WikiMigrator.Server2CopyWS.CopyResult();

                            WikiMigrator.Server2CopyWS.CopyResult[] myCopyResultArray = { myCopyResult1, myCopyResult2 };

                            try

              {

                                uint myCopyUint = myCopyService2.CopyIntoItems(copySource, copyDest, myFieldInfoArray2, myByteArray, out myCopyResultArray);

                                if (myCopyUint == 0)

                                {

                                    int idx = 0;

                                    foreach (WikiMigrator.Server2CopyWS.CopyResult myCopyResult in myCopyResultArray)

                                    {

                                        string opString = (idx + 1).ToString();

                   if (myCopyResultArray[idx].ErrorMessage == null)

                                        {

                                            Trace.WriteLine("Copied to: " + myCopyResultArray[idx].DestinationUrl);

                                            txt_Status.Text += "Copied to: " + myCopyResultArray[idx].DestinationUrl + "\r\n";

                                            txt_Status.Select(txt_Status.Text.Length, 0);

                                       txt_Status.ScrollToCaret();

                                            copySuccess = true;

                                        }

                                        else

                                        {

                              Trace.WriteLine("COPY SERVICE FAILURE--TRY LOCAL DEST INSTEAD");

                                            Trace.WriteLine("ERROR: " + myCopyResultArray[idx].ErrorMessage);

                                            txt_Status.Text += "Copy Operation Failure. Try Local Dest instead. Exception: " + myCopyResultArray[idx].ErrorMessage + "\r\n";

                                            txt_Status.Select(txt_Status.Text.Length, 0);

                                            txt_Status.ScrollToCaret();

                                        }

                                        idx++;

                                    }

                                }

                            }

    // In full code, this catch processes CopyResult

                            catch {}

                        }

                        else

                        { // Local dest

                            copySuccess = manualLocalCopy(sourceWikiField, itemName, myFieldInfoArray2, myByteArray, copyDest, copySuccess);

                        }

                    }

                }

            }

            catch {}

        }

    }

    catch {}

}

 

Part 06:
blogs.msdn.com/dwinter/archive/2008/06/28/migrating-wiki-pages-remotely-part-06.aspx