Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Advanced Microsoft Content Management Server Development
Advanced Microsoft Content Management Server Development

Advanced Microsoft Content Management Server Development: Working with the Publishing API, Placeholders, Search, Web Services, RSS, and Sharepoint Integration

eBook
€8.99 €39.99
Paperback
€48.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Advanced Microsoft Content Management Server Development

Chapter 1. Building CMS Explorer

The Building Websites with Microsoft Content Management Server book (Packt Publishing, January 2005, ISBN 1-904811-16-7) makes extensive use of MCMS’s Publishing Application Programming Interface (PAPI). We show how to use it to provide custom functionality within template files, to add business processes to workflow events, to tailor the Web Author Console, and to implement forms authentication for the Tropical Green site, which the reader builds as they progress through the book.

The PAPI is in fact a huge library. You could code with it for months and still find new tricks you never knew existed! This is the first of three chapters that compliment the understanding you will have gained from the book and attempt to take your understanding of the PAPI to another level. Follow along as we demonstrate several highly useful techniques and show how they can be leveraged in a real-world scenario, as we apply them to the Tropical Green site.

Note

Where can I download a copy of the Tropical Green website?

The code files for the Tropical Green website created over the course of the earlier book are available as a download package on this book’s download page. Go to the Packt website at http://www.packtpub.com/support/ , and choose Advanced Microsoft Content Management Server Development in the dropdown.

A Central Administrative Tool: CMS Explorer

We put some serious thought into creating an example that would not only give you a thorough grounding in the more advanced methods available in the PAPI but would also leave you with a tool that you will find handy in your day-to-day MCMS work. From our own experiences as MCMS developers working in the time-critical world of the software industry, one thing that we have found invaluable has been a custom MCMS administrative tool. In the first three chapters of this book, we walk you through the process of building such a tool, which we will name CMS Explorer.

Here’s how CMS Explorer will look once completed:

The interface is made up of two sections:

  • At the top of the page (in case you hadn’t guessed, we’re going to create the tool as a web application), you’ll see a toolbar. The toolbar provides a drop-down list with options to create new postings and channels. It also has three buttons: one to toggle to a list of channels and postings, a second to list template galleries and templates, and a third for resource galleries and resources.

  • The second half of the page is a DataGrid. The grid lists the items in the current container. Each row has an Edit button, which reveals a list of actions for each object when clicked.

For navigation, you can move in two directions: click on the name of the container to see what’s in it, or use the Up button on the toolbar to move up one level.

Why build a tool when the out-of-the box-solution provides not one, but three tools to manage MCMS objects? There’s already a Site Manager and the Web Author as well as the Template Explorer available within Visual Studio .NET. There are several reasons why building the CMS Explorer tool is worthwhile:

  • Firstly of course, you’ll get first-hand experience in using many of the more advanced methods from the PAPI. After building this tool, you will not only be very comfortable with the PAPI but also well on your way to becoming an expert in it!

  • Although the PAPI contains a large collection of classes, it doesn’t cover everything. While it would be nice for the CMS Explorer to be able to do everything that the tools shipped with MCMS can do, it can’t go beyond what’s available in the PAPI. One of the secondary objectives of the next few chapters is to highlight the PAPI’s limitations.

  • Finally, this tool could quite likely be useful in your daily work. There are some actions that can only be done using Site Manager, some that are available only within Web Author, and others exclusive to Template Explorer. For example, you would use Site Manager to create a channel and switch over to Web Author to create postings within it. CMS Explorer attempts to fill in this gap by providing as much functionality as possible from a single location.

A Central Administrative Tool: CMS Explorer


We put some serious thought into creating an example that would not only give you a thorough grounding in the more advanced methods available in the PAPI but would also leave you with a tool that you will find handy in your day-to-day MCMS work. From our own experiences as MCMS developers working in the time-critical world of the software industry, one thing that we have found invaluable has been a custom MCMS administrative tool. In the first three chapters of this book, we walk you through the process of building such a tool, which we will name CMS Explorer.

Here’s how CMS Explorer will look once completed:

The interface is made up of two sections:

  • At the top of the page (in case you hadn’t guessed, we’re going to create the tool as a web application), you’ll see a toolbar. The toolbar provides a drop-down list with options to create new postings and channels. It also has three buttons: one to toggle to a list of channels and postings, a second to list template galleries and templates, and a third for resource galleries and resources.

  • The second half of the page is a DataGrid. The grid lists the items in the current container. Each row has an Edit button, which reveals a list of actions for each object when clicked.

For navigation, you can move in two directions: click on the name of the container to see what’s in it, or use the Up button on the toolbar to move up one level.

