Microsoft

Blog Categories

Subscribe to RSS feed

Archives

Recursively Aggregating Postings from MCMS Channels

The structural relationship between channels in MCMS is hierarchical, with channels capable of containing both postings and other channels. Postings are "leaves" and channels are the "branches," just as are files and folders, respectively, in a file system.

Although there are many possible semantic relationships between a parent channel and its subchannels, it is likely that the postings contained in each are similar to one another. Subchannels may be the sections of a policy manual channel, for example; or they may correspond to departments in an organization channel.

What if you wanted to get all the postings of a given channel and all its subchannels? This question makes sense given the natural relationship between levels in a hierarchy. I came across this requirement on a client project and had to write some code to implement it, which I would like to share with others.

The MCMS Channel object contains several properties: Postings returns the postings within the channel, Channels returns the channels within the channel, and AllChildren returns the postings and channels within the channel. These properties return only the direct child elements, though, so we had to write some code to implement their desired behavior, which was to return all postings of a channel, recursing through all subchannels, too. An additional requirements was that the resultant collection of postings had to be in a specified sort order and direction, with sort order being one of the common MCMS sorts – by change date, display name, last modified date, and so forth.

Initially, one might think to simply start at the base channel, add all the postings to an accumulator collection and recursively visit each subchannel and add its postings to the accumulator. When finished, the accumulator collection would be sorted according to the specified sort order and direction. This approach is sound, except for when the sort order is Ordinal.

Using the MCMS Site Manager, an administrator can bring up the properties of a channel and, using the Sorting tab, explicitly set the order of objects (postings and channels) within the channel. This would be useful when writing a policy manual, for example: within the base channel, the table of contents and introductions postings might come first, followed by several channels corresponding to the sections of the policy manual, and finally followed by some additional postings (faq or glossary, for example). If we are to grab all the postings from the base channel and all subchannels but retain the ordinal positions, it is clear we have to accumulate the postings using a depth first scan, with each channel sorted by Ordinal to begin with. The resultant accumulator collection is then in the proper order and can be returned as is.

With all the other sort orders, we don’t care about the order until after we accumulate the posting collection, and can apply the sort later.

The code shown below is to demonstrate the basic principle behind the two different recursion methods, and is not intended to be a complete solution. There is additional supporting code (enumerations and sorting code) that are not included.

The following method takes a channel, sort order, and sort direction and dumps all the postings within the channel and subchannels into a pre-created ArrayList. If the sort order is Ordinal or Default (the same thing as Ordinal), then one iteration method is called; otherwise, another iteration method is called to handle all the other sort orders.

public static void GetChannelPostings( Channel channel, ChannelSortOrder sortOrder, ChannelSortDirection sortDirection, ArrayList postings )
{
if ( sortOrder == ChannelSortOrder.Ordinal || sortOrder == ChannelSortOrder.Default )
{
GetChannelPostingsByOrdinal( channel, sortDirection, postings );
}
else
{
GetChannelPostingsByNonOrdinal( channel, sortOrder, sortDirection, postings );
}
}

For the depth-first traversal by Ordinal, here is the routine:

private static void GetChannelPostingsByOrdinal( Channel channel, ChannelSortDirection sortDirection, ArrayList postings )
{
// Get all the children – both postings and channels – of the specified channel.
ChannelAndPostingCollection channelItems = channel.AllChildren;

// Apply the specified sort to the contents before proceeding.
SortChannelItems( channelItems, ChannelSortOrder.Ordinal, sortDirection );

// Walk the channel item collection, adding each posting found to the ‘postings’ array and recursively scanning each channel found, if recursion is specified.
foreach ( ChannelItem channelItem in channelItems )
{
if ( channelItem is Posting )
{
// Item is a Posting
postings.Add( channelItem );
}
else
{
// Item is Channel
GetChannelPostingsByOrdinal( ( Channel ) channelItem, sortDirection, postings );
}
}

}

For the "accumulate and sort later" method used for the other sort orders, here are the two routines. The Postings property automatically returns the postings in Ordinal order.

private static void GetChannelPostingsByNonOrdinal( Channel channel, ChannelSortOrder sortOrder, ChannelSortDirection sortDirection, ArrayList postings )
{
PostingCollection postingCollection = null;

// Get all the postings in the channel, recursively
GetChannelPostingsByNonOrdinal( channel, ref postingCollection );

// Sort the contents of the accumulator and convert to an array list
ConvertToArrayList( SortChannelItems( postingCollection, sortOrder, sortDirection ), postings );

}

private static void GetChannelPostingsByNonOrdinal( Channel channel, ref PostingCollection postingCollection )
{
// There is no constructor for a PostingCollection, so we have to get an initial one by accessing
// a Posting property, then use the Union method to accumlate additional postings into it.
if ( postingCollection == null )
{
// Get the initial posting collection
postingCollection = channel.Postings;
}
else
{
// Add some more posting to the posting collection we already have
postingCollection = ( PostingCollection ) postingCollection.Union( channel.Postings );
}

// Accumulate each of the subchannels
foreach ( Channel subchannel in channel.Channels )
{
GetChannelPostingsByNonOrdinal( subchannel, ref postingCollection );
}
}

Leave a Reply