Token replacement aka WordPress style shortcodes in ASP.Net MVC

Friday, March 17, 2017

For a digital publishing platform, token replacement can be a good way to allow users to easily insert special types of content into their posts.
Wordpress have their square bracket syntax ‘shortcodes’, so plugin developers can enable users to insert say, a contact form into a blog post with code as simple as [contactform].

I recently implemented something similar in an ASP.Net MVC project; here’s how it can be done in an MVC way, using a PartialView to contain the markup of the form.

I’m using XML syntax for my shortode:
<contactform requireemail=true requirephone=true>

I’ve chosen valid XML syntax for my shortcode so that I can use built in XML parsing tools to read the data from a token string.

Here’s my model:

[Serializable]
[XmlRoot(ElementName = "contactform")]
ContactForm 
{
	[XmlAttribute("requireemail")]
	bool RequireEmail {get;set;}

	[XmlAttribute("requirephone")]
	bool RequirePhone {get;set;}
} 

And the code to deserialise a valid token:

public ContactForm Deserialize(string data)
{
	try
	{
		var doc = XDocument.Parse(data).Root;
		if (doc == null)
			return null;

		var ser = new XmlSerializer(typeof(ContactForm));
			return (ContactForm)ser.Deserialize(doc.CreateReader());
	}
	catch
	{
		return null;
	}
}

As mentioned, I have the markup for my contact form in a Partial View. I also have a string of content from the database possibly containing a contact form shortcode.

Using my Regex and Deserialize method, I want to turn a string like:

“Contact me by filling out my form. <contactform> Thank you!”

Into something like:

Contact me by filling out my form. 
<div class="form">
...
</div>
Thank you!

This can be achieved by finding the token instance in the string, rendering the partial view on the server with the model obtained from my Deserialize method and replacing the token with the result.

To render a partial view from within my app, I need a ViewContext. I can new one of these up using a ControllerContext.

The code looks like this:

public string RenderPartial(ControllerContext context, string partialName, object model){

	var view = ViewEngines.Engines.FindPartialView(context, partialName).View;
	
	using (StringWriter sw = new StringWriter()){
		var viewContext =
				new ViewContext(context, 
						view,
						new ViewDataDictionary {Model = model},
						new TempDataDictionary(),sw);	
		view.Render(viewContext, sw);		
		return sw.GetStringBuilder().ToString();
	}
}

Finally we need a Regex that can identify the shortcode in a string. In this case it’s just (<contactform.*?/>). Now from within a Controller I can combine the above methods to replace instances of <contactform> with the full markup contained in my Partial, using the following method:

public string InsertContactForms(string content){

	Regex contactformRegex = new Regex("(<contactform.*?/>)", RegexOptions.Compiled);
	Match m = contactformRegex.Match(content);
	
	while (m.Success){
		ContactForm contactformVm = Deserialize(m.Value);
		
		if (contactformVm != null){
		
			content = contactformRegex.Replace(content, RenderPartial(ControllerContext, "_ContactForm.cshtml", contactformVm), 1);	
		}			
		
		m = m.NextMatch();			
	}
	
	return content;
}

If this gets output to an MVC View, it’s as simple as calling @Html.Raw(Model.content), or if retrieved via ajax could be appended to a page using Javascript.


Leave a Reply

Your email address will not be published. Required fields are marked *