Why build a tool when the out-of-the box-solution provides not one, but three tools to manage MCMS objects? There’s already a Site Manager and the Web Author as well as the Template Explorer available within Visual Studio .NET. There are several reasons why building the CMS Explorer tool is worthwhile:

  • Firstly of course, you’ll get first-hand experience in using many of the more advanced methods from the PAPI. After building this tool, you will not only be very comfortable with the PAPI but also well on your way to becoming an expert in it!

  • Although the PAPI contains a large collection of classes, it doesn’t cover everything. While it would be nice for the CMS Explorer to be able to do everything that the tools shipped with MCMS can do, it can’t go beyond what’s available in the PAPI. One of the secondary objectives of the next few chapters is to highlight the PAPI’s limitations.

  • Finally, this tool could quite likely be useful in your daily work. There are some actions that can only be done using Site Manager, some that are available only within Web Author, and others exclusive to Template Explorer. For example, you would use Site Manager to create a channel and switch over to Web Author to create postings within it. CMS Explorer attempts to fill in this gap by providing as much functionality as possible from a single location.

Creating the Workspace


Let’s start by creating a work area for the CMS Explorer tool. Create a new Visual C# MCMS Web Application Project in Visual Studio .NET.

  1. 1. Name the new project CMSExplorer.

  2. 2. Get the Styles.css file from the book’s code download. Select Project | Add Existing Item and add it to the CMSExplorer project.

  3. 3. Create a new folder and name it images. Download the image files for this tutorial from the code download section of the book’s companion website and add them to the project.

  4. 4. Right-click the CMSExplorer project in the Solution Explorer and select Properties. Click Designer Defaults. Set the Page Layout field to Flow. This will set the default layout to flow instead of grid for all web forms created in the project. Click OK.

  5. 5. Right-click the Console folder and select Delete.

  6. 6. Add a new web form to the CMSExplorer project, and name it default.aspx.

  7. 7. In Design view, drag and drop the Styles.css file from Solution Explorer onto the form. This applies the stylesheet to the page.

  8. 8. Switch to HTML view. Add the table below between the <form> tags to provide the basic structure of the page. We use a litCurrentContainer Literal control to display the name of the current container. The lblPublishingMode Label will be used later to display the current publishing mode.

    <table cellSpacing="0" cellPadding="0">
    <tr>
      <td>
        <table>
        <tr>
          <td valign="top">
            <asp:Image runat="server" ID="imgTitle"></asp:Image>
          </td>
          <td valign="center">
            <h1>
              <asp:Literal ID="litCurrentContainer" runat="server"/>
            </h1>
          </td>
        </tr>
        </table>
        <asp:Label ID="lblPublishingMode" runat="server"
             CssClass="BodyText"/>
      </td>
    </tr>
    
    <tr>
      <td width="100%" bgcolor="#cccccc">(Space for Toolbar)</td>
    </tr>
    
    <tr>
      <td>(Space for DataGrid)</td>
    </tr>
    </table>
  9. 9. Toggle to Design view. Double-click on the form to get to its code-behind file. Above the namespace declaration, import the Microsoft.ContentManagement.Publishing namespace.

    							//MCMS PAPI
    							using Microsoft.ContentManagement.Publishing;
    
    namespace CMSExplorer
    {
      /// <summary>
      /// Summary description for _default.
      /// </summary>
      public class _default : System.Web.UI.Page
      {
    
        . . . code continues . . .
      }
    }

The Four MCMS Publishing Modes


MCMS uses four different publishing modes:

  • Published:Mode used for displaying a live version of the site

  • Staging:Mode used for staging the site using Site Stager

  • Unpublished:Mode used for displaying an unpublished version of the site (e.g. in edit site mode or in preview screens)

  • Update:Mode used for updating the site (e.g. on the authoring screen with placeholder controls in authoring mode)

Determining the Current Publishing Mode

The current mode can be found using the CmsHttpContext.Mode property. Let’s find out which mode CMS Explorer is currently using.

Above the Page_Load() event handler in the code-behind file, add the following line:

// the current CmsHttpContext
private CmsHttpContext cmsContext;

Inside the Page_Load() event, add the following code:

private void Page_Load(object sender, System.EventArgs e)
{
   cmsContext = CmsHttpContext.Current;
						if (!Page.IsPostBack)
						{
						// display the publishing mode
						lblPublishingMode.Text = "Publishing Mode: "
						+ cmsContext.Mode.ToString();
						}
}

Save and build the solution. Navigate to http://localhost/CMSExplorer/default.aspx. Notice that the label says Publishing Mode: Published. When you first view a web page on an MCMS site, you are shown the site in its Published mode. You can ignore the broken image for now as we’ll address that further along.

In Published mode, you only have access to channels and postings that have live versions and that are not marked as hidden. Channels that have expired or have their start dates set to a future date will not be available. Postings that have never been published before or are in a “Waiting For Moderator Approval”, “Waiting For Editor Approval”, “Submitted”, “Approved” or “Expired” state will also not be accessible. Obviously, for CMS Explorer to be useable, it’s got to be able to see all objects regardless of their states. In order to work with unpublished objects, we have to change the current publishing mode from Published to Unpublished, and we look at ways to accomplish this in the following sections.

