Book Image

Advanced Microsoft Content Management Server Development

Book Image

Advanced Microsoft Content Management Server Development

Overview of this book

Microsoft Content Management Server 2002 is a dynamic web publishing system with which you can build websites quickly and cost-efficiently. MCMS provides the administration, authoring, and data management functionality, and you provide the website interface, logic, and workflow. Microsoft SharePoint Portal Server (SPS) also features in the book. SPS 2003 enables enterprises to deploy an intelligent portal that seamlessly connects users, teams, and knowledge so that people can take advantage of relevant information across business processes to help them work more efficiently.You've mastered the basics of MCMS, and setup your own MCMS installation. You've only scratched the surface. This book is your gateway to squeezing every penny from your investment in MCMS and SPS, and making these two applications work together to provide an outstanding richness of content delivery and easy maintainability. As a developer, the Publishing API (PAPI) is at the heart of your work with MCMS, and this book starts by taking you on the most detailed tour of the PAPI you will find anywhere. As a live example, a component that reveals the structure of your MCMS site is created, taking you through how to manage the common elements of MCMS programmatically. Getting SharePoint and MCMS to work together is the next stop in the book. You will see how to use SharePoint's search engine to search MCMS content, publish content between the two systems, and create SharePoint Web Parts to draw content from MCMS.To ease your everyday work with MCMS, there are chapters on placeholder validation, and some useful custom placeholders for common MCMS tasks, such as a date-time picker, a placeholder for multiple attachments, and a DataGrid placeholder among others. There are a number of ways to consume MCMS content from the outside world, and we look at two exciting ways here; RSS and InfoPath/Web Services. The InfoPath solution provides another interface to MCMS content that allows content authors to concentrate on content and not the presentation. The book is rounded off with a number of must-have MCMS tips and tricks. Revert a posting to a previous version Change a postingÔø???s template Build a recycle bin Deal with links to deleted resources Update a postingÔø???s properties directly from a template file Re-write ugly URLs to friendly URLs Export resource gallery items using the site deployment API (SDAPI) Configure the position and size of the Web Author Console Dialogs Get frames and IFrames to work correctly in a template file
Table of Contents (21 chapters)
Advanced Microsoft Content Management Server Development
Credits
About the Authors
About the Reviewers
Index

Rendering Collections in a DataGrid


Our next task is to display a list of objects held by a given container in a DataGrid.

We could choose to iterate through collections of channels and postings and add them into a table. However, there’s an even faster way to accomplish this: we bind the collection of items to a DataGrid. No iterations and tables are needed; simply set the collection of objects as the data source of the DataGrid and call the DataBind() method:

// data bind a collection to a DataGrid
DataGrid1.DataSource = myCollectionOfPostingsAndChannels;
DataGrid1.DataBind();

To see how this works, open default.aspx in HTML view. Drag and drop a DataGrid from the Toolbox into the cell containing the words (Space for DataGrid) and delete the text markers. Set the properties of DataGrid1 to:

Property

Value

AutoFormat

Simple 3

Width

100%

Font-size

10pt

DataKeyField

Guid

ID

DataGrid1

Double-click on the form to get to its code-behind file. Directly below the Page_Load() event handler, add the BindData() method. The method gets a collection of all objects in the start container and sorts them by name in ascending order. The last two lines set the collection as the DataSource of DataGrid1 and call the DataGrid1.DataBind() method.

private void BindData()
{
  // getting a collection of all channels and
  // postings below the root channel
  if (startItem is Channel)
  {
    Channel startChannel = startItem as Channel;
    ChannelItemCollection allChildren;
    allChildren = startChannel.AllChildren;
    allChildren.SortByDisplayName(true);

    // display the collection of items retrieved in a datagrid
    DataGrid1.DataSource = allChildren;
  }
  DataGrid1.DataBind();
}

Lastly, in the Page_Load() event handler, add a call to the BindData() method inside the if (!Page.IsPostBack) code block:

if (!Page.IsPostBack)
{
  // display the publishing mode
  lblPublishingMode.Text = "Publishing Mode: "
                         + cmsContext.Mode.ToString();
  // use the start channel's display name as the
  // header for the page
  litCurrentContainer.Text = startItem.Name;
}

// bind the object collection on every page load, regardless
					// of a postback
					BindData();
				

Save and build the solution and navigate to http://localhost/CmsExplorer. The figure below shows what you will see. The image at the top is broken because we haven’t assigned an image to it yet.

The DataGrid displays a list of all objects in the channel, as well their properties. It’s a very useful technique for getting a bird’s eye view of all the objects in a collection.

Displaying Only Selected Properties in the DataGrid

Obviously, we aren’t going to display all property values in the grid. We will show only:

  • Name

  • Last Modified Date

First, set the AutoGenerateColumns property of DataGrid1 to false. This will prevent the DataGrid from displaying all fields in the collection. Within the <asp:DataGrid> tags, add the following code:

<Columns>
<asp:TemplateColumn HeaderText="Name">
<ItemTemplate>
  <a id="aName" runat="server">
  <%# DataBinder.Eval(Container.DataItem, "Name") %>
  </a>
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="LastModifiedDate"
                 ReadOnly="True"
                 HeaderText="Last Modified Date"
                 DataFormatString="{0:dd MMM yyyy hh:mm tt}">
</asp:BoundColumn>
</Columns>

Using this method, we display only the properties that we are interested in showing in the grid. You may be wondering why we didn’t use a BoundColumn for the name. That’s because, in this particular setup, the name field isn’t static. We want to render the name field for a container (channels, template galleries, or resource galleries) as a hyperlink that reveals its contents in the grid when clicked. Since postings, templates, and resources do not contain child items, their names will remain as text.

Considerations for Template Galleries and Resource Galleries

Unlike channels, there isn’t an equivalent of the AllChildren property for template galleries and resource galleries. In fact, if you study the PAPI carefully, you will find that collections of template galleries belong to the TemplateGalleryCollection class and collections of templates belong to the TemplateCollection class. Because a TemplateGalleryAndTemplateCollection class does not exist, you can’t mix both into a single collection. The same applies for resource galleries and resources.

The only way to get around this is to iterate through both collections, and add each item to a DataTable. Our DataTable will consist of three columns, one for each of the properties we have chosen to display: Guid, Name, and LastModifiedDate. It is created using a PrepareDataTable() helper function added directly below the BindData() method:

private DataTable PrepareDataTable()
{
  DataTable dt = new DataTable();
  dt.Columns.Add(new DataColumn("Guid"));
  dt.Columns.Add(new DataColumn("Name"));
  dt.Columns.Add(new DataColumn("LastModifiedDate"));

  return dt;
}

Next, we iterate through the parent container and add all sub-galleries and objects as rows to our DataTable. This will give us a collection of sub-gallery names followed by a collection of objects, which we’ll then bind to the DataGrid. Let’s add this code to the BindData() method:

private void BindData()
{
  // getting a collection of all containers and
  // items below the start container

  if (startItem is Channel)
  {
    Channel startChannel = startItem as Channel;
    ChannelItemCollection allChildren;
    allChildren = startChannel.AllChildren;
    allChildren.SortByDisplayName(true);
    // display the collection of items retrieved in a datagrid
    DataGrid1.DataSource = allChildren;
  }

  else if (startItem is TemplateGallery)
							{
							TemplateGallery startTemplateGallery = startItem as TemplateGallery;
							DataTable dt = PrepareDataTable();
							// add the template galleries
							foreach(TemplateGallery tg in startTemplateGallery.TemplateGalleries)
							{
							DataRow dr = dt.NewRow();
							dr = AddItem(dr, tg);
							dt.Rows.Add(dr);
							}
							// add the templates
							foreach(Template t in startTemplateGallery.Templates)
							{
							DataRow dr = dt.NewRow();
							dr = AddItem(dr, t);
							dt.Rows.Add(dr);
							}
							DataGrid1.DataSource = dt.DefaultView;
							}
							else if (startItem is ResourceGallery)
							{
							ResourceGallery startResourceGallery = startItem as ResourceGallery;
							DataTable dt = PrepareDataTable();
							// add the resource galleries
							foreach(ResourceGallery rg in startResourceGallery.ResourceGalleries)
							{
							DataRow dr = dt.NewRow();
							dr = AddItem(dr, rg);
							dt.Rows.Add(dr);
							}
							// add the resources
							foreach(Resource r in startResourceGallery.Resources)
							{
							DataRow dr = dt.NewRow();
							dr = AddItem(dr, r);
							dt.Rows.Add(dr);
							}
							DataGrid1.DataSource = dt.DefaultView;
							}

  DataGrid1.DataBind();
}

