Listing all sites per variations - an STSADM extension solution

A little while ago, we ran into an issue with the Variation label creation process; it was ending successfully without creating everything.  To make matters worst, it wasn't picking the missing sites/pages with the Synchronization Timer Job.

 

So it was very difficult to figure out what was missing in a particular variation except by browsing to each pages and figuring out if the page was missing.  While we only had 150 sub-sites, it could create 10 or 140 of them.

 

For those interested, it seems that the issue is due to 3 things combined:

  1. Memory leak in the variation creation process (scheduled to be fixed)
  2. Potential memory leak when the farm is on a VMWare virtual environment
  3. Not enough memory on the server :)

 

Now, while waiting to have more memory and a fix, we still had to create variations correctly.  So far, we could only delete a variation and recreate it while hoping it would be done successfully.  In order to assert that we had an incorrect variation label and to what extend, I ended up creating a small STSADM extension that was simply listing each sub-sites in an Xml format along with a counter on each node to mention how many sub-sites is under.

 

Essentially, create a class that implements ISPStsadmCommand and in the main class, call the EnumSites method passing the output file name & Url.  Here's the Visual Basic.NET code excerpt :

 

        Private Function EnumSites(ByVal vOutputFile As String, ByVal vUrl As String) As String

            Dim output As String = String.Empty

            Dim siteMOSS As SPSite = Nothing

            Dim rootWeb As SPWeb = Nothing

            Dim publishingRootWeb As Publishing.PublishingWeb = Nothing

 

 

            Try

                siteMOSS = New SPSite(vUrl)

                rootWeb = siteMOSS.OpenWeb

                publishingRootWeb = PublishingWeb.GetPublishingWeb(rootWeb)

                Dim xmlDoc As New XmlDocument()

 

 

                Dim xmlDeclaration As XmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", Nothing)

                xmlDoc.AppendChild(xmlDeclaration)

 

                Dim xmlRoot As XmlElement = xmlDoc.CreateElement("Variations")

 

 

 

                For Each web As PublishingWeb In publishingRootWeb.GetPublishingWebs

                    If web.Label IsNot Nothing Then

 

                        Dim variation As XmlElement = xmlDoc.CreateElement("Variation")

                        Dim attrSource As XmlAttribute = xmlDoc.CreateAttribute("source")

                        Dim attrName As XmlAttribute = xmlDoc.CreateAttribute("name")

                        Dim attrSiteCount As XmlAttribute = xmlDoc.CreateAttribute("numOfSubSites")

                        Dim numOfSubSites As Integer = 0

 

                        attrSource.Value = web.Label.IsSource.ToString

                        attrName.Value = web.Name

 

                        variation.Attributes.Append(attrName)

                        variation.Attributes.Append(attrSource)

 

                        output &= "Variation : " & web.Name & ", isSource=" & web.Label.IsSource.ToString & Environment.NewLine

                        numOfSubSites += EnumSites(xmlDoc, variation, web.Web.GetSubwebsForCurrentUser(), output)

 

                        attrSiteCount.Value = numOfSubSites.ToString

                        variation.Attributes.Append(attrSiteCount)

                        xmlRoot.AppendChild(variation)

                    End If

                Next

                xmlDoc.AppendChild(xmlRoot)

 

                output &= "Saving Xml output file ..."

                xmlDoc.Save(vOutputFile)

 

                Return output

            Catch ex As Exception

                output &= "Error : " & ex.Message & Environment.NewLine

            Finally

                If siteMOSS IsNot Nothing Then _

                    siteMOSS.Dispose()

                If rootWeb IsNot Nothing Then _

                    rootWeb.Dispose()

            End Try

 

            Return output

        End Function

 

        Private Function EnumSites( _

            ByVal vXmlDoc As XmlDocument, _

            ByVal vXmlParent As XmlElement, _

            ByVal vWebs As SPWebCollection, _

            ByRef rOutput As String _

        ) As Integer

            Dim numOfSubSites As Integer = 0

 

            Try

 

                For Each web As SPWeb In vWebs

                    numOfSubSites += 1

 

                    Dim site As XmlElement = vXmlDoc.CreateElement("Site")

                    Dim attrName As XmlAttribute = vXmlDoc.CreateAttribute("name")

                    Dim attrWelcomePage As XmlAttribute = vXmlDoc.CreateAttribute("welcomePage")

                    Dim attrSiteCount As XmlAttribute = vXmlDoc.CreateAttribute("numOfSubSites")

 

                    attrName.Value = web.Name

                    Try

                        attrWelcomePage.Value = PublishingWeb.GetPublishingWeb(web).DefaultPage.Name

                    Catch ex As Exception

                        attrWelcomePage.Value = "N/D --- " & ex.Message

                    End Try

 

                    site.Attributes.Append(attrName)

                    site.Attributes.Append(attrWelcomePage)

 

                    Dim numofSubSubSites As Integer = EnumSites(vXmlDoc, site, web.GetSubwebsForCurrentUser, rOutput)

                    numOfSubSites += numofSubSubSites

 

                    attrSiteCount.Value = numofSubSubSites.ToString

                    site.Attributes.Append(attrSiteCount)

 

                    vXmlParent.AppendChild(site)

                Next

            Catch ex As Exception

                rOutput &= "Error : " & ex.Message & Environment.NewLine

            End Try

 

            Return numOfSubSites

        End Function

 

You can essentially do the same for pages if you want to list them and do a compare with the source variation in order to list exactly what's missing, however, when we found out that the memory was the issue, we didn't extend this application so it's still in the "napkin format" :)

 

Maxime