Changing the MCMS Publishing Mode

There are various ways to change the MCMS publishing mode, such as by modifying the querystring parameters in the URL or by manipulating the modes via CmsHttpContext and CmsApplicationContext. Let’s take a look at each of these methods.

The Ugly URL Querystring Specifies the Publishing Mode

Let’s try a little experiment.

  1. 1. Open your browser and navigate to the http://localhost/tropicalgreen site.

  2. 2. Log in as an administrator. Click on the Switch to Edit Site button and observe the URL displayed in the browser’s address bar. It changes from a friendly URL to an ugly long URL containing the familiar querystring parameters at its tail end:

    									http://localhost/NR/exeres/71EDAD1D-9D58-4D65-8069-19DFC0114F54.htm?
    NRMODE=Unpublished
    &WBCMODE=PresentationUnpublished
    &wbc_purpose=Basic

At the same time, the Switch To Edit Site button disappears and a Switch To Live Site button appears in its place.

Now, let’s make a few changes to the querystring. With the page open in Unpublished mode:

  1. 1. Change the NRMODE querystring parameter of the ugly URL from Unpublished to Published.

  2. 2. Delete the WBCMODE querystring parameter.

  3. 3. The URL at the address bar now looks something like this:

    									http://localhost/NR/exeres/71EDAD1D-9D58-4D65-8069-19DFC0114F54.htm?
    NRMODE=Published&wbc_purpose=Basic
  4. 4. Click the Go button next to the address bar of the browser.

    Notice that the Switch To Live Site button changes back to the Switch To Edit Site button! You have effectively changed from Unpublished mode back to Published mode.

This test shows how publishing modes in MCMS can be controlled by playing around with the querystring of the generated ugly URL.

Toggling Modes with CmsHttpContext

When building your application, instead of messing around with the URLs, you can generate the querystrings for each mode on the fly using two properties of the ChannelItem object:

  • QueryStringModeUpdate for working in Update mode

  • QueryStringModeUnpublished for working in Unpublished mode

We will use this technique in CMS Explorer to switch from Published mode to Unpublished mode.

In order to get the QueryStringModeUnpublished property, we first need to get a reference to any ChannelItem. In this example, we use the root channel. If we are not in Unpublished mode, the page redirects to itself with the querystring returned by the QueryStringModeUnpublished property appended to its address. Modify the code in the Page_Load() event handler as follows:

private CmsHttpContext cmsContext;