Rows are added to the table using the AddItem() helper function. Add the AddItem() method directly below the PrepareDataTable() method:

private DataRow AddItem(DataRow dr, HierarchyItem hi)
{
  dr[0] = hi.Guid;
  dr[1] = hi.Name;
  dr[2] = hi.LastModifiedDate;

  return dr;
}

Adding Custom Columns to the DataGrid

Binding the entire collection to the grid and specifying only the properties you want displayed is a handy trick. But let’s say you want to add an icon at the side of each object to indicate whether it’s a channel, posting, template gallery, or something else. None of the existing properties in the collection gives an indication of the object’s type.

At the same time, we want to supply the URLs for hyperlinks surrounding channel display names. For channels, the URLs point to default.aspx?<QueryStringModeUnpublished>, and postings won’t be clickable so the Href property of their surrounding <A> tags will be left blank.

We could change our script to iterate through each object one by one and add these additional columns to a DataTable before binding it to the DataGrid. However, that would mean changing our code. The good news is that we don’t have to rewrite the code. We can implement the DataGrid1_ItemDataBound event handler to populate columns with custom values depending on whether the object is a channel or a posting.

First, add a new TemplateColumn to the DataGrid:

<Columns>
  <asp:TemplateColumn>
						<ItemTemplate></ItemTemplate>
						</asp:TemplateColumn><asp:TemplateColumn HeaderText="Name">
  <ItemTemplate>
   <a id="aName" runat="server">
   <%# DataBinder.Eval(Container.DataItem, "Name") %>
   </a>
  </ItemTemplate>
  </asp:TemplateColumn>
  <asp:BoundColumn DataField="LastModifiedDate" ReadOnly="True" HeaderText=
   "Last Modified Date" DataFormatString="{0:dd MMM yyyy hh:mm tt}"/>
</Columns>

The new TemplateColumn will contain an image indicating the type of the object bound to this row.

Next, we implement the DataGrid1_ItemDataBound() event handler. A quick way to register the event handler is to select the Events button at the top of the DataGrid1 properties window (available in Design view). Double-click on the ItemDataBound field to get to the DataGrid1_ItemDataBound event handler in the code-behind file and modify it as shown below:

private void DataGrid1_ItemDataBound(object sender,
  System.Web.UI.WebControls.DataGridItemEventArgs e)
{
  if (e.Item.ItemType==ListItemType.EditItem
						|| e.Item.ItemType==ListItemType.Item
						|| e.Item.ItemType==ListItemType.AlternatingItem)
						{
						string guid = DataGrid1.DataKeys[e.Item.ItemIndex].ToString();
						HierarchyItem hItem = cmsContext.Searches.GetByGuid(guid);
						if (hItem is Channel)
						{
						// if the object is a channel, show the channel icon
						// set the Name to be a hyperlink
						// that points to default.aspx?{QueryStringModeUnpublished}
						e.Item.Cells[0].Text="<img src='images/channel.gif'>";
						HtmlAnchor aName;
						aName=e.Item.Cells[1].FindControl("aName") as HtmlAnchor;
						aName.HRef = "default.aspx?"
						+ ((Channel)hItem).QueryStringModeUnpublished;
						}
						else if (hItem is Posting)
						{
						// if the object is a posting, show the posting icon
						// leave the Name as text
						e.Item.Cells[0].Text="<img src='images/posting.gif'>";
						}
						else if (hItem is TemplateGallery)
						{
						// if the object is a template gallery, show the
						// template gallery icon
						// set the Name to be a hyperlink
						// that points to default.aspx?{QueryStringModeUnpublished}
						e.Item.Cells[0].Text="<img src='images/templategallery.gif'>";
						HtmlAnchor aName;
						aName=e.Item.Cells[1].FindControl("aName") as HtmlAnchor;
						aName.HRef = PrepareUrl(hItem, PublishingMode.Unpublished,
                             "default.aspx");
						}
						else if (hItem is Template)
						{
						// if the object is a template, show the template icon
						// leave the Name as text
						e.Item.Cells[0].Text="<img src='images/template.gif'>";
						}
						else if (hItem is ResourceGallery)
						{
						// if the object is a resouce gallery, show the
						// resource gallery icon
						// set the Name to be a hyperlink
						// that points to default.aspx?{QueryStringModeUnpublished}
						e.Item.Cells[0].Text="<img src='images/resourcegallery.gif'>";
						HtmlAnchor aName;
						aName=e.Item.Cells[1].FindControl("aName") as HtmlAnchor;
						aName.HRef = PrepareUrl(hItem, PublishingMode.Unpublished,
                              "default.aspx");
						}
						else if (hItem is Resource)
						{
						// if the object is a resource, show the resource icon
						// leave the name as text
						e.Item.Cells[0].Text="<img src='images/resource.gif'>";
						}
						// add actions specific to the object type
						If (e.Item.ItemType==ListItemType.EditItem)
						{
						// in the table generated by the datagrid,
      // the action column is the 4th cell
						TableCell actionCell = e.Item.Cells[4];
						AddActionItems(actionCell, hItem);
						}
						}
}

This method determines the type of HierarchyItem that’s being bound, be it a channel, posting, resource gallery, resource, template gallery, or template. It then sets the icon in each row of the DataGrid to the URL of the image that represents that object type. If the object is a channel, template gallery, or resource gallery the object name is linked using our PrepareUrl() method to reload the page setting it as the startItem. The last section calls the AddActionItems() method, which we’ll use to build an edit action menu for each row in the DataGrid. Let’s take a look at this method in the following section.

Building an Edit Menu

We need to add an Edit button to each row. When the button is clicked, a list of options that can be performed on the object is displayed. The table below shows a list of options for each object type.

Object Type

Actions

Channel, Template Gallery, Template, Resource Gallery

Properties

Delete

Posting

Copy

Move

Create Connected Posting

Properties

Delete

Template

Copy

Move

Create Connected Template

Properties

Delete

Resource

Replace

Properties

Delete

Here’s how the DataGrid will appear once we’re done, and we click the Edit button for the Egg Plant posting:

We add two new columns to the DataGrid, one to show the Edit button and another to contain the list of possible actions.

<Columns>
<asp:TemplateColumn>
   <ItemTemplate></ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="DisplayName">
<ItemTemplate>
   <a id="aName" runat="server">
   <%# DataBinder.Eval(Container.DataItem, "DisplayName") %>
   </a>
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="LastModifiedDate" ReadOnly="True"
 HeaderText="Last Modified Date"
 DataFormatString="{0:dd MMM yyyy hh:mm tt}"></asp:BoundColumn>

<asp:EditCommandColumn ButtonType="LinkButton" EditText="Edit">
						</asp:EditCommandColumn>
						<asp:TemplateColumn></asp:TemplateColumn></Columns>

When the Edit button is clicked, we set the EditItemIndex of DataGrid1 to the index of the selected row. In the events property window of DataGrid1, double-click EditCommand to register the event handler and add the following code:

						private void DataGrid1_EditCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
						{
						DataGrid1.EditItemIndex = e.Item.ItemIndex;
						BindData();
						}
					

