Templating is designed primarily as a way to allow non-coders to make changes to the content and layout of text communications from Community without having to access and alter any code. It is similar in functionality to Translations, but while translations can only be static text that is the same every time it is used, templates are designed to include data to customize the message based on context, such as inserting the username and title of content in a notification about creating content.
[toc]
Why/When Should I Use Templates?
Templating is the accepted method within Community to include any dynamic data in any communication generated by Community, while maintaining the ability to customize/alter the content on the fly within the Administration UI, and while respecting the possibility of translating the communication to be sent to more than one locality. While templating can be used for many different purposes, the most prominent use case is for sending emails.
Understanding Template Context
Templates are designed to be generic - you design once and can reuse it many times, because you can identify exactly what can be the same every time the template is sent, and what needs to be different. In other words, you create the overall design while leaving "holes" for your data to be filled in later. However, because of that, the template cannot be used by itself unless it has some data to plug in. Community calls this data the template's Context. Whenever a template is being rendered, a TemplateContext is required, and it must have the data the template engine is looking for to fill in the "holes". A TemplateContext is essentially a blind dictionary; objects are stored keyed by Guids. Plugins registering tokens are responsible for specifying what object they need in the context by specifying its id; Plugins rendering templates are responsible for generating the context and populating it with the ids and object types the tokens expect.
Allowing Your Plugins to Utilize Templates
When you want a plugin to be able to utilize the functionality of templates, you should implement ITemplatablePlugin. Common usages would be for notification and email templates, or any other plugin you create that may send messages to your users and where you may wish to include data unique to the current context/reason for sending that message.
Similar to Plugin Translations, templates need a controller to access retrieving and rendering functionality. Then they need an initial declaration of - in this case - the templates. You can set up a default template in as many languages as necessary. In defining the template, you specify an id, Name, Description, and a set of ContextualDataTypeIds. These contextual data type ids correspond to the type of data your template will be working with. If you are creating a notification about a blog post, for instance, you would specify the Blog Post Data Type Id. This would also give access to other objects and their properties that would be exposed from the Blog Post object - for instance, the Blog Post's Blog, Group, or Author.
private ITemplatablePluginController _controller; public void SetController(ITemplatablePluginController controller) { _controller = controller; } private TokenizedTemplate[] _defaultTemplates; public TokenizedTemplate[] DefaultTemplates { get { if (_defaultTemplates == null) { var myTemplate = new TokenizedTemplate("body") { Name = "Template Body", Description = "Sample template", ContextualDataTypeIds = new[] { PublicApi.BlogPosts.ContentTypeId } }; myTemplate.Set("en-us", @""); _defaultTemplates = new[] { myTemplate }; } return _defaultTemplates; } }
A note about ContentTypeId vs DataTypeId: each token category needs a unique id to represent when to include those tokens in the token selection list in the template editor. Because some of these tokens are for Content Types such as blog posts, the ContentTypeId was reused in these cases. For tokens that are not for a Content Type - for instance, likes or mentions - a new Id was created and referred to as a DataTypeId.
In order to actually utilize the template engine, you need to create a context and populate it with the information the template will need. While there are various ways to do this, for our sample we will use the method of handling an event and then generating an email from that. You can learn more about Events at Handling Events.
// Register to handle the event in the IPlugin Initialize() method. public void Initialize() { Apis.Get<IBlogPosts>().Events.AfterCreate += BlogPost_AfterCreate; }
When handling the event, we fetch the blog post in question, create a context with that blog post, render the templates with that context, and then send an email using those rendered templates.
void BlogPost_AfterCreate(BlogPostAfterCreateEventArgs e) { BlogPost post = Apis.Get<IBlogPosts>().Get(e.ContentId); var context = new TemplateContext { PostTarget = "Email" }; context.AddItem(PublicApi.BlogPosts.ContentTypeId, post); string renderedSubject = _controller.RenderTokenString("subject", context); string renderedBody = _controller.RenderTokenString("body", context); Apis.Get<ISendEmail>().Send(new SendEmailOptions { Subject = renderedSubject, Body = renderedBody, ToEmail = "recipient@test.com" }); }
Note that there are better ways to integrate with the EmailNotificationDistributionType; this is just a sample of working with rendered templates.