private void Page_Load(object sender, System.EventArgs e)
{
  cmsContext = CmsHttpContext.Current;

  // Redirect if not in unpublished mode
							if (cmsContext.Mode != PublishingMode.Unpublished
							&& cmsContext.Mode != PublishingMode.Update)
							{
							string query;
							query = cmsContext.RootChannel.QueryStringModeUnpublished;
							Response.Redirect("default.aspx?" + query);
							}

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

Save and build the solution. Navigate to http://localhost/CMSExplorer/default.aspx again. Notice that the label now says Publishing mode: Unpublished. We have successfully toggled to Unpublished mode!

The drawback of using CmsHttpContext to toggle between modes is that it requires you to first get a reference to a ChannelItem object as well as a client redirect. For this example, we used the root channel. If the user does not have rights to the root channel, the code fails.

Note

How can I toggle to Update mode?

To toggle to Update mode, simply use the ChannelItem.QueryStringModeUpdate property instead, like so:

if (CmsContext.Mode != PublishingMode.Update)
{
   Response.Redirect("default.aspx?"
     + CmsHttpContext.Current.RootChannel.QueryStringModeUpdate);
}

Toggling Modes with CmsApplicationContext

Another popular method of toggling between modes leverages the CmsApplicationContext object. This object is typically used for stand-alone applications that run outside IIS, such as console and desktop applications. In these cases, the CmsHttpContext is meaningless and can’t be used.

You can also use the CmsApplicationContext object within a web application when you require additional CmsContext objects, especially when working with different modes. You can maintain CmsHttpContext in Published mode, and have a separate CmsApplicationContext in Update mode. Another advantage to using CmsApplicationContext is that it reduces the number of client round trips required.

We won’t be using CmsApplicationContext in the CMS Explorer application. Nevertheless, no lesson on mode switching is complete without introducing the class.

To use the CmsApplicationContext object, first create a new instance of it:

// Create a new CmsApplicationContext
CmsApplicationContext cmsContext = new CmsApplicationContext();

Unlike CmsHttpContext, CmsApplicationContext must be authenticated with the MCMS server using one of four authentication methods. Each authentication method accepts an input parameter of type PublishingMode specifying the mode you wish to work in.

  • AuthenticateAsCurrentUser

    Authenticates using the credentials of the currently logged-on user. This method does not work correctly from within a web application. It is used only when running the application outside of IIS, e.g. from a console application, because it uses the process token. For web applications, using the process token means that the currently logged on user is the user configured in the machine.config file (in IIS 5) or the application pool account (in IIS 6) instead of the user that has been authenticated in CmsHttpContext (which uses the thread token).

    To authenticate as the current user:

    // authenticate as the current user
    cmsContext.AuthenticateAsCurrentUser(PublishingMode.Unpublished);
  • AuthenticateAsGuest

    Authenticates using the guest account specified in the SCA. Works only if you have guest access turned on.

    To authenticate as the guest user:

    // authenticate as Guest user
    cmsContext.AuthenticateAsGuest(PublishingMode.Published);
  • AuthenticateAsUser

    This method accepts at least two parameters: the user ID and the password. The user ID is always in the format WinNT://domain/UserId. The password has to be passed in as a string.

    To authenticate with a specified user ID:

    // specify the user ID, password and publishing mode
    cmsContext.AuthenticateAsUser("WinNT://domain/UserId",
                                  "password",PublishingMode.Unpublished);
  • AuthenticateUsingUserHandle

    Authenticates using a Windows token by passing in the token of the currently logged on Windows user. This method has the advantage of not requiring the developer to code a password and is often used within web applications. However, if your chosen authentication mechanism is Forms Authentication, this method will not work as Windows tokens are not issued in that case.

    To authenticate with the Windows token of the currently logged on user:

    // get a Windows token of the currently logged on user
    System.Security.Principal.WindowsIdentity ident;
    ident = HttpContext.Current.User.Identity as
       System.Security.Principal.WindowsIdentity;
    
    CmsApplicationContext cmsContext = new CmsApplicationContext();
    cmsContext.AuthenticateUsingUserHandle(ident.Token,
                                     PublishingMode.Unpublished);

Once authenticated, you can use the Searches object to retrieve objects as you would with CmsHttpContext. The objects you access with CmsApplicationContext will be presented in the mode that you specify.

Adding Querystring Parameters to the URL with CmsHttpContext.PropagateParameter()

We are going to do lots of toggling between modes in our CMS Explorer application. To make things easier, we will write a helper function called PrepareUrl() that will use the CmsHttpContext object to generate a URL to change the publishing mode. The PrepareUrl() method will accept three input parameters:

  • hItem: HierarchyItem object the user has selected to work with. It could be the start container or any of its child items.

  • Mode:Publishing mode to work in. To list both published and unpublished content, we need to use Unpublished mode. To modify object property values, you need to be in Update mode.

  • pageName:Name of the dialog or page to open.

The method returns the URL, which is made up of the pageName appended with the QueryStringModeUnpublished or QueryStringModeUpdate property of the root channel. The generated querystring contains the GUID of the current channel or posting somewhere in the URL but holds no information about template galleries and resource galleries. To get around this, we add more information by introducing two additional parameters:

  • CMSObject:Contains a string with value “Templates” or “Resources”.

  • CMSObjectGuid:Stores the GUID of the template gallery or resource gallery selected by the user as the start container.

The CmsHttpContext.PropagateParameter() method inserts these two parameters into all URLs generated by MCMS within the session. Add PrepareUrl() directly below the Page_Load() event handler:

private string PrepareUrl(HierarchyItem hItem, PublishingMode mode,
                          string pageName)
{
  string url = "";
  if (hItem != null)
  {
    string cmsObject = "";
    if (hItem is TemplateGallery)
    {
      cmsObject = "Templates";
    }
    else if (hItem is ResourceGallery)
    {
      cmsObject = "Resources";
    }
    cmsContext.PropagateParameter("CMSObject",cmsObject);
    cmsContext.PropagateParameter("CMSObjectGuid",
                         HttpUtility.UrlEncode(hItem.Guid));
    url = pageName + "?";

    if (mode == PublishingMode.Unpublished)
    {
      url += cmsContext.RootChannel.QueryStringModeUnpublished;
    }
    else if (mode == PublishingMode.Update)
    {
      url += cmsContext.RootChannel.QueryStringModeUpdate;
    }
  }
  return url;
}

The next time a URL is requested from the ChannelItem.Url property (or any of the properties that generate URLs), the querystring includes the two additional parameters:

http://localhost/cmsexplorer/default.aspx?
CMSObject=Templates&

NRMODE=Unpublished&
FRAMELESS=true&
CMSObjectGuid=%7b4D1912B-9DD3-11D1-B44E-
							006097071264%7d&NRNODEGUID=%7bE4D19123-9DD3-11D1-B44E-006097071264%7d
						

PrepareUrl() will be used to generate URLs later as we work through the CMS Explorer code.

Specifying the Parent Container


In this example we plan to use a DataGrid to display a list of all the objects in the selected container. However, before we can get the DataGrid to display a list of objects, we need to specify a parent container. The parent container will be a channel, resource gallery, or template gallery. We’ll need this container to determine the objects to be displayed in the DataGrid. If the user hasn’t specified a parent container, we use the root containers (i.e. the Channels channel, the Templates template gallery, and the Resources resource gallery).

Above and within the Page_Load() event handler, add the following code:

// the current CmsHttpContext
private CmsHttpContext cmsContext;

// the parent container whose contents we are displaying
					private HierarchyItem startItem;
private void Page_Load(object sender, System.EventArgs e)
{
  cmsContext = CmsHttpContext.Current;

  // Get the URL of the current page
					string currentUrl = HttpContext.Current.Request.Url.ToString();

  // Redirect if not in unpublished mode
  if (cmsContext.Mode != PublishingMode.Unpublished
       && cmsContext.Mode != PublishingMode.Update)
  {
    string query;
    query = cmsContext.RootChannel.QueryStringModeUnpublished;
    Response.Redirect(currentUrl + "?" + query);
  }

  InitializeStartItem();

  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;
  }
}