At the same time, we want to display a list of possible actions that can be performed on the selected object. This is done by the AddActionItems() method. The method creates hyperlinks for each of the action items defined in the table above. The AddActionItems() method accepts two input parameters:

  • A TableCell named actionCell. This is the cell to add action button to.

  • A HierarchyItem named hItem. This is the item we are creating the action buttons for.

After determining the type of object passed to the AddActionItems() method, we add the type-specific action buttons for the current object. For example, if a posting is passed to the method Copy and Move buttons are added.

In addition to the type-specific options a Properties button is added to the menu, which applies to all objects. Finally, we will check to see if the user has permissions to delete the current object and if so, we’ll add a Delete button.

Notice that we’re using the URL generated by our PrepareUrl() method to assign to the NavigateUrl property of each action button. Add the AddActionItems() method below the DataGrid1_EditCommand() event handler:

private void AddActionItems(TableCell actionCell, HierarchyItem hItem)
{
  if (hItem is Posting)
  {
    Posting currentPosting = hItem as Posting;
   // actions for postings include:
   // Copy, Move, Create Connected Posting.

   // the copy option
   HyperLink hCopy = new HyperLink();
   hCopy.Text = "Copy<br>";
   hCopy.Target = "_blank";
   hCopy.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                  "CopyPosting.aspx");
   actionCell.Controls.Add(hCopy);

   // the move option
   if (currentPosting.CanMove)
   {
      HyperLink hMove = new HyperLink();
      hMove.Text = "Move<br>";
      hMove.Target = "_blank";
      hMove.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                     "MovePosting.aspx");
      actionCell.Controls.Add(hMove);
   }

    // the create connected posting option
    HyperLink hCreateConnected = new HyperLink();
    hCreateConnected.Text = "Create Connected Posting<br>";
    hCreateConnected.Target = "_blank";
    hCreateConnected.NavigateUrl = PrepareUrl(hItem,
    PublishingMode.Update, "CreateConnectedPosting.aspx");
    actionCell.Controls.Add(hCreateConnected);
  }
  else if (hItem is Template)
  {
    Template currentTemplate = hItem as Template;
    // actions for templates include:
    // Copy, Move, Create Connected Template

    // the copy option
    HyperLink hCopy = new HyperLink();
    hCopy.Text = "Copy<br>";
    hCopy.Target = "_blank";
    hCopy.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                   "CopyTemplate.aspx");
    actionCell.Controls.Add(hCopy);
    // the move option
    if (currentTemplate.CanMove)
    {
      HyperLink hMove = new HyperLink();
      hMove.Text = "Move<br>";
      hMove.Target = "_blank";
      hMove.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                     "MoveTemplate.aspx");
      actionCell.Controls.Add(hMove);
    }

    // the create connected template option
    HyperLink hCreateConnected = new HyperLink();
    hCreateConnected.Text = "Create Connected Template<br>";
    hCreateConnected.Target = "_blank";
    hCreateConnected.NavigateUrl = PrepareUrl(hItem,
             PublishingMode.Update, "CreateConnectedTemplate.aspx");
    actionCell.Controls.Add(hCreateConnected);
  }
  else if (hItem is Resource)
  {
    Resource currentResource = hItem as Resource;
    // Resources have an additional option
    // to Replace their contents.
    // the replace option
    if (currentResource.CanSetContent)
    {
      HyperLink hReplace = new HyperLink();
      hReplace.Text = "Replace<br>";
      hReplace.Target = "_blank";
      hReplace.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                        "ReplaceResource.aspx");
      actionCell.Controls.Add(hReplace);
    }
  }

  // add shared options include:
  // Properties and Delete.

  // the properties option
  HyperLink hProperties = new HyperLink();
  hProperties.Text = "Properties<br>";
  hProperties.Target = "_blank";
  hProperties.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                        "Properties.aspx");
  actionCell.Controls.Add(hProperties);

  // the delete option
  if (hItem.CanDelete)
  {
     HyperLink hDelete = new HyperLink();
     hDelete.Text = "Delete<br>";
     hDelete.Target = "_blank";
     hDelete.NavigateUrl = PrepareUrl(hItem, PublishingMode.Update,
                                      "Delete.aspx");
     actionCell.Controls.Add(hDelete);
  }
}

