Predictably, all the methods in this section will require that the Context be in Update mode (see the Getting into Update PublishingMode sidebar in Chapter 25). We will be using the function introduced in Listing 25-1 to get an authenticated CmsApplicationContext in Update Publishing Mode. 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. As always, after an object has been deleted, attempting to call any of its methods will cause an exception. Also, attempting to call any of these methods on a historical revision will cause an exception. In general, the OwnedBy property (also discussed in Chapter 25) is implicitly changed to the current user so that other authors cannot concurrently update the Posting object or any of its ConnectedPostings. All changes only become permanent and ownership released (if appropriate) once the Context CommitAll method is called. Calling the Context RollbackAll method before CommitAll will return the Posting object to its previously committed condition. The simple error handling in the code throughout these code samples isn't meant to be the model for how to handle CMS exceptions; there is an application code block from Microsoft on how to do exception handling in .NET. These functions also assume the presence of both a ListBox1 and a Label1 to provide visual feedback to the user, which certainly won't typically be normal, but it works for our purposes. The first thing we will tackle in code is the creation of a new PlaceholderDefinition. Like the CmsApplicationContext covered in Chapter 24, PlaceholderDefinitions are created using a constructor.
NOTE: Protected members are outside the scope of this chapter and are covered in the context of creating a custom placeholder in Chapter 29. PlaceholderDefinition Constructor, CreatePlaceholderThe PlaceholderDefinition constructor creates a new instance of a PlaceholderDefinition within a given template. The new PlaceholderDefinition will need to be of a specific PlaceholderDefinition constructor because the .NET Framework will not let us create an instance of an abstract class; so we will use the HtmlPlaceholderDefinition constructor in our example. The steps that we would need to go through to create a PlaceholderDefinition in a template manually are similar to the steps that we need to follow programmatically:
After we have a PlaceholderDefinition, we create an in-memory Placeholder object based upon that new definition. Once again, we will be using the function introduced in Listing 25-1 of Chapter 25 to get an authenticated CmsApplicationContext in Update PublishingMode. Replace the Button1_Click function of our Scratchpad template file (see the Scratchpad sidebar in Chapter 24 for details) with the following code: private void Button1_Click(object sender, System.EventArgs e) { try { //1. Check to be sure a new PlaceholderDefinition name was // entered. Note that there is no checking for name validity if (TextBox1.Text.Length > 0) { //2. Grab the current Template Template cmsCurrentTemplate = CmsHttpContext.Current.Posting.Template; //3. Check to see if the user has sufficient rights if (cmsCurrentTemplate.CanSetProperties) { //4. Grab an Authenticated Context in Update PublishingMode // from Listing 25 1 CmsApplicationContext cmsContextApp = GetAuthenticatedCmsApplicationContext( PublishingMode.Update); //5. Find the current Template in the Update Context in // which to create the PlaceholderDefinition // Cast the result of a Searches object as a Template Template cmsUpdateTemplate = cmsContextApp.Searches.GetByGuid(cmsCurrentTemplate.Guid) as Template; //6. Create HtmlPlaceholderDefinition object using // a constructor HtmlPlaceholderDefinition cmsDefinition = new HtmlPlaceholderDefinition(); //7. Replace the default name with the value in // the TextBox cmsDefinition.Name = TextBox1.Text.ToString(); //8. Create the PlaceholderDefinition in the // Update Template HtmlPlaceholderDefinition cmsCreatedDefinition = cmsUpdateTemplate.CreatePlaceholderDefinition( cmsDefinition) as HtmlPlaceholderDefinition; //9. Check if the PlaceholderDefinition create // was successful if (cmsCreatedDefinition != null) { //10. Create Placeholder using new PlaceholderDefinition HtmlPlaceholder cmsCreatedPlaceholder = cmsCreatedDefinition.CreatePlaceholder() as HtmlPlaceholder; //11. Check if the Placeholder Object create // was successful if (cmsCreatedPlaceholder != null) { //12. Populate the label with Template Name Label1.Text = "<b>Update Template Name: </b>" + HttpUtility.HtmlEncode( cmsUpdateTemplate.Name.ToString()); //13. Add to label the New PlaceholderDefinition Name Label1.Text += "<br><b>New PlaceholderDefinition Name: " + "</b>" + HttpUtility.HtmlEncode( cmsCreatedDefinition.Name.ToString()); //14. Add to the label the New Placeholder Object Name // Because this is only an in-memory Placeholder it // does not have a Datasource, a Name, etc. Label1.Text += "<br><b>New Placeholder Object Type: </b>" + cmsCreatedDefinition.GetType().ToString(); } else { //15. Provide nonerror feedback to the developer Label1.Text = "HtmlPlaceholder create failed"; } } else { //16. Provide nonerror feedback to the developer Label1.Text = "PlaceholderDefinition create failed"; } //17. Commit all changes cmsContextApp.CommitAll(); //18. Dispose of the stand-alone Application Context cmsContextApp.Dispose(); } else { //19. Provide nonerror feedback to the developer Label1.Text = "User does not have CanSetProperties rights"; } } else { //20. Provide nonerror feedback to the developer Label1.Text = "TextBox must be a PlaceholderDefinition name"; } } catch(Exception eError) { //21. Provide error feedback to the developer Label1.Text = "<b>Error: </b>" + eError.Message.ToString(); } } At the top of the code window is a list of namespaces in use. If there isn't a line that looks like the following, you will need to add it: using Microsoft.ContentManagement.Publishing.Extensions.Placeholders; Build the solution and then refresh the Scratchpad posting in Internet Explorer; or browse to it, type the name for a new PlaceholderDefinition (we used TestPlaceholderDefinition), and click the Button. The page should reload and look similar to Figure 27-2. Figure 27-2. Create PlaceholderDefinition and Placeholder objectWhen we close the browser or the posting goes out of scope, the in-memory Placeholder object will be expunged, but the PlaceholderDefinition is a permanent part of our template definition. Open the PlaceholderDefinitions collection property for the ScratchTemplate in VS.NET, and we can see the PlaceholderDefinition that was created in our code at the bottom of the list in the Editor (Figure 27-3). It may be necessary to right-click the templates TemplateGallery and select Refresh before VS.NET will requery the database to see the change. Figure 27-3. PlaceholderDefinition in VS.NETIf in line 7 we hadn't altered the Name of the created PlaceholderDefinition or if the name that we gave had already existed, CMS would have given the default name of NewPlaceholderDefinitionX, where X is a sequential number incremented by one until a unique name is found. We also could have provided an alternate Datasource when we created our in-memory Placeholder object, and then we could have put in content and later retrieved that content. However, there is an example of how to do this in Chapter 29. CloneThe Clone method is an alternative way of creating a new PlaceholderDefinition in the same or a different template. It works exactly the same as the previous example, except that line 6: //6. Create HtmlPlaceholderDefinition with a Constructor HtmlPlaceholderDefinition cmsDefinition = new HtmlPlaceholderDefinition(); would be changed to get a clone of an existing definition (we'll clone the PlaceholderDefinition that we created in the last example): //6. Create HtmlPlaceholderDefinition as a read-only Clone // of the TestPlaceholderDefinition previously created HtmlPlaceholderDefinition cmsDefinition = cmsCurrentTemplate.PlaceholderDefinitions ["TestPlaceholderDefinition"].Clone() as HtmlPlaceholderDefinition; We can optionally pass in a Boolean parameter that if true would make the created PlaceholderDefinition read-only. The default value of this parameter is false. Of course, then we couldn't modify any of the PlaceholderDefinition's properties. DeleteThe Delete method is used to remove unwanted PlaceholderDefinitions. However, there are some rules. The PlaceholderDefinition must be retrieved via the PlaceholderDefinitions collection of the template rather than the Definition property of a specific placeholder. The user must have sufficient rights in this case, CanSetProperties rights and as always the Context must be in Update mode. Deleting a PlaceholderDefinition may change the state of the template. Let's remove the TestPlaceholderDefinition that we created in the earlier example. Once again, we will be using the function introduced in Listing 25-1 of Chapter 25 to get an authenticated CmsApplicationContext in Update PublishingMode. 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 Template Template cmsCurrentTemplate = CmsHttpContext.Current.Posting.Template; //2. Check to see if the user has sufficient rights if (cmsCurrentTemplate.CanSetProperties) { //3. Grab an Authenticated Context in Update PublishingMode // from Listing 25 1 CmsApplicationContext cmsContextApp = GetAuthenticatedCmsApplicationContext( PublishingMode.Update); //4. Find the current Template in the Update Context // Cast the result of the Searches object as a Template Template cmsUpdateTemplate = cmsContextApp.Searches.GetByGuid(cmsCurrentTemplate.Guid) as Template; //5. Populate the ListBox with the Names of all the // PlaceholderDefinitions in the collection foreach(PlaceholderDefinition cmsDefinition in cmsUpdateTemplate.PlaceholderDefinitions) { ListBox1.Items.Add("Before: " + cmsDefinition.Name.ToString()); } //6. Grab the PlaceholderDefinition to delete, must be // referenced thru the PlaceholderDefinitions collection PlaceholderDefinition cmsDoomedDefinition = cmsUpdateTemplate.PlaceholderDefinitions [TextBox1.Text.ToString()] as PlaceholderDefinition; //7. Check to see if we found the PlaceholderDefinition if (cmsDoomedDefinition != null) { //8. Populate the label with PlaceholderDefinition Name Label1.Text = "<b>PlaceholderDefinition To Delete: </b>" + HttpUtility.HtmlEncode( cmsDoomedDefinition.Name.ToString()); //9. Delete the PlaceholderDefinition cmsDoomedDefinition.Delete(); //9. Commit all changes cmsContextApp.CommitAll(); //10. Populate the ListBox with the Names of all the // PlaceholderDefinitions that remain foreach(PlaceholderDefinition cmsDefinition in cmsUpdateTemplate.PlaceholderDefinitions) { ListBox1.Items.Add("After: " + cmsDefinition.Name.ToString()); } //11. Dispose of the stand-alone Application Context cmsContextApp.Dispose(); } else { //12. Provide nonerror feedback to the developer Label1.Text = "Didn't find the PlaceholderDefinition"; } } else { //13. Provide nonerror feedback to the developer Label1.Text = "User does not have CanSetProperties rights"; } } catch(Exception eError) { //14. 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, type the name for a PlaceholderDefinition to delete (we used TestPlaceholderDefinition), and click the Button. The page should reload and look similar to Figure 27-4. Figure 27-4. Delete PlaceholderDefinitionWe increased the height of the ListBox on the template for this example so that Figure 27-4 would show all five entries. You may need to scroll down to see the last one. Serialize, DeserializeObjectThe Serialize method removes all the state (property values) from the specified PlaceholderDefinition into either an XML string or an Xml Writer. In the example that follows, we place that XML string into the label so we can see it. The DeserializeObject method does just the opposite. It takes a previously serialized PlaceholderDefinition as represented by an XmlNode and hydrates a PlaceholderDefinition object from that XML. 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 Template Template cmsTemplate = CmsHttpContext.Current.Posting.Template; //2. Grab the HtmlPlaceholderDefinition to serialize HtmlPlaceholderDefinition cmsDefinition = cmsTemplate.PlaceholderDefinitions[TextBox1.Text.ToString()] as HtmlPlaceholderDefinition; //3. Check to see if we found the PlaceholderDefinition if (cmsDefinition != null) { //4. Show the name of the original PlaceholderDefinition ListBox1.Items.Add("Original: " + cmsDefinition.Name.ToString()); //5. Serialize the PlaceholderDefinition XML into the label Label1.Text = HttpUtility.HtmlEncode( cmsDefinition.Serialize()); //6. Declare and initialize an XmlDocument object System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument(); //7. Populate the xmlDocument with PlaceholderDefinition XML xmlDocument.InnerXml = cmsDefinition.Serialize(); //8. Declare and initialize an XmlNode object with the XML System.Xml.XmlNode xmlDefinition = xmlDocument.DocumentElement; //9. Use the XmlNode to hydrate a new PlaceholderDefinitnion HtmlPlaceholderDefinition cmsDeserializedDefinition = HtmlPlaceholderDefinition.DeserializeObject(xmlDefinition) as HtmlPlaceholderDefinition; //10. Show the name of the hydrated PlaceholderDefinition ListBox1.Items.Add("Deserialized: " + cmsDeserializedDefinition.Name.ToString()); } else { //11. Provide nonerror feedback to the developer Label1.Text = "Didn't find the PlaceholderDefinition"; } } catch(Exception eError) { //12. 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, type the name for a PlaceholderDefinition to serialize (we used NewHtmlPlaceholderDefinition1), and click the Button. The page should reload and look similar to Figure 27-5. Figure 27-5. Serialize and Deserialize PlaceholderDefinitionPropertyChangedThe PropertyChanged event for a PlaceholderDefinition is fired whenever changes are made to the PlaceholderDefinition through a public property setter. |