Export a SharePoint 2007 wiki to the filesystem

Posted: December 20, 2010 in 2007, SharePoint
Tags: , ,

*Updated* – A codeplex project has been created for this. http://sp2007wikiexport.codeplex.com/

I recently needed to move a SharePoint 2007 wiki into a SharePoint 2010 wiki library. After some digging, I found no easy way to do this.

So I’ve decided to write a couple client apps – one that can export the 2007 wiki to the filesystem, then another to import the files into the 2010 wiki library (I figure the import app might also come in handy in the future).

Here is the UI  –

The result is a folder of html/aspx/(or whatever extension you provide) files that can be parsed further in some other process.

Some code reference from these sites were used.
http://sqlblogcasts.com/blogs/drjohn/archive/2007/11/02/Getting-a-list-of-files-from-a-moss-document-library-using-a-SharePoint-web-service.aspx

http://blogs.msdn.com/b/arpans/archive/2007/07/24/sharepoint-web-service-example-grabbing-wiki-content.aspx

Here’s the majority of the code. When I have time, I will create a codeplex project and clean this up a bit…


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.IO;

namespace MOSS2007_Wiki_Export
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
string siteUrl;
string documentLibraryName;
string rowLimit = “10000”;
string wikiBody;
string wikiPageName;
string htmlBody;
string extention;
string beforeText;
string afterText;

public MainWindow()
{
InitializeComponent();
}

private void btnGo_Click(object sender, RoutedEventArgs e)
{
if(ParametersSet())
DoIt();

}

private bool ParametersSet()
{
siteUrl = txtURL.Text;
documentLibraryName = txtLibraryName.Text;
int limit;
if (int.TryParse(txtRowLimit.Text, out limit))
{
rowLimit = limit.ToString();
}
extention = cmbExt.SelectedValue.ToString();
beforeText = txtBefore.Text;
afterText = txtAfter.Text;
if (!DestinationFolderSet())
{
txtOutput.Text += “Please set the destination folder.”;
return false;
}
return true;
}

private bool DestinationFolderSet()
{
if (string.IsNullOrEmpty(txtDestination.Text))
return false;
return true;
}

