Creating and Deleting Objects in a Channel

To programmatically build the hierarchy illustrated in Figure 25-1, we will use the CreateChannel method of the current channel that our posting is in. We will then create a posting in one of the created channels. We will discuss, but not illustrate, the creation of a ConnectedPosting using the CreateConnectedPosting method. Using Site Manager, we will view our hierarchical masterpiece. And finally, we will delete the entire hierarchy.

CreateChannel

To successfully use the CreateChannel method, the CanCreateChannels property must return true for the referencing Channel object, and the PublishingMode must be set to Update. Ensuring that CanCreateChannels returns true isn't too difficult (see the Sufficient Rights sidebar). Getting the referencing Channel object into Update PublishingMode requires a little more work. If we skip this step, we will receive a verbose .NET error page that basically says, You must be in Update mode to do this.

Although it may sound a little more challenging at first, we will use the second approach in the Getting into Update PublishingMode sidebar for the examples that follow. It is a universal solution that can be applied in many places in the code to follow.

The next two code listings will help us break the task of creating a channel hierarchy into manageable, potentially reusable functions.

The first function will return an authenticated CmsApplication Context to the calling method. In Listing 25-1 we are using the AuthenticateUsingUserHandle authentication method, but you can use your preferred CmsApplicationContext authentication method. By encapsulating this functionality, we can simply call the GetAuthenticated CmsApplicationContext function, passing it the PublishingMode we want it to have, and we can be confident that we will successfully get a valid CmsApplicationContext in the specified mode. The comments should adequately explain what the code is doing.

NOTE: Authentication methods of the CmsApplicationContext are covered in detail in Chapter 24.


Listing 25-1 Function to get authenticated CmsApplicationContext
 private CmsApplicationContext GetAuthenticatedCmsApplicationContext   (PublishingMode cmsMode) //***************************************************************** //Create a new CmsApplicationContext and authenticate it //Pass the created Context back to the calling method //***************************************************************** {   //1. Declare a Context variable   CmsApplicationContext cmsContextApp = null;   try   {     //2. Grab a new Application Context     cmsContextApp = new CmsApplicationContext();     //3. Assign current Windows User to a WindowsIdentity variable     //   This will only work if IIS is set to Windows     //   Authentication and Guest Access is enabled in the SCA     System.Security.Principal.WindowsIdentity identCurrentUser =       System.Security.Principal.WindowsIdentity.GetCurrent();     //4. Log in to CMS     //   Use the currently authenticated Windows User credentials     //   Put Context into the PublishingMode passed to the function     cmsContextApp.AuthenticateUsingUserHandle(       identCurrentUser.Token, cmsMode);     //5. Return the Authenticated Context     return cmsContextApp;   }   catch(Exception eError)   {     //6. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();     //7. Return the null Context in the event of an error     return cmsContextApp;   } } 

The second function, in Listing 25-2, creates a Channel object and returns it to the calling method. By encapsulating this functionality, we can simply call the CreateNewChannel function, passing it the Channel object in which we want the new channel created, along with the name we want the new channel to have, and we can be confident that, assuming sufficient user rights exist, we will successfully get back the created Channel object. The comments should adequately explain what the code is doing.

Getting into Update PublishingMode