Add the InitializeStartItem() method directly below the Page_Load() event handler:

private void InitializeStartItem()
{
  // determine the object type
  string cmsObject = "";
  if (Request.QueryString["CMSObject"] != null)
  {
    cmsObject = Request.QueryString["CMSObject"].ToString();
  }

  // determine the GUID of the working container
  string cmsObjectGuid = "";
  if (Request.QueryString["CMSObjectGuid"] != null)
  {
    cmsObjectGuid = Request.QueryString["CMSObjectGuid"].ToString();
  }

  if (cmsObjectGuid == "")
  {
    // if not specified, we start with channels
    startItem = cmsContext.Channel;
  }
  else
  {
    startItem = cmsContext.Searches.GetByGuid(cmsObjectGuid);
  }

  // no working container has been specified. Use the root containers
  if (startItem == null)
  {
    switch(cmsObject)
    {
      case "Templates":
        // using the root template gallery
        startItem = cmsContext.RootTemplateGallery;
        break;
      case "Resources":
        // using the root resource gallery
        startItem = cmsContext.RootResourceGallery;
        break;
      default:
        // using the root channel
        startItem = cmsContext.RootChannel;
        break;
    }
  }
}

The code first determines the type of objects you are working with: channels, template galleries, or resource galleries. It gets this information from the CMSObject querystring parameter.

For channels, the logic is straightforward. Information about the channel is available from the CmsHttpContext.Channel property. If it isn’t null, the DataGrid uses that as the start channel. Otherwise, the root container is assigned to startItem.

For template galleries and resource galleries, the current gallery item can’t be obtained from the current CmsHttpContext. For these objects, the PAPI gets the GUID of the working container from the CMSObjectGuid querystring parameter we inserted earlier.

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.

Using Reflection to List Properties and their Values


In the Properties dialog, we are going to list all properties of the selected object. Usually, when you want to access a property value in code, you simply type the object name, followed by the period key and then the property name.

This is fine when you are just dealing with one or two properties, but there are over 40 properties for the channel object alone. In order to display all its property values in this way, you would have to type in at least 40 lines of code. And should there be future upgrades to the PAPI, the list may grow even longer. The good news is there’s a short cut—.NET Reflection.

Reflection is a technique used to access information about a class, such as its methods, properties, events and even information about its assembly. To implement reflection, use the GetType() method of the object (inherited from System.Object), which returns an object of type System.Reflection.Type. The System.Reflection.Type class contains methods that retrieve metadata about the object’s class. For example, given any object, you can get a list of its methods by calling:

MyObject.GetType().GetMethods();

and to get a list of its properties, you could call:

MyObject.GetType().GetProperties();

As you can see, reflection is a powerful feature. In this example, instead of writing 40+ lines of code to list the properties of a ChannelItem object, we will simply use reflection to iterate through each property and display its value.

Let’s display the list of properties in a DataGrid. To start, add a new web form to the CMSExplorer project. Name the new web form Properties.aspx. Toggle to HTML view and add a couple of headings to the form to describe what it does:

<form>
<h1>Properties of <asp:Literal Runat="server" ID="litCurrentItem"/></h1>
<h2>List of all Properties and their values</h2></form>

In the web form’s code-behind file, import the Microsoft.ContentManagement.Publishing and System.Reflection namespaces and add the following code in the Page_Load() event handler:

. . . code continues . . .
// MCMS PAPI
using Microsoft.ContentManagement.Publishing;
// for reflection
using System.Reflection;

namespace CMSExplorer
{
  /// <summary>
  /// Summary description for Properties.
  /// </summary>
  public class Properties : System.Web.UI.Page
  {
    HierarchyItem hItem; // the current item

