As we mentioned, there are essentially three elements that you can use to create navigation: postings, channels, and administrative functions. To utilize each of these elements, you'll use the PAPI, accessing the specific objects and properties. Examine the Objects and Properties Necessary for NavigationWith the exception of administrative functions, all navigation is built with channels or postings. Both channels and postings share common properties for building navigation within your Web site. A list of these properties follows, with a brief explanation of each. Keep in mind that if you closely study the .NET class reference in the CMS documentation, you'll find other properties that may be useful, but the properties listed here are the ones you'll use most frequently.
Creating Basic NavigationNow that you've familiarized yourself with the general design considerations and components of building navigation, let's examine some basic code samples to show you how to use what you've learned. As a start, let's look at basic channel navigation. By basic channel navigation, we're simply referring to getting a collection of channels from the PAPI and creating HTML that a content consumer can click on to navigate through that collection. Looking at Listing 14-1, you can see the basic traversal of a channel within the current channel. Listing 14-1 Basic channel navigationprivate void createBasicChannelNav(Channel startChannel) { // Get a collection of channels from the startChannel ChannelCollection navChannels = startChannel.Channels; // Make sure we've gotten a collection back if (navChannels != null) { // Create a new anchor link and iterate through each // channel in the collection HtmlAnchor navLink; foreach(Channel navChannel in navChannels) { // For each channel in the collection, create new anchor // link and add it to the .NET placeholder control on // on our page navLink = new HtmlAnchor(); navLink.InnerText = navChannel.DisplayName; navLink.HRef = navChannel.Url; BasicChannelNav.Controls.Add(navLink); BasicChannelNav.Controls.Add(new LiteralControl("<BR>")); } } } In this example, you'll notice that we're using the Channel object and the properties listed in Listing 14-1. We first retrieve a collection of channels in the start channel. Then, we iterate through the collection, pulling out the Url and DisplayName properties. We've created an ASPX page with a .NET placeholder control (note: this is not the CMS placeholder control) to hold the HtmlAnchor controls we're creating in our code (we could have also added the control to the page controls collection). In Figure 14-9 you can see the result of this code (the results of all code samples in this section are shown in this figure). Figure 14-9. Basic channel navigation in an ASPX pageThe next example is basic posting navigation. In Listing 14-2, you'll notice the code is very similar to the channel example. We simply get a collection of postings from the pass-in startChannel object and then iterate through the collection Listing 14-2 Basic posting navigationprivate void createBasicPostingNav(Channel startChannel) { PostingCollection navPostings = startChannel.Postings; if (navPostings != null) { HtmlAnchor navLink; foreach(Posting navPosting in navPostings) { navLink = new HtmlAnchor(); navLink.InnerText = navPosting.DisplayName; navLink.HRef = navPosting.Url; BasicPostingNav.Controls.Add(navLink); BasicPostingNav.Controls.Add(new LiteralControl("<BR>")); } } } Combining both of the prior examples and using recursion to iterate through a whole hierarchy, Listing 14-3 demonstrates mixed channel and posting navigation. Again, this sample builds on the prior two examples, with the exception of the recursive call. Listing 14-3 Basic channel and posting navigation[View full width] private void createBasicChannelPostingNav(Channel startChannel, string navIndent) { // Retrieve a collection of postings and channels PostingCollection navPostings = startChannel.Postings; ChannelCollection navChannels = startChannel.Channels; // Create an anchor link HtmlAnchor navLink = new HtmlAnchor(); // Write out the link to the start channel and add it // to the .NET (not CMS) placeholder control navLink.HRef = startChannel.Url; navLink.InnerText = startChannel.DisplayName; // Add an indent before the channel link; this will be additive for // each recursive call BasicChannelPostingNav.Controls.Add(new LiteralControl(navIndent)); // Add the link to the .NET placeholder control BasicChannelPostingNav.Controls.Add(navLink); BasicChannelPostingNav.Controls.Add(new LiteralControl("<br>")); // Check to see if we got back a collection of postings if (navPostings != null) { foreach(Posting navPosting in navPostings) { // Write out a link to each posting navLink = new HtmlAnchor(); navLink.InnerText = navPosting.DisplayName; navLink.HRef = navPosting.Url; // Add an indent to each posting (this will be additive as // we call the function recursively) BasicChannelPostingNav.Controls.Add(new LiteralControl (" "+navIndent)); // Add the posting link to the .NET placeholder control BasicChannelPostingNav.Controls.Add(navLink); BasicChannelPostingNav.Controls.Add(new LiteralControl("<BR>")); } } // Make sure we got back a collection of channels if (navChannels != null) { foreach(Channel subChannel in navChannels) { // For each of the sub channels, call the function recursively createBasicChannelPostingNav(subChannel, navIndent+" "); } } } Our last sample is a very simple breadcrumb example. In Listing 14-4, notice that we use the parent property of the Channel object to "walk" up the hierarchy. Each time, we retrieve the appropriate properties from the channel we're working with and then reset the currentLocation variable to the parent of that channel. This function provides a good opportunity to create an overload for this function that accepts a posting instead of a channel; the basics of this function would remain the same, since the parent property of a posting also returns a channelitem. Although this function was implemented using a conditional loop, it could just easily have been implemented using recursion. Listing 14-4 Basic breadcrumb navigationprivate void createBasicBreadcrumbNav(Channel startChannel) { // Create a variable to hold the start channel object Channel myCurrentLocation = startChannel; // Make sure you're not already at the "root" of your site if (myCurrentLocation.Guid != myBaseChannel.Guid) { HtmlAnchor navLink; // While you're not at your root create a breadcrumb while(myCurrentLocation.Guid != myBaseChannel.Guid) { // Create a new anchor link and set the appropriate // values navLink = new HtmlAnchor(); navLink.HRef = myCurrentLocation.Url; navLink.InnerText = myCurrentLocation.DisplayName; // Add your breadcrumb to the .NET placeholder control BasicBreadcrumbNav.Controls.AddAt(0,navLink); BasicBreadcrumbNav.Controls.AddAt(0,new LiteralControl(" > ")); // Set the current location to the parent of the // current location; we're walking one level up myCurrentLocation = myCurrentLocation.Parent; } // When the loop exits, create a new anchor object // and set the appropriate value to the root of your // breadcrumb navLink = new HtmlAnchor(); navLink.HRef = myBaseChannel.Url; navLink.InnerText = myBaseChannel.DisplayName; BasicBreadcrumbNav.Controls.AddAt(0,navLink); } } BOTS Consulting Navigation SamplesReviewing basic examples is nice, but it's always better to see the navigation implemented in "real" sites. BOTS is certainly not a very complicated site, but we thought it was useful to share the main and left navigation code as a way of comparing the basic examples to code that had a few exceptions. Both the left and main navigation code was implemented in a user control that was dropped onto a page for ease of reuse. In Listing 14-5, you'll notice that we're not taking in a value to start the navigation. In the function, BOTS is grabbing a very specific channel and starting with that. They made the assumption that this code would be used only in their site. This isn't a bad technique, but it does tend to limit reuse. As you can see, there aren't any exceptions in this code; it's really just basic channel navigation with some HTML formatting. Listing 14-5 BOTS Consulting main navigation[View full width] private void createMainNavigation() { try { // Retrieve the main BOTS Consulting channel Channel NavParentChannel = (Channel)CmsHttpContext. Current.Searches.GetByPath ("/Channels/botsconsulting"); this.GlobalNav.Attributes.Add("width","100%"); TableRow GlobalNavRow = new TableRow(); // Make sure we got the channel back if (NavParentChannel != null) { // Get the collection of channels in the BOTS Consulting site ChannelCollection NavParentSubChannels = NavParentChannel.Channels; HtmlAnchor navLink; // Iterate through the channels in the site foreach(Channel SubChannel in NavParentSubChannels) { navLink = new HtmlAnchor(); TableCell newTableCell = new TableCell(); newTableCell.Attributes.Add("align","center"); navLink.Style.Add("FONT-WEIGHT","bold"); navLink.Style.Add("COLOR","gray"); navLink.HRef = SubChannel.Url; navLink.InnerText = SubChannel.DisplayName; newTableCell.Controls.Add(navLink); GlobalNavRow.Cells.Add(newTableCell); } } this.GlobalNav.Rows.Add(GlobalNavRow); } // Perform some very basic exception "handling" catch(Exception errException) { TableRow GlobalNavRow = new TableRow(); TableCell NavItem = new TableCell(); NavItem.Text = "The following exception occurred: " +errException.Message + "<br>SOURCE: " + errException.Source; GlobalNavRow.Cells.Add(NavItem); this.GlobalNav.Rows.Add(GlobalNavRow); } } In the following listings, we provide various "interesting" sections of the left navigation. The code is somewhat lengthy, so we're listing only certain portions that need to be highlighted. In Listing 14-6, we've provided the task-based navigation. These few lines of code provide a way to create a new posting based on the Press Release Detail template of the In the Press channel. Notice that there are some conditions wrapped around the link so that a content contributor can't accidentally create a posting with the wrong template or in the wrong channel. Listing 14-6 Task-based publishing in the Web Author[View full width] // Check to see if the posting is in EDIT mode if ((WebAuthorContext.Current.Mode == WebAuthorContextMode.PresentationUnpublished) && (CmsHttpContext.Current.Channel.Name.ToString() == "press") && CmsHttpContext.Current.Posting.Template.Name == "Press Release Detail") { // Get the template and channel object for this posting Template thisTemplate = CmsHttpContext.Current.Posting.Template; Channel thisChannel = CmsHttpContext.Current.Channel; // Create the task link HtmlAnchor taskPublishingLink = new HtmlAnchor(); // Get a URL to a new posting based on the Press Release Detail template taskPublishingLink.HRef = WebAuthorContext.Current.GetAuthoringNewUrl(thisTemplate ,thisChannel); taskPublishingLink.InnerText = "New Press Release"; NewTableCell.Controls.Add(new LiteralControl("<br>")); // Add the publishing link to the side navigation table cell NewTableCell.Controls.Add(taskPublishingLink); } In Listing 14-7, we're showing how BOTS handles most of the left navigation, including the channels and postings. Notice the special conditions with regard to the default posting and the custom property setting that determines whether to show postings in the left navigation or not. Again, in some cases they want postings to display and in other they don't. In an effort to make the code more flexible, they're examining a custom property on the channel to determine whether postings should be listed. Listing 14-7 The left navigation logic for BOTS[View full width] private void BuildLeftNavTable(Channel StartChannel) { // Define the necessary variables for creating the nav table // and the links HtmlAnchor NavigationLink; TableRow NewTableRow; TableCell NewTableCell; Literal TextPlaceholder; // Get a collection of channels in the pass-in channel ChannelCollection SubChannels = StartChannel.Channels; // Check to make sure we got back a collection if(SubChannels != null) { // Iterate through the collection foreach(Channel SubChannel in SubChannels) { // Create a new row, cell and anchor link NewTableRow = new TableRow(); NewTableCell = new TableCell(); NavigationLink = new HtmlAnchor(); NavigationLink.HRef = SubChannel.Url; // If the channel we're on matches the current channel // add bolding to the style if (SubChannel.Guid.ToString() == CmsHttpContext.Current .Channel.Guid.ToString()) { NavigationLink.Attributes.Add("style","FONT-WEIGHT: Bold; COLOR: gray"); } else { NavigationLink.Attributes.Add("style","COLOR: gray"); } NavigationLink.InnerText = SubChannel.DisplayName; NewTableCell.Controls.Add (NavigationLink); // If the channel we're on matches the current channel // determine whether we need to display postings if (SubChannel.Guid.ToString() == CmsHttpContext.Current .Channel.Guid.ToString()) { // Only display postings if we're not on the default posting // or the custom property ShowPostingsOnDefault is set to true if((CmsHttpContext.Current.Posting.Name.ToString()!= "default") || ((CustomProperty)SubChannel.CustomProperties ["ShowPostingsOnDefault"]).Value .ToString() == "True") { TextPlaceholder = new Literal(); TextPlaceholder.Text="</UL>"; NewTableCell.Controls.Add(TextPlaceholder); // Call the show postings function ShowPostingsInChannel(SubChannel,NewTableCell); TextPlaceholder = new Literal(); TextPlaceholder.Text="</UL>" NewTableCell.Controls.Add(TextPlaceholder); } } NewTableRow.Cells.Add(NewTableCell); this.LeftNav.Rows.Add(NewTableRow); } } NewTableCell = new TableCell(); TextPlaceholder = new Literal(); TextPlaceholder.Text = "<hr color='gray' size='1' width='100%'>"; NewTableCell.Controls.Add(TextPlaceholder); NewTableRow = new TableRow(); NewTableRow.Cells.Add(NewTableCell); this.LeftNav.Rows.Add(NewTableRow); } private void ShowPostingsInChannel(Channel CurrentChannel, TableCell PostingTableCell) { Literal TextPlaceholder; HtmlAnchor NavigationLink; foreach(Posting SubPosting in CurrentChannel. Postings) { if(SubPosting.Name.ToLower() != "default") { TextPlaceholder = new Literal(); TextPlaceholder.Text = "<br>"; NavigationLink = new HtmlAnchor(); // If the posting we're on is the current posting // then add bolding to the style if(SubPosting.Guid.ToString() == CmsHttpContext .Current.Posting.Guid.ToString()) { NavigationLink.Attributes.Add("style","FONT-WEIGHT: bold; COLOR: gray"); } else { NavigationLink.Attributes.Add("style","COLOR: gray"); } NavigationLink.HRef = SubPosting.Url; NavigationLink.InnerText = SubPosting.DisplayName; PostingTableCell.Controls.Add(NavigationLink); PostingTableCell.Controls.Add(TextPlaceholder); } } } It is a little more complex than the main navigation, but still uses the concepts we demonstrated in the basic navigation samples. In addition, it accounts for the unique situations within the BOTS site. |