There are at least two ways to get a CMS Context into Update PublishingMode:

  1. Navigate or redirect the posting to the URL that puts the authenticated user's CmsHttpContext into an Update PublishingMode. In an earlier example in this chapter, we included an unexplained anchor tag called Link to Update Mode. We constructed the link using a Channel property called UrlModeUpdate that we talk about later in this chapter. Run that sample and try clicking that link, and then click the Button. You will see that the PublishingMode changes from Published to Update. Alternatively, redirection could be used to put the CmsHttpContext into Update PublishingMode. See the following code for an example of how this might be accomplished (the Woodgrove sample uses this approach):

     if(cmsContext.Mode != PublishingMode.Update) {   Response.Redirect(cmsContext.Channel.UrlModeUpdate, true); } else {   // Create Channel here } 
  2. Use the CmsApplicationContext to authenticate into a stand-alone Context in Update PublishingMode, as we did in Chapter 24. However, the CmsApplicationContext will not be privy to the current channel, so an extra step will be required to position this stand-alone Context on the channel where we want to create our new channel. The steps in this approach include creating and authenticating a new CmsApplication Context in Update PublishingMode, positioning the CmsApplication Context to the current CmsHttpContext Channel, creating our Channel(s), committing our changes, and disposing of the CmsApplication Context.

It is good to understand both methods, but standardizing on a single method and using it consistently across the entire site is wise.


If the CreateNewChannel function fails for any reason, the Channel object passed back will simply be null.

Listing 25-2 Function to create a new channel
 private Channel CreateNewChannel   (Channel parentChannel, string newChannelName) //***************************************************************** //Create a new Channel in the parentChannel using the //newChannelName both passed to the function //Pass the created Channel back to the calling method //***************************************************************** {   //1. Declare a Channel variable   Channel cmsNewChannel = null;   try   {     //2. Determine if the user has sufficient rights to create     // a Channel from the would-be parent Channel     if(parentChannel.CanCreateChannels)     {     //3. Create the Channel     cmsNewChannel = parentChannel.CreateChannel();     //4. Validate successful creation     if(cmsNewChannel != null)     {       //5. Give it the Name passed to the function       cmsNewChannel.Name = newChannelName;       //6. Provide visual feedback of Channel creation in Listbox1       ListBox1.Items.Add(newChannelName + " Channel created in " +         parentChannel.Name.ToString() + " Channel"         );     }     else     {       //7. Provide visual feedback of creation failure in Listbox1       ListBox1.Items.Add(newChannelName +         " Channel creation failed");     }     }     else     {     //8. Provide nonerror feedback to the developer     Label1.Text = "<b>User NOT allowed to create Channels";     }     //9. Return the created Channel     return cmsNewChannel;   }   catch(Exception eError)   {     //10. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();     //11. Return the null Channel in the event of an error     return cmsNewChannel;   } } 

A new channel is not saved to the database until the CommitAll method is called on the Context. The default values for properties are either empty, inherited from the parent channel (including rights), or intuitive. Also, CMS allows the creation of duplicate channels even though this can lead to a situation where ambiguous objects in the hierarchy are created. Of course, they will have different GUIDs, so CMS won't get confused, but it may be confusing to users. So it may be wise to prevent duplicates programmatically.

Now let's use these two functions, along with some elementary PAPI code, to create the channel hierarchy depicted in Figure 25-1. Ensure that the code from Listings 25-1 and 25-2 is entered above the Button1_Click function of our Scratchpad template file.

Replace the Button1_Click function of our Scratchpad template file with the following code:

 private void Button1_Click(object sender, System.EventArgs e) {   try   {     //1. Grab the current CMS Context     CmsHttpContext cmsContextHttp = CmsHttpContext.Current;     //2. Grab an Authenticated Context in Update PublishingMode     //   using the GetAuthenticatedCmsApplicationContext function     //   from Listing 25 1     CmsApplicationContext cmsContextApp =       GetAuthenticatedCmsApplicationContext(PublishingMode.Update);     //3. Position the Application Context to the current     //   CmsHttpContext Channel using its GUID (the most efficient     //   Searches method)     //   Cast the result of the Searches object as a Channel     Channel currentChannel =       cmsContextApp.Searches.GetByGuid(cmsContextHttp.Channel.Guid)       as Channel;     //4. Populate the label with the name of the Application     //   Context Channel and PublishingMode     Label1.Text = "<b>Channel: </b>" +       currentChannel.Name.ToString() +       "<br><b>PublishingMode: </b>" +       cmsContextApp.Mode.ToString();     //5. Create the Channel hierarchy depicted in Figure 25 1     //   using the CreateNewChannel function from Listing 25 2     Channel FamilyChannel;     Channel ParentChannel;     Channel ChildChannel;     familyChannel = CreateNewChannel(currentChannel, "Brady");     parentChannel = CreateNewChannel(familyChannel,  "Alice");     parentChannel = CreateNewChannel(familyChannel, "Carol");     childChannel  = CreateNewChannel(parentChannel,  "Jan");     childChannel  = CreateNewChannel(parentChannel,  "Marsha");     childChannel  = CreateNewChannel(parentChannel,  "Cindy");     parentChannel = CreateNewChannel(familyChannel, "Mike");     childChannel  = CreateNewChannel(parentChannel,  "Greg");     childChannel  = CreateNewChannel(parentChannel,  "Peter");     childChannel  = CreateNewChannel(parentChannel,  "Bobby");     //6. Commit of all changes. If not explicitly called, the     //   disposition of changes will be based upon     //   RollbackOnSessionEnd     cmsContextApp.CommitAll();     //7. Dispose of the stand-alone Application Context     cmsContextApp.Dispose();   }   catch(Exception eError)   {     //8. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();   } } 

NOTE: The Searches method of a CMS Context is covered in detail in Chapter 28.


Build the solution and then refresh the Scratchpad posting in Internet Explorer, or browse to it and click the Button. The page should reload and look similar to Figure 25-3.

Figure 25-3. Create channel hierarchy

graphics/25fig03.gif

Scroll down the ListBox to see all the channels created. Open Site Manager or choose Global Refresh from the Site Manager View menu if it is already open. The channel hierarchy as depicted in Figure 25-1 should be displayed.

CreatePosting

Hierarchies don't do us a lot of good unless we can put postings in them. Channel hierarchies are designed to house postings. To successfully use the CreatePosting method, the CanCreatePostings property must return true for the referencing Channel object, and the PublishingMode must be set to Update. Ensuring that CanCreatePostings returns true isn't too difficult. Remember, you must be in Update Mode to do this (see the Getting into Update PublishingMode sidebar).

A new posting is not saved to the database until the CommitAll method is called on the Context. The default values for properties are either empty or intuitive. Also, CMS allows the creation of duplicate postings. This can lead to a situation where ambiguous objects in the hierarchy are created. Of course, they will have different GUIDs, so CMS won't get confused, but it may be confusing to users. So it may be wise to prevent duplicates programmatically.

Similar to the CreateNewChannel function in Listing 25-2 is the CreateNewPosting function in Listing 25-3. We can simply call the CreateNewPosting function, passing it the Channel object in which we want the new posting created, the name we want the new posting to have, as well as the template we want to base the new posting on, and we can be confident that, assuming sufficient user rights exist, we will successfully get back the created Posting object. The comments should adequately explain what the code is doing. If the CreateNewPosting function fails for any reason, the Posting object that is passed back will simply be null.

Listing 25-3 Function to create a new posting
 private Posting CreateNewPosting   (Channel parentChannel, string newPostingName,    Template cmsTemplate) //**************************************************************** //Create a new Posting in the parentChannel using the //newPostingName based upon the cmsTemplate all passed to the //function. Pass the created Posting back to the calling method //**************************************************************** {   //1. Declare a Posting variable   Posting cmsNewPosting = null;   try   {     //2. Determine if the user has sufficient rights to create     //   a Posting from the would-be parent Channel     if(parentChannel.CanCreatePostings)     {       //3. Create the Posting using the Template passed to       //   the function       cmsNewPosting = parentChannel.CreatePosting(cmsTemplate);       //4. Validate successful creation       if(cmsNewPosting != null)       {         //5. Give it the Name passed to the function         cmsNewPosting.Name = newPostingName;         //6. Provide visual feedback of Posting creation         ListBox1.Items.Add( newPostingName +           " Posting created in " +           parentChannel.Name.ToString() + " Channel"           );       }       else       {         //7. Provide visual feedback of creation failure         ListBox1.Items.Add(newPostingName +           " Posting creation failed"           );       }     }     else     {       //8. Provide nonerror feedback to the developer       Label1.Text = "<b>User NOT allowed to create Postings";     }     //9. Return the created Posting     return cmsNewPosting;   }   catch(Exception eError)   {     //10. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();     //11. Return the null Posting in the event of an error     return cmsNewPosting;   } } 

Next we will create a posting named Sam based upon our Scratchpad template file from the Root TemplateGallery called Templates in the Alice channel (/Channels/Scratch/Brady/Alice) of the channel hierarchy created in the previous code example. Ensure that the code from Listings 25-1 and 25-3 is entered above the Button1_Click function of our Scratchpad template file and that the channel hierarchy from the previous code example is visible in Site Manager. Thus far, our posting doesn't have any placeholders to be troubled with.

Replace the Button1_Click function of our Scratchpad template file with the following code:

 private void Button1_Click(object sender, System.EventArgs e) {   try   {     //1. Grab the current CMS Context     CmsHttpContext cmsContextHttp = CmsHttpContext.Current;     //2. Grab the Template to create a Posting using its path     //   Cast the result of the Searches object as a Template     Template scratchpadTemplate =       cmsContextHttp.Searches.GetByPath(         "/Templates/ScratchpadTemplate")       as Template;     //3. Grab an Authenticated Context in Update PublishingMode     //   using the GetAuthenticatedCmsApplicationContext function     //   from Listing 25 1     CmsApplicationContext cmsContextApp =       GetAuthenticatedCmsApplicationContext(PublishingMode.Update);     //4. Grab the Channel to create a Posting using its path     //   Cast the result of the Searches object as a Channel     Channel cmsAliceChannel =       cmsContextApp.Searches.GetByPath(         "/Channels/Scratch/Brady/Alice")       as Channel;     //5. Create a new Posting called Sam in the Alice Channel     //   based upon the Scratchpad Template using the     //   CreateNewPosting function from Listing 25 3     Posting cmsPosting = CreateNewPosting(cmsAliceChannel,       "Sam",       scratchpadTemplate);     //6. If Posting was successfully created it will not be null     if(cmsPosting != null)     {     //7. Populate the label with the name of the Application     //   Context Channel, then name of the created Posting,     //   and PublishingMode     Label1.Text = "<b>Channel: </b>" +       cmsAliceChannel.Name.ToString() +       "<br><b>Posting: </b>" +       cmsPosting.Name.ToString() +       "<br><b>PublishingMode: </b>" +       cmsContextApp.Mode.ToString();     }     //8. Commit of all changes. If not explicitly called, the     //   disposition of changes will be based upon     //   RollbackOnSessionEnd     cmsContextApp.CommitAll();     //9. Dispose of the stand-alone Application Context     cmsContextApp.Dispose();   }   catch(Exception eError)   {     //10. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();   } } 

Build the solution and then refresh the Scratchpad posting in Internet Explorer, or browse to it and click the Button. The page should reload and look similar to Figure 25-4.

Figure 25-4. Create posting

graphics/25fig04.gif

Open Site Manager or choose Global Refresh from the Site Manager View menu if it is already open. Drill into the channel hierarchy until you can highlight the Alice channel. The new Sam posting should be visible in the pane to the right, as shown in Figure 25-5.

Figure 25-5. Created posting in Site Manager

graphics/25fig05.gif

CreateConnectedPosting

Sometimes it makes sense for postings to share content. We will explore this concept in detail in the next chapter. For now, just be aware of the rules: To successfully use the CreateConnectedPosting method, the CanCreatePostings property and the CanSetProperties property must both return true for the referencing Channel and Posting objects, respectively. As always, the PublishingMode must be set to Update. A new ConnectedPosting is not saved to the database until the CommitAll method is called on the Context. The default values for properties are either empty or intuitive; however, the following properties are shared between ConnectedPosting objects: Description, Name, Placeholders, and CustomProperties. CMS allows the creation of ConnectedPosting objects with duplicate names. There is also a material limit of 15 distinct Template objects for a pool of ConnectedPostings to share. Attempting to go beyond that limit will result in an exception. It may be wise to create a CreateNewConnectedPosting function to encapsulate that functionality.

The state of the newly created ConnectedPosting is dependent on the state of the existing posting and sometimes whether the channel has a moderator or not. Table 25-1 provides the matrix of state values.

Delete (Inherited from HierarchyItem)

We've created a channel hierarchy using PAPI; let's delete that hierarchy in like manner. To successfully use the Delete method, the CanDelete property must return true for the referencing HierarchyItem (Channel or Posting). See the rules for CanDelete earlier in this chapter. As before, the PublishingMode must be set to Update.

Table 25-1. Initial ConnectedPosting State

Existing Posting State

Channel Moderator

New ConnectedPosting State

New

Yes or No

New

Saved

Yes or No

Saved

Waiting For

Yes or No

Waiting For Editor Approval Editor Approval

Editor Declined

Yes or No

Editor Declined

Waiting For

No

Approved, Published, or Expired Moderator Approval

Moderator Declined

No

Approved, Published, or Expired

Approved

No

Approved, Published, or Expired

Published

No

Approved, Published, or Expired

Expired

No

Approved, Published, or Expired

Waiting For

Yes

Waiting For Moderator Approval Moderator Approval

Moderator Declined

Yes

Waiting For Moderator Approval

Approved

Yes

Waiting For Moderator Approval

Published

Yes

Waiting For Moderator Approval

Expired

Yes

Waiting For Moderator Approval

Calling the Delete method merely marks an item for deletion. The item isn't actually deleted from the database until CommitAll on the Context is called. Even after a CommitAll, the item isn't actually deleted from any in-memory collection in which it existed before deletion. Interaction with a deleted item before CommitAll will return read-only values; interaction with a deleted item after CommitAll will cause an exception. Unlike with Site Manager, deletion of an item after a CommitAll cannot be undone. The item is not moved into a Deleted Items folder. It is possible to undo a delete before a CommitAll by calling a RollbackAll. Any attempt to delete an item that is being edited by another user will cause an exception.

In Listing 25-4 is a function called DeleteAllChildrenFrom. We can use it to delete all the postings and channels for the Channel object passed to the function. The child channels must be empty for CMS to delete them. So, it is implied that any postings and channels of any decedents will also be deleted. We will call this function recursively to accomplish this task.

Listing 25-4 Function to delete all children from a channel
 private void DeleteAllChildrenFrom(Channel cmsChannel) //***************************************************************** //Delete all Postings and Channels from the cmsChannel //passed to the function. If a child Channel has children, this //function is called recursively //***************************************************************** {   try   {     //1. Iterate each Channel in the Channels collection     foreach (Channel cmsChildChannel in cmsChannel.Channels)     {       //2. Check to see if a child Channel has children       if(cmsChildChannel.AllChildren.Count > 0)       {         //3. Call this function recursively         DeleteAllChildrenFrom(cmsChildChannel);       }       //4. Check if the user has sufficient rights to delete       if(cmsChildChannel.CanDelete)       {         //5. Delete empty Channel         cmsChildChannel.Delete();         //6. Provide visual feedback of Channel deletion         ListBox1.Items.Add( cmsChildChannel.Name +           " Channel was deleted from " +           cmsChannel.Name.ToString() + " Channel "           );       }     }     //7. Iterate each Posting in the Postings collection     foreach (Posting cmsChildPosting in cmsChannel.Postings)     {       //8. Check if the user has sufficient rights to delete       if(cmsChildPosting.CanDelete)       {         //9. Delete the Posting         cmsChildPosting.Delete();         //10. Validate successful deletion         if(cmsChildPosting.IsDeleted)         {         //11. Provide visual feedback of Posting deletion         //    Notice that the Name of a deleted item can be         //    referenced as read-only         ListBox1.Items.Add( cmsChildPosting.Name +           " Posting was deleted from " +           cmsChannel.Name.ToString() + " Channel"           );         }       }     }   }   catch(Exception eError)   {     //12. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();   } } 

Next we'll delete the entire channel hierarchy we created earlier in this section (posting and all), leaving the original Scratch channel and the single Pad posting.

Replace the Button1_Click function of our Scratchpad template file with the following code:

 private void Button1_Click(object sender, System.EventArgs e) {   try   {     //1. Grab the current CMS Context     CmsHttpContext cmsContextHttp = CmsHttpContext.Current;     //2. Grab an Authenticated Context in Update PublishingMode     //   using the GetAuthenticatedCmsApplicationContext function     //   from Listing 25 1     CmsApplicationContext cmsContextApp =       GetAuthenticatedCmsApplicationContext(PublishingMode.Update);     //3. Grab the Channel to expunge     //   Cast the result of the Searches object as a Channel     Channel cmsBradyChannel =       cmsContextApp.Searches.GetByPath("/Channels/Scratch/Brady")       as Channel;     //4. Populate the label with the name of the Application     //   Context Channel and PublishingMode     Label1.Text = "<b>Channel: </b>" +       cmsBradyChannel.Name.ToString() +       "<br><b>PublishingMode: </b>" +       cmsContextApp.Mode.ToString();     //5. Delete all Channels (and their descendants) and Postings     DeleteAllChildrenFrom(cmsBradyChannel);     //6. Check if the user has sufficient rights to delete     if(cmsBradyChannel.CanDelete)     {       //7. Delete empty parent Channel       cmsBradyChannel.Delete();       //8. Provide visual feedback of Posting creation in Listbox1       ListBox1.Items.Add( cmsBradyChannel.Name +         " Channel was deleted from " +         cmsBradyChannel.Parent.Name.ToString() + " Channel "         );     }     //9. Commit of all changes. If not explicitly called the     //   disposition of changes will be based upon     //   RollbackOnSessionEnd     cmsContextApp.CommitAll();     //10. Dispose of the stand-alone Application Context     cmsContextApp.Dispose();   }   catch(Exception eError)   {     //11. Provide error feedback to the developer     Label1.Text = "<b>Error: </b>" + eError.Message.ToString();   } } 

Build the solution and then refresh the Scratchpad posting in Internet Explorer, or browse to it and click the Button. The page should reload and look similar to Figure 25-6.

Figure 25-6. Delete channel hierarchy

graphics/25fig06.gif

Scroll down the list box to see all the channels and postings deleted. Open Site Manager or choose Global Refresh from the Site Manager View menu if it is already open. The entire channel hierarchy as depicted in Figure 25-1 should be expunged. Notice that you don't even find it in the Deleted Items hive.

Of course, create and delete functionality is typically the domain of a CMS administrator. This PAPI functionality would naturally be manifest in custom administration pages or Web Author customizations.

NOTE: See Chapter 30 for details on customizing the Web Author.




Microsoft Content Management Server 2002. A Complete Guide
Microsoft Content Management Server 2002: A Complete Guide
ISBN: 0321194446
EAN: 2147483647
Year: 2003
Pages: 298

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net