private void DoIt()
{
StringBuilder updates = new StringBuilder();

SharePointLists.Lists wsList = new SharePointLists.Lists();
wsList.Credentials = System.Net.CredentialCache.DefaultCredentials;
wsList.Url = siteUrl + @”/_vti_bin/lists.asmx”;

// get a list of all top level lists
XmlNode allLists = wsList.GetListCollection();
// load into an XML document so we can use XPath to query content
XmlDocument allListsDoc = new XmlDocument();
allListsDoc.LoadXml(allLists.OuterXml);
// allListsDoc.Save(@”c:\allListsDoc.xml”); // for debug
XmlNamespaceManager ns = new XmlNamespaceManager(allListsDoc.NameTable);
ns.AddNamespace(“d”, allLists.NamespaceURI);

// now get the GUID of the document library we are looking for
XmlNode dlNode = allListsDoc.SelectSingleNode(“/d:Lists/d:List[@Title='” + documentLibraryName + “‘]”, ns);
if (dlNode == null)
{
updates.AppendLine(“Document Library ” + documentLibraryName + ” not found!”);
txtOutput.Text = updates.ToString();
}
else
{
// obtain the GUID for the document library and the webID
string documentLibraryGUID = dlNode.Attributes[“ID”].Value;
string webId = dlNode.Attributes[“WebId”].Value;

updates.AppendLine(“Opening folder ” + documentLibraryName + ” GUID=” + documentLibraryGUID);
txtOutput.Text = updates.ToString();

// create ViewFields CAML
XmlDocument viewFieldsDoc = new XmlDocument();
XmlNode ViewFields = AddXmlElement(viewFieldsDoc, “ViewFields”, “”);
AddFieldRef(ViewFields, “GUID”);
AddFieldRef(ViewFields, “ContentType”);
AddFieldRef(ViewFields, “BaseName”);
AddFieldRef(ViewFields, “Modified”);
AddFieldRef(ViewFields, “EncodedAbsUrl”);
AddFieldRef(ViewFields, “WikiField”);
AddFieldRef(ViewFields, “LinkFilename”);
//viewFieldsDoc.Save(@”c:\viewFields.xml”); // for debug

// create QueryOptions CAML
XmlDocument queryOptionsDoc = new XmlDocument();
XmlNode QueryOptions = AddXmlElement(queryOptionsDoc, “QueryOptions”, “”);
AddXmlElement(QueryOptions, “Folder”, documentLibraryName);
AddXmlElement(QueryOptions, “IncludeMandatoryColumns”, “FALSE”);
// this element is the key to getting the full recusive list
XmlNode node = AddXmlElement(QueryOptions, “ViewAttributes”, “”);
AddXmlAttribute(node, “Scope”, “Recursive”);
queryOptionsDoc.Save(@”c:\queryOptions.xml”); // for debug

//Create Query CAML
XmlDocument queryDoc = new XmlDocument();
XmlNode Query = AddXmlElement(queryDoc, “Query”, “”);

// obtain the list of items in the document library
XmlNode listContent = wsList.GetListItems(documentLibraryGUID, null, Query, ViewFields, rowLimit, QueryOptions, webId);

XmlDocument xmlResultsDoc = new XmlDocument();
xmlResultsDoc.LoadXml(listContent.OuterXml);
ns = new XmlNamespaceManager(xmlResultsDoc.NameTable);
ns.AddNamespace(“z”, “#RowsetSchema”);
// xmlResultsDoc.Save(@”c:\listContent.xml”); // for debug

XmlNodeList rows = xmlResultsDoc.SelectNodes(“//z:row”, ns);
if (rows.Count == 0)
{
updates.AppendLine(“No content found”);
txtOutput.Text = updates.ToString();

}
foreach (XmlNode row in rows)
{
htmlBody = beforeText;

wikiPageName = txtDestination.Text + “\\” + row.Attributes.GetNamedItem(“ows_LinkFilename”).Value.Replace(“.aspx”, “.html”);
wikiBody = row.Attributes.GetNamedItem(“ows_WikiField”).Value;

htmlBody += wikiBody;
htmlBody += afterText;
WriteFile(wikiPageName, wikiBody);

}
}
updates.AppendLine(“Done”);
txtOutput.Text = updates.ToString();
}

private void WriteFile(string name, string body)
{
StreamWriter sw = new StreamWriter(name);
sw.WriteLine(htmlBody);
sw.Close();
}

public static XmlNode AddXmlElement(XmlNode parent, string elementName, string elementValue)
{
XmlNode element = parent.AppendChild(parent.OwnerDocument.CreateNode(XmlNodeType.Element, elementName, “”));
if (elementValue != “”)
element.InnerText = elementValue;
return (element);
}

public static XmlNode AddXmlElement(XmlDocument parent, string elementName, string elementValue)
{
XmlNode element = parent.AppendChild(parent.CreateNode(XmlNodeType.Element, elementName, “”));
if (elementValue != “”)
element.InnerText = elementValue;
return (element);
}

public static XmlNode AddXmlAttribute(XmlNode element, string attrName, string attrValue)
{
XmlNode attr = element.Attributes.Append((XmlAttribute)element.OwnerDocument.CreateNode(XmlNodeType.Attribute, attrName, “”));
if (attrValue != “”)
attr.Value = attrValue;
return (attr);
}

public static void AddFieldRef(XmlNode viewFields, string fieldName)
{
XmlNode fieldRef = AddXmlElement(viewFields, “FieldRef”, “”);
AddXmlAttribute(fieldRef, “Name”, fieldName);
}

private void btnBrowse_Click(object sender, RoutedEventArgs e)
{
System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog();

// Show open file dialog box
dlg.ShowDialog();

// Process open file dialog box results
if (!string.IsNullOrEmpty(dlg.SelectedPath))
{
// Open document
txtDestination.Text = dlg.SelectedPath;
}

}

}
}

Comments
  1. This would be an interesting utility. The lack of ability to easily export wiki pages (even in a basic format) stopped the adoption of SP wikis in 2 companies I worked with. I can see a need for a simple wiki export tool.

  2. Lawrence says:

    I cannot make your code to work in my project.

    Can you please provide the exe package? or link to codeplex project.

    That would be great.

    Thanks,
    Lawrence

  3. Aimee says:

    Hi

    Trying to export a private (intranet type) set of wiki pages and the program stops responding every time, any idea what may be going wrong?

    • jdbranham says:

      At this time there is not a separate thread for the UI. It’s possible it is still working in the background but cannot update the UI until it is complete.
      Have you let it run for a while when it appears to be frozen?

  4. Ron says:

    Does the utility support secure sites? I’ve tried using it to export a wiki from our internal SP site (https://…) and it crashes. I get the “MOSS2007_Wiki_Export has encountered an error and needs to close.” pop-up.

  5. Susan says:

    It worked beautifully. I had to do some reformatting on the pages, but WHO CARES!

    I love you…

    • Thank you Susan! I’ve started a new project that implements a type of utility interface, where multiple functions like this can be executed from a single app. Hopefully I’ll be able to add that to codeplex soon….

  6. Leonard Sisson says:

    Had to wrap GetNamedItem statement in MainWindow.xaml.cs with a try – catch for cases where a wiki doc had been created but contained no text:

    try
    {
    wikiBody = row.Attributes.GetNamedItem(“ows_WikiField”).Value;
    }
    catch (NullReferenceException e) {
    wikiBody = ” “;
    }

Leave a reply to jdbranham Cancel reply