    private void Page_Load(object sender, System.EventArgs e)
    {
      CmsHttpContext cmsContext = CmsHttpContext.Current;
hItem = null;
string cmsObjectGuid = "";
if (Request.QueryString["CMSObjectGuid"]!=null)
{
// template gallery items and resource gallery items
cmsObjectGuid = Request.QueryString["CMSObjectGuid"];
hItem = cmsContext.Searches.GetByGuid(cmsObjectGuid);
}
else
{
// channels and postings
hItem = cmsContext.ChannelItem;
}
// list all properties and their values in the grid
if (hItem!=null)
{
litCurrentItem.Text = hItem.Path;
ListProperties();
}
    }
  . . . code continues . . .
  }
}

The code gets a reference to the HierarchyItem whose properties you wish to view. For template galleries, templates, resource galleries, and resources, this is obtained by getting the GUID from the CMSObjectGuid querystring parameter and using the Searches.GetByGuid() method. Channels and postings are obtained from the CmsHttpContext.Current.ChannelItem property.

In Design view, drag and drop the Styles.css file and DataGrid onto the Properties.aspx web form and give the DataGrid the following property values:

Property

Value

Auto Format

Simple 3

Font-Size

10pt

ID

DataGrid1

Below the Page_Load() event handler, add the ListProperties() method. The method first creates a DataTable containing the following columns:

  • The property name

  • The property value

  • Whether or not the property can be written to

We use the GetType.GetProperties() method to retrieve a collection of all properties associated with the hierarchy item. Next, iterate through each property of the current ChannelItem and add each one as a row to the DataTable. Notice that the property value is obtained by calling PropertyInfo.GetValue() and passing in the hierarchy item as an input parameter. Finally, we bind the DataTable to the DataGrid.

private void ListProperties()
{
  // display the property names and values for the current channelitem
  DataTable dt = new DataTable();
  DataRow dr;

  // add columns for the property name, property value and
  // the boolean that indicates if the property is writable
  dt.Columns.Add(new DataColumn("PropertyName"));
  dt.Columns.Add(new DataColumn("PropertyValue"));
  dt.Columns.Add(new DataColumn("CanWrite"));

  // use reflection to iterate through a list of the object's properties
  foreach(PropertyInfo pi in hItem.GetType().GetProperties())
  {
    if (pi.PropertyType.ToString().StartsWith("System"))
    {
      dr = dt.NewRow();
      dr[0] = pi.Name;
      Object piObject = pi.GetValue(hItem, null);
      if (piObject!=null)
      {
        dr[1] = piObject.ToString();
      }
      dr[2] = pi.CanWrite.ToString();
      dt.Rows.Add(dr);
    }
  }

  // bind the datatable to the datagrid
  DataGrid1.DataSource = dt.DefaultView;
  DataGrid1.DataBind();
}

When you are done, save and build the solution. To see the code in action:

  1. Access http://localhost/CmsExplorer.

  2. Click on the Edit button on the row corresponding to the Tropical Green channel.

  3. Click Properties.

The Properties page opens as shown on the facing page. Notice that all properties of the Tropical Green channel are displayed! The page also works when viewing the properties of other object types like template galleries and resource galleries.

Updating Property Values


Look at the grid. Properties whose values we can modify using the PAPI have a CanWrite property value of true.

So far, we have only been reading property values and using Web Author to update them. Let’s attempt to change the Description property value using the PAPI.

In HTML view, add the code shown below (including the text markers) above the opening <asp:DataGrid> tag:

<p>
  <table>
  <tr>
    <td>Description:</td>
    <td>(Add the text box for the Description here)</td>
  </tr>
  <tr>
    <td colspan="2" align="right">
      (Add the Update button here)
      <INPUT type="button" value="Close" onclick="javascript:window.close();">
    </td>
  </tr>
  </table>
  (Add the Label for displaying error messages here)
</p>

Toggle to Design view. Drag and drop the following controls from the Web Forms section of the Toolbox and delete all text markers. We will be adding a textbox for entering the channel item’s new description, a button for saving it, and a label for showing error messages (if there are any). Arrange them as shown in the diagram below and set their properties accordingly.

Control

Property

Property Value

TextBox

ID

Rows

TextMode

txtDescription

3

MultiLine

Button

ID

Text

btnUpdate

Update

Label

ID

Text

lblErrorMessage

(empty string)

In the Page_Load() event handler, add code to read the Description of the current hierarchy item and display it on the screen.

private void Page_Load(object sender, System.EventArgs e)
{
  // Put user code to initialize the page here
  CmsHttpContext cmsContext = CmsHttpContext.Current;
  hItem = null;

  string cmsObjectGuid = "";
  if (Request.QueryString["CMSObjectGuid"] != null)
  {
    // template gallery items and resource gallery items
    cmsObjectGuid = Request.QueryString["CMSObjectGuid"];
    hItem = cmsContext.Searches.GetByGuid(cmsObjectGuid);
  }
  else
  {
    // channels and postings
    hItem = cmsContext.ChannelItem;
  }

  // list all properties and their values in the grid
  if (hItem != null)
  {
    litCurrentItem.Text = hItem.Path;
    if (!Page.IsPostBack)
					{
					txtDescription.Text = hItem.Description;
					}
    ListProperties();
  }
}