Note

If you receive a JavaScript error message when you test the above code, you probably need to change the ID in the opening form tag to something else, such as “CMSExplorerDefault” as the browser may not like the ID “default” that Visual Studio .NET assigned to the form.

Save and build the solution and navigate to http://localhost/CmsExplorer. At this point you can browse the channels by clicking on the channel names as well as viewing our actions menu.

The next thing we’ll need is a toolbar to move up a level in the hierarchy from the currently selected parent container, to refresh the page, and to select a root path other than channels, such as templates or resources.

Building the Toolbar

The gray bar at the top of the grid is the toolbar. It will consist of six controls:

  • The Up button that brings the user one level up the channel hierarchy

  • The Refresh button that updates the display

  • A DropDownList containing options to create a new channel, posting, template, template gallery, or resource

  • The Channels button to navigate through the available channels and postings

  • The Templates button to navigate through the available template galleries and templates

  • The Resources button to navigate through the available resource galleries and resources

In HTML view for the default.aspx page, replace the text in the space marked (Space for Toolbar) with the code below:

<table cellSpacing="0" cellPadding="3">
<tr>
  <td>
    <asp:LinkButton ID="btnUp" Runat="server">
      <img src="images/parentfolder.gif" border="0" align="absmiddle">
      <span style="text-decoration:none">Up</span>
    </asp:linkbutton>
  </td>
  <td>
    <asp:LinkButton ID="btnRefresh" Runat="server">
       <img src="images/refresh.gif" border="0" align="absmiddle">
       <span style="text-decoration:none">Refresh</span>
    </asp:linkbutton>
  </td>
  <td>|</td>
  <td>
    <asp:DropDownList id="ddlNewItem" Runat="server"
            AutoPostBack="True"></asp:dropdownlist>
  </td>
  <td>|</td>
  <td>
    <asp:LinkButton ID="btnChannels" Runat="server">
      <img src="images/channel.gif" border="0" align="absmiddle">
      <span style="text-decoration:none">Channels</span>
    </asp:LinkButton>
  </td>
  <td>|</td>
  <td>
    <asp:LinkButton ID="btnTemplates" Runat="server">
      <img src="images/templategallery.gif" border="0" align="absmiddle">
      <span style="text-decoration:none">Templates</span>
    </asp:LinkButton>
  <td>|</td>
  <td>
    <asp:LinkButton ID="btnResources" Runat="server">
      <img src="images/resourcegallery.gif" border="0" align="absmiddle">
      <span style="text-decoration:none">Resources</span>
    </asp:LinkButton>
  </td>
</tr>
</table>

The Up Button

In Design view, double-click on the btnUp LinkButton. This button will essentially be performing the same function as the Up button in explorer, namely taking you one level back up the hierarchy.

If the current startItem is a channel and it has a parent channel we’ll simply reload default.aspx, appending the information about the Unpublished mode of the parent container. If the current item is a template gallery or resource gallery, we’ll try to obtain the URL using our PrepareUrl() method of the gallery’s parent. If PrepareUrl() returns an empty string, the current gallery has no parent so we won’t reload the page.

