Maintaining Placeholder Values
The ASP.NET Web service
will be able to load and maintain placeholder values of postings. To do
this we will create two objects, namely LightweightPlaceholder and a corresponding LightweightPlaceholderCollection.
1. | In Visual Studio .NET, right-click on the project and a new class called LightweightPlaceholder.
|
2. | Add the following namespaces to the file:
using System;
using System.Xml;
using System.IO;
using Microsoft.ContentManagement.Publishing;
using Microsoft.ContentManagement.Publishing.Extensions.Placeholders;
namespace McmsAuthoringWebService
{
. . . code continues . . .
}
|
3. | Now,
let’s add the following public variables to our class, which will
contain the placeholder’s name, the type of placeholder, and its value:
/// <summary>
/// The placeholder name in MCMS
/// </summary>
public string Name;
/// <summary>
/// The type of placeholder e.g. Microsoft.ContentManagement.Publishing.
/// Extensions.Placeholders.HtmlPlaceholder
/// </summary>
public string Type;
/// <summary>
/// The XmlNode representation of the current value of the placeholder
/// </summary>
public XmlNode Value;
|
To be able to load an XmlNode with HTML, we will convert the HTML into well-formed XHTML. We will do this by using a publicly available tool called SgmlReader, by Chris Lovett, who works for a small outfit known as the Microsoft Corporation.
SGML stands for Standard Generalized Markup Language, the precursor to XML. The SgmlReader suite of tools provides many ways to create well-formed XML, but for the purposes of this example we will only need SgmlReader.dll.
To set up and configure the SgmlReader, follow these steps:
1. | |
2. | Extract the ZIP file to c:\sgml\SgmlReader.
|
3. | Create a directory under <local_drive>\Program Files called SgmlReader and copy the C:\SGML\SgmlReader\SgmlReaderDll\bin\release\SgmlReaderDll.dll file to the new folder.
|
We are now ready to create the CleanHtml() method, which will convert HTML into well-formed XHTML, for instance from <img src="theImage.jpg"> into <img src="theImage.jpg"></img>. This method will utilize the SgmlReader assembly we previously referenced. Before it is converted, the HTML must be wrapped in a single tag, in this case <html>, so the SgmlReader will recognize it as one element. Once it is returned from the SgmlReader, we will escape some characters that cannot be escaped by InfoPath and we will remove the enclosing <html> element from the content.
1. | Open the McmsAuthoringWebService project in Visual Studio .NET, and add a reference to <local_drive>\Program Files\SgmlReader\SgmlReaderDll.dll.
|
2. | Create the following method in LightweightPlaceholder.cs:
/// <summary>
/// Convert 'badly formed' HTML into well formed XHTML using the
/// SgmlReader utility by Chris Lovett, Microsoft Corporation
/// </summary>
/// <param name="BadlyFormedHtml">The badly formed HTML</param>
/// <returns>Well formed XHTML representation of the HTML</returns>
public string CleanHtml(string BadlyFormedHtml)
{
// Wrap the entire HTML string in a single tag so the
// SGMLReader can process the data as one element
BadlyFormedHtml = "<html>" + BadlyFormedHtml + "</html>";
// Load the string reader with the badly formed HTML
StringReader _stringReader = new StringReader(BadlyFormedHtml);
// instantiate and set the badly formed HTML string reader
// as the input stream of the SgmlReader
Sgml.SgmlReader _sgmlReader = new Sgml.SgmlReader();
_sgmlReader.InputStream = _stringReader;
// Instantiate the string writer and xmltext writer
// that will be used to hold the well formed XHTML
StringWriter _stringWriter = new StringWriter();
XmlTextWriter _xmlTextWriter = new XmlTextWriter(_stringWriter);
// Read the stream
_sgmlReader.Read();
// loop through the SgmlReader until the end of the stream
while(!_sgmlReader.EOF)
{
// add a node to the XML text writer
_xmlTextWriter.WriteNode(_sgmlReader, true);
}
// cleanup the XML text writer
_xmlTextWriter.Flush();
_xmlTextWriter.Close();
// Replace non-breaking spaces ( ) with spaces
// as InfoPath escapes these in placeholders.
string strCleanedXHTML = _stringWriter.ToString().Replace("&nbsp;",
" ");
// Remove the surrounding <html> tag by using an XmlDocument
// Create a new XmlDocument and load in the clean XHTML
XmlDocument _tempDocument = new XmlDocument();
_tempDocument.LoadXml(strCleanedXHTML);
// Get the html node and return the inner XML
return _tempDocument.SelectSingleNode("/html").InnerXml;
}
|
3. | Now we are able to create well-formed XHTML from HTML stored in an HtmlPlaceholder, we can populate the XmlNode property that the placeholder content will be stored in for use by InfoPath.
/// <summary>
/// Create an XHTML XmlNode from 'badly formed' HTML
/// </summary>
/// <param name="BadlyFormattedHtml">The badly formed HTML string</param>
/// <returns>XHTML XmlNode representation of input HTML</returns>
public XmlNode LoadXHTML(string BadlyFormattedHtml)
{
// Create a temporary XmlDocument object to generate nodes
System.Xml.XmlDocument tempDocument = new System.Xml.XmlDocument();
// Create a wrapper node for the data so InfoPath will correctly detect
// the XHTML content
System.Xml.XmlElement theNode =
(System.Xml.XmlElement)tempDocument.CreateNode(
System.Xml.XmlNodeType.Element, "theNode", "");
// Create a "RichText" element in the XHTML namespace
System.Xml.XmlElement theRichText = (System.Xml.XmlElement)
tempDocument.CreateNode(System.Xml.XmlNodeType.Element, "RichText",
"http://www.w3.org/1999/xhtml" );
// Clean the badly formed HTML and set the inner XML of the RichText node
theRichText.InnerXml = CleanHtml(BadlyFormattedHtml);
// Append the RichText node to the wrapper node
theNode.AppendChild( theRichText );
//Return the wrapper element.
return theNode;
}
|
4. | Next we will add a constructor to the LightweightPlaceholder class that accepts a Microsoft.ContentManagement.Publishing.Placeholder parameter. The Name and Type properties are set along with the Value property. To load the value we will first verify the placeholder is an HtmlPlaceholder and then will call the LoadXHTML() method passing in the placeholder’s HTML. LoadXHTML() will convert the placeholder’s HTML into an XmlNode.
/// <summary>
/// Instantiate and load a MCMS placeholder
/// </summary>
/// <param name="_Placeholder">MCMS placeholder to load</param>
public LightweightPlaceholder(Placeholder _Placeholder)
{
// Set the name and the type of placeholder
this.Name = _Placeholder.Name;
this.Type = _Placeholder.GetType().ToString();
// Is this an HtmlPlaceholder?
if (_Placeholder is HtmlPlaceholder)
{
// Convert the contents of the placeholder to XHTML
// and set the Value property
this.Value = LoadXHTML(((HtmlPlaceholder)_Placeholder).Html);
}
}
|
5. | The placeholder values will be saved back to MCMS for the relevant posting. So let’s create the Save() method. This solution currently only works with HtmlPlaceholders so we must first check to see if the placeholder is an HtmlPlaceholder and then we can set the Html property to the XML stored in the Value XmlNode.
/// <summary>
/// Save the placeholder contents to a posting
/// </summary>
/// <param name="_Posting">Posting to save against</param>
public void Save(Posting _Posting)
{
// Set the placeholder value for the current placeholder
// to the InnerXml of the Value property
// Note: This will only work with HtmlPlaceholders. If enhanced for
// other placeholders we would check the placeholder type before
// casting.
if (_Posting.Placeholders[this.Name] is HtmlPlaceholder)
{
((HtmlPlaceholder)_Posting.Placeholders[this.Name]).Html =
this.Value.InnerXml;
}
else
{
throw new Exception("The placeholder '" + this.Name
+ "' is not an HtmlPlaceholder.");
}
} |