Toggle to Design mode and double-click on the btnUpdate button. In the btnUpdate_OnClick() event handler, we will write the code that updates the Description property value of the HierarchyItem based on the text entered into the textbox.

private void btnUpdate_Click(object sender, System.EventArgs e)
{
  try
					{
					// IMPORTANT: You must be in update mode for the code to work
					// update the description
					hItem.Description = txtDescription.Text;
					// commit the change
					CmsHttpContext.Current.CommitAll();
					// refresh the page to ensure that the change shows up in the
					// datagrid
					Response.Redirect(HttpContext.Current.Request.Url.ToString());
					}
					catch(Exception ex)
					{
					CmsHttpContext.Current.RollbackAll();
					// after a rollback the CMS context needs to be disposed.
					CmsHttpContext.Current.Dispose();
					lblErrorMessage.Text = ex.Message;
					}
}

Save and build the solution. Let’s test it to see if it works:

  1. 1. With the properties page open, click the Refresh button.

  2. 2. Enter TropicalGreen—Live the Sunny Side of Life in the Description field.

  3. 3. Click Update.

Look at the grid again. The description property of the TropicalGreen channel has been updated!

Summary


We have used the MCMS Publishing API (PAPI) to create the CMS Explorer and provide a web interface with many of the features of Site Manager as well as some additional ones.

In the process of building the CMS Explorer we have learned about many of the capabilities offered by the PAPI. There are many things that we could add to the CMS Explorer to make it even more valuable to your organization. In the next chapter we will demonstrate additional PAPI features by extending our CMS Explorer project, where we will add channel and posting management functionality.

Left arrow icon Right arrow icon

Key benefits

  • Learn directly from recognized community experts
  • Extensive coverage of the Publishing API (PAPI)
  • Get Sharepoint and MCMS working together
  • InfoPath, RSS and hot topics covered

Description

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

Who is this book for?

This book is written for developers who want to the skills to fully exploit the power of MCMS and SPS. The book presumes a working knowledge of MCMS, the .NET Framework and familiarity with the C# language. All the code examples are in C#.

What you will learn

  • Extensive coverage of the Publishing API (PAPI)
  • Managing Channels and Postings with the PAPI
  • Managing Templates, Template Galleries, Resources, and Users with the PAPI
  • Getting Sharepoint and MCMS to work together
  • Publishing content between MCMS and SharePoint
  • Preparing postings for search indexing
  • Creating Sharepoint Web Parts to display MCMS data
  • Creating powerful custom Placeholder Controls
  • Adding validation to Placeholder Controls
  • Combining InfoPath, Web Services and MCMS s robust content repository
  • Using RSS to syndicate content from your site, and display content from other sites
  • Staging static versions of your pages
  • A further 60 pages of invaluable MCMS tips and tricks
Estimated delivery fee Deliver to Austria

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Nov 16, 2005
Length: 544 pages
Edition : 1st
Language : English
ISBN-13 : 9781904811534
Vendor :
Microsoft
Concepts :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Austria

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Publication date : Nov 16, 2005
Length: 544 pages
Edition : 1st
Language : English
ISBN-13 : 9781904811534
Vendor :
Microsoft
Concepts :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 149.97
Business Process Execution Language for Web Services 2nd Edition
€58.99
Advanced Microsoft Content Management Server Development
€48.99
Expert Delphi
€41.99
Total 149.97 Stars icon
Banner background image

Table of Contents