private void btnUp_Click(object sender, System.EventArgs e)
{
  // if the current item is a channel...
							if (startItem is Channel)
							{
							Channel startChannel = startItem as Channel;
							// if this channel has a parent, reload the page setting the
							// parent container to the current channel's parent
							if (startChannel.Parent!=null)
							{
							Response.Redirect("default.aspx?"
							+ startChannel.Parent.QueryStringModeUnpublished);
							}
							}
							// else if the current item is a template gallery
							else if (startItem is TemplateGallery)
							{
							// if this TemplateGallery has a parent, reload the page setting
							// the parent container to the TemplateGallery's parent
							string url = PrepareUrl(((TemplateGallery)startItem).Parent,
                      PublishingMode.Unpublished, "default.aspx");
							if (url!="")
							{
							Response.Redirect(url);
							}
							}
							// else if the current item is a resouce gallery
							else if (startItem is ResourceGallery)
							{
							// if this ResourceGallery has a parent, reload the page setting
							// the parent container to the ResourceGallery's parent
							string url = PrepareUrl(((ResourceGallery)startItem).Parent,
                        PublishingMode.Unpublished, "default.aspx");
							if (url!="")
							{
							Response.Redirect(url);
							}
							}
}

The Refresh Button

In design view, double-click on the btnRefresh button. To update the display on the DataGrid, we need to fetch the latest data from the MCMS repository. This is done in the BindData() method defined later.

private void btnRefresh_Click(object sender, System.EventArgs e)
{
  // refresh the content displayed in the DataGrid
							BindData();
}

The DropDownList

Double-click on the ddlNewItem DropDownList in the default.aspx page in Design view to get to the ddlNewItem_SelectedIndexChanged() event handler. When the user selects an item in the DropDownList, the dialog associated with the selection opens in a new browser window. The URL of the dialog is determined by the last parameter of our PrepareUrl() method. Remember the last parameter of the PrepareUrl() method allows us to specify a page other than default.aspx to add to the URL. We’re going to use this to specify a specific dialog to open in a new window. Don’t worry about the dialogs for now; we’ll be creating them later on.

private void ddlNewItem_SelectedIndexChanged(object sender,
         System.EventArgs e)
{
  // get the value of the selected item in the DropDownList
							string selectedOption = ddlNewItem.SelectedItem.Value;
							string url = "";
							// depending upon the item selected...
							// construct a URL pointing to a specific dialog page
							// and append the information about the Update mode of the current
							// container
							switch(selectedOption)
							{
							case "NewChannel":
							{
							url = "CreateChannel.aspx?"
							+ ((Channel)startItem).QueryStringModeUpdate;
							break;
							}
							case "NewPosting":
							{
							url = "CreatePosting.aspx?"
							+ ((Channel)startItem).QueryStringModeUpdate;
							break;
							}
							case "NewTemplateGallery":
							{
							url = PrepareUrl(startItem, PublishingMode.Update,
                      "CreateTemplateGallery.aspx");
							break;
							}
							case "NewTemplate":
							{
							url = PrepareUrl(startItem, PublishingMode.Update,
                       "CreateTemplate.aspx");
							break;
							}
							case "NewResource":
							{
							url = PrepareUrl(startItem, PublishingMode.Update,
                       "CreateResource.aspx");
							break;
							}
							}
							// if a URL was generated, register a JavaScript block to open
							// a new window with the specified URL and reset the dropdownlist
							if (url != "")
							{
							// register the javascript
							string script = "";
							script += "<script language=\"javascript\">";
							script += "window.open('" + url + "');";
							script += "</script>";
							Page.RegisterClientScriptBlock("CreateNewItem",script);
							// reset the dropdownlist
							ddlNewItem.SelectedIndex = 0;
							}
}

Now we need to initialize the toolbar by adding the options as shown in the table below to the ddlNewItem DropDownList. These options will be specific to the type of the startItem and our code will ensure that these will only show up if the user has the appropriate permissions.

StartItem Type

Options Added

Channel

New Channel

New Posting

TemplateGallery

New Template Gallery

New Template

ResourceGallery

New Resource

For example, the options “New Channel” and “New Posting” will be added if the startItem is a channel and if the user has rights to create channels and postings within the parent channel.

Below ddlNewItem_SelectedIndexChanged() add the following PrepareToolbar() method, which inserts the options in the drop-down list:

private void PrepareToolbar()
{
  // remove any pre-existing options from the DropDownList
  ddlNewItem.Items.Clear();
  ddlNewItem.Items.Add(new ListItem("New",""));

  ListItem li = null;

  if (startItem is Channel)
  {
    Channel currentChannel = startItem as Channel;

    // if the user has rights to create channels, add option to create
    // a new channel
    if (currentChannel.CanCreateChannels)
    {
      li = new ListItem("New Channel","NewChannel");
      ddlNewItem.Items.Add(li);
    }

    // if the user has rights to create postings, add option to create
    // a new posting
    if (currentChannel.CanCreatePostings)
    {
      li = new ListItem("New Posting","NewPosting");
      ddlNewItem.Items.Add(li);
    }
    imgTitle.ImageUrl = "images/channelopen_big.gif";
  }

  else if (startItem is TemplateGallery)
  {
    TemplateGallery templateGallery = startItem as TemplateGallery;

    // if the user has rights to create template galleries, add option
    // to create a new template gallery
    if (templateGallery.CanCreateTemplateGalleries)
    {
       li = new ListItem("New Template Gallery",
           "NewTemplateGallery");
       ddlNewItem.Items.Add(li);
    }

    // if the user has rights to create templates, add option to create
    // a new template
    if (templateGallery.CanCreateTemplates)
    {
       li = new ListItem("New Template","NewTemplate");
       ddlNewItem.Items.Add(li);
    }
    imgTitle.ImageUrl = "images/templategalleryopen_big.gif";
  }

  else if (startItem is ResourceGallery)
  {
    ResourceGallery resourceGallery = startItem as ResourceGallery;

    // if the user has rights to create resources, add option to create
    // a new resource
    if (resourceGallery.CanCreateResources)
    {
       li = new ListItem("New Resource","NewResource");
       ddlNewItem.Items.Add(li);
    }
    imgTitle.ImageUrl = "images/resourcegalleryopen_big.gif";
  }
}

Next, add the following line inside the if (!Page.IsPostBack) code block at the end of the Page_Load() event handler:

							. . . code continues . . .

  if (!Page.IsPostBack)
  {
    // display the publishing mode
    lblPublishingMode.Text = "Publishing Mode: "
                           + cmsContext.Mode.ToString();

    // use the start channel's display name as the
    // header for the page
    litCurrentContainer.Text = startItem.Name;

    // initialize the toolbar based on the current startItem
							PrepareToolbar();
  }

The Channels Button

The Channels button in the CMS Explorer UI allows the user to browse through the channel structure to inspect channel and posting objects.

In Design view, double-click on the btnChannels LinkButton. When the btnChannels button is clicked, we will simply refresh the page to show the contents of the root channel. This is simply achieved by redirecting back to the default.aspx page.

private void btnChannels_Click(object sender, System.EventArgs e)
{
  Response.Redirect("default.aspx");
}

The Templates Button

The Templates button enables the user to browse the template gallery structure and view template gallery and template objects.

In Design view, double-click on the btnTemplates LinkButton. The btnTemplates button brings the user to the root Template Gallery. We use the PrepareUrl() method to get the correct URL and querystrings:

private void btnTemplates_Click(object sender, System.EventArgs e)
{
  string url;
							url = PrepareUrl(cmsContext.RootTemplateGallery,
                   PublishingMode.Unpublished, "default.aspx");
							Response.Redirect(url);
}

The Resources Button

The Resources button lets the user browse through the resource gallery structure to inspect resource gallery and resource objects.

In Design view, double-click on the btnResources LinkButton. The btnResources button brings the user to the root Resource Gallery. We use the PrepareUrl() method to get the correct URL and querystrings.

private void btnResources_Click(object sender, System.EventArgs e)
{
  string url;
							url = PrepareUrl(cmsContext.RootResourceGallery,
                   PublishingMode.Unpublished, "default.aspx");
							Response.Redirect(url);
}

The Completed User Interface

When you are done, save and build the solution. The user interface for CMS Explorer is complete! Click on the display name of Channels to drill down deeper into the hierarchy. Click the Up button to move up a level. Select the Edit button to reveal a set of actions that can be performed on each channel or posting.