Sometimes you can bang your head against a problem for minutes, hours, days, even weeks. And, as it turns out, the solution is really quite simple.
I've been trying to build a custom web part for a client that would simply iterate all areas of a SharePoint Portal and look for items that require Approval (they have a status of Pending). The part would then display these items to the user so they can approve the item in one place, as opposed to navigating all over the portal looking for and approving items.
I was having trouble because there are two types of content that we were interested in - Portal Listings and Document Libraries. With SharePoint, the Portal product and the standard WSS (Windows SharePoint Services) are very closely integrated, yet very different when coding against their object models. Portal Listings are only available when looking at the Portal object Area. Document Libraries are only available when looking at the WSS object Web.
To get to the Portal Listings, I tried getting a reference to the Home Area and looping through its Areas collection. The Areas collection contains Area objects that represent sibling areas. This didn't seem to give me access to all the areas and sub-areas that had been created on our Portal.
I also had to use AllWebs to gain access to all the Web objects in our Portal to get to the Document Libraries. This just didn't seem right to me.
I played around with this for days, and finally had my epiphany. I remembered some code that Jon Box had written for our Atomic.NET class. It showed using recursion to iterate a control tree of an ASPX Page.
Recursion!!!! Jeez! It's such an elementary concept. And I never considered it. So, now my code is much simpler, and it iterates everything area of the portal and looks at every list. It's perfect!
Here's a skeleton example of the code:
Private Sub iterateAllAreas(ByVal thisArea As Area, ByVal output As HtmlTextWriter)
'Display the Portal Listings of this Area
OutputListing(thisArea.Listings, thisArea, output)
'Areas can give you a Web object using its Web property
Dim thisWeb As SPWeb = thisArea.Web
Dim lists As SPListCollection = thisWeb.Lists
lists.ListsForCurrentUser = True
lists.IncludeRootFolder = True
For Each list As SPList In lists
If (list.BaseTemplate = SPListTemplateType.DocumentLibrary) Then
Dim docLib As SPDocumentLibrary = CType(list, SPDocumentLibrary)
'Make sure this list allows for Approvals
'And make sure this user has permissions to approve items in this list
If docLib.EnableModeration And docLib.Permissions.DoesUserHavePermissions(SPRights.ManageLists) Then
OutputDocLibItems(docLib, Replace(thisArea.Path, ":", "/"), output)
'Recursively call this procedure for all areas and sub-areas
For Each subArea As Area In thisArea.Areas
You will notice that when iterating Document Libraries, I am setting the IncludeRootFolder property to true. This is because in the OutputDocLibItems procedure, I am similarly recursively calling that procedure in order to iterate all of the subfolders of the document library. Otherwise, it would ignore all items in any subfolders that the users may have created in the doc lib.