13 Chapters
Building CMS Explorer Chevron down icon Chevron up icon
Managing Channels and Postings with the PAPI Chevron down icon Chevron up icon
Managing Templates, Template Galleries, and Resources Chevron down icon Chevron up icon
Preparing Postings for Search Indexing Chevron down icon Chevron up icon
Searching MCMS with SharePoint Chevron down icon Chevron up icon
Publishing Content Between MCMS and SharePoint Chevron down icon Chevron up icon
Building SharePoint Web Parts Chevron down icon Chevron up icon
Useful Placeholder Controls Chevron down icon Chevron up icon
Validating Placeholder Controls Chevron down icon Chevron up icon
Staging Static Pages Chevron down icon Chevron up icon
InfoPath with MCMS Web Services Chevron down icon Chevron up icon
MCMS and RSS Chevron down icon Chevron up icon
Essential How-Tos, Tips, and Tricks Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Full star icon 5
(3 Ratings)
5 star 100%
4 star 0%
3 star 0%
2 star 0%
1 star 0%
David Adamson Jun 18, 2006
Full star icon Full star icon Full star icon Full star icon Full star icon 5
If you've ever been involved with MCMS 2002, you will know the authors of this title, and the great work they do for the Content Management Server community. They are either Microsoft employees or MVPs and are recognised experts in CMS development.This book is next in line after the title, "Building Websites with Microsoft Content Management Server". It delves deep into the more advanced development topics on the MCMS platform. To help you understand the topics and areas presented, there is an abundance of code which is essential. The best thing about the code examples is that they are not throw away HelloWorld demonstrations, but real life applications and uses of functionality that you will more than likely adapt to use in your own implementation. That's where the experience of the authors shines through.As well as pure MCMS content, there are also a number of chapters dedicated to explaining and demonstrating Sharepoint integration points and searching (a major feature lacking from MCMS). For many company intranets, MCMS or Sharepoint are not enough on their own and must be combined to provide a complete solution. This book goes some way towards making the combination less painful.My only (selfish) criticism of this book is the timing of its release. It would have been an awesome training tool when I was getting into MCMS development!! That aside, the examples given are still very relevant for development today and will offer even the seasoned developer new tricks, give them a deeper understanding of the APIs, and provoke new ideas and thoughts on what can be achieved. Chapters on RSS enabling your sites and integrating Infopath forms to web services in MCMS are two areas that probably wouldn't have been covered a few years ago, but are now hot topics.The book also includes a number of "essential how-tos, tips and tricks" that are obviously taken from the authors' own experiences with MCMS customers. You too will have wondered how to do these things, and if you worked it out alone, would be cursing not having had this book in your collection at the time.I consider this book, along with its predecessor, `must have' guides with material for anybody involved in MCMS development. You will definitely get a lot out of them.
Amazon Verified review Amazon
Joe Capka Jun 06, 2006
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This book offers a hands-on approach to learning MCMS topics that mimic real world problems. While most books and manuals focus on the ideal or typical scenario, this book explores how to deal with the tough scenarios where the product shortcomings need to be overcome by creative and innovative solutions. Definitive answers are provided to many of the tough questions that every developer asks when delving deep into MCMS. Working code samples make up a significant portion of the book and are extremely valuable in understanding the topics being explained.A few chapters of the book focus on the integration of MCMS and SharePoint technologies which while being a failry popular topic in industry is not something that has been well documented until now. Integration of MCMS with SharePoint or RSS is viewed as a difficult task but has now been made significantly easier.This book is meant for developers that want to push MCMS past the typical scenario and get the most out of the product. It is not meant to teach MCMS but to help developers familiar with the product to get to the next level of expertise.
Amazon Verified review Amazon
Derek Strickland Jun 05, 2006
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This book starts out strong with 3 chapters fully devoted to creating a sample application using the Publishing API. While the code examples are copious they are (necessarily) somewhat redundant. The authors chose to create an administration tool as the most effective means of illustrating the Publishing API's capability. This was an effective technique in that it exposed the core of the API very quickly to the reader, as well as having the added benefit of communicating the purpose of the MCMS Server. If you are uncertain, as I was, on what problems Microsoft Content Management Server may or may not be the right solution for, this book will take you a long way towards understanding the product and its role in the platform.After finishing the baseline administration tool, the book takes a refreshing detour on the topic of search engines. Rather than going into detail I will summarize this chapter by saying this, if you need a primer on the basics of Search Engine Optimization, give this chapter a shot. I think you will like it.Next, the authors spend three chapters on SharePoint integration and configuration. If you are using SharePoint as a foundation for your product or the enabling technology for your internal portal, you should consider the benefits of integrating with MCMS or possibly using MCMS in lieu of SharePoint. My experiences with SharePoint have always reminded me of the end of a brewery tour; fraught with bloat. While SharePoint is remarkably feature-rich, it always seems that the average user either isn't interested in the features or is intimidated by them. The appealing aspect of MCMS, from my perspective, is that the Publishing API is designed to allow you to write your applications/sites your way (with some caveats), and still have the added benefit of a tool that handles the administrative duties (transactional document management). I quickly got the feeling that if my singular goal was to manage web content across any number of channels then MCMS was a nice lightweight alternative to SharePoint. In fact, I kept thinking about website design firms and wondering how a product like this could impact the efficiency of their business.The refreshing thing to learn, for me at least, was that while MCMS can and does integrate with SharePoint, SharePoint is not required. In fact the book does a fine job of illustrating how to avoid using SharePoint altogether.With SharePoint fully dealt with, the book moves on from that point to discuss the intricacies of the aforementioned caveats of implementing dynamic content, validating dynamic content, and staging static content as well. Also of note are chapters devoted to integrating InfoPath as an editing tool and integrating RSS feeds into yours site, all with full code samples.All in all, this book was enjoyable. With the exception of the unavoidable SharePoint section, the book was devoted to MCMS development and as such had a lot of example code to sift through. As a testament to this book, I think you could read the code examples alone and get an introduction to the Publishing API. One disclaimer, the example applications in this book are intentionally straight forward. All the sample code is procedural in nature. Take it for what it is, a readable set of examples. This book is not intended to address issues of application design, and if you expect that you will be sorely disappointed.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela