Friday, 18 May, 2018

Umbraco - TinyMCE. How to Add the CodeSample plugin for Code Snippet

Umbraco adding Code Snippets to Rich Text Editors.

This article still requires a final proof reading.

Preamble

Recently I revamped the JAGC website, adding some new features here and there from my wish list, one of which was improved code sample support when inserting code snippets using Umbraco's built in Rich Text editor. In the past I'd always used the Markdown editor for these posts as it has better code snippet support, which is fine until you find yourself trying to edit post a with code snippets using the Rich Text editor and find yourself cursing it's lack of support.

This Post

This post explains how to install and use the CodeSample TinyMCE plugin extension for use with Umbraco Rich Edit Controls.

Umbraco and TinyMCE

Umbraco 7.10+ uses TinyMCE 4 (https://www.tinymce.com/) to display Rich Text Edit input controls in the back-office. Editors enter content and the resulting HTML is piped out when users browse the website. The thing is, it's not configured how you'd expect it to be after reading the documentation on TinyMCE. Umbraco has a unique configuration detailed in the Umbraco documentation under https://our.umbraco.org/documentation/reference/config/tinymceconfig/ but other than that there isn't any information.

The CodeSample Plugin

Umbraco ships TinyMCE with a full set of plugins; you can find them under <yourproject>\Umbraco\lib\tinymce\plugins. There are plugins to perform spellchecking, a word count and printing to name a few. The one we're using is called Code Sample (https://www.tinymce.com/docs/plugins/codesample/).

"The Code Sample plugin (codesample) lets a user insert and embed syntax color highlighted code snippets into the editable area. It also adds a button to the toolbar which on click will open a dialog box to accept raw code input." TinyMCE Website

CodeSample produces code snippets like below.

public ActionResult Index(RenderModel model)
{
    return Content("Error:Contact Surface Controller index not found.");
}

Note: When adding a code snippet using the CodeSample plugin with the Umbraco RTE you need to click the CodeSample button and then add the code and select the language; if you try to highlight code after you've pasted it in to the RTE control CodeSample will not work.

Prism.js

In turn, the CodeSample plugin is built on Prism.js (http://prismjs.com/). It's bundled inside the CodeSample plugin and shipped with TinyMCE inside Umbraco.

"Prism is a lightweight, extensible syntax highlighter, built with modern web standards in mind. It’s used in thousands of websites, including some of those you visit daily"Prism Website.

Once configured the CodeSample and Prism.js will display your snippets perfectly formatted inside you Rich Editor control but not on your website front-end which is why I mention it here.

Configuring Umbraco

Now we're going to add a new button and command to the Umbraco Rich Text Editor toolbar. Firstly open the <yourproject>\config\tinyMceConfig.config xml file, find the commands section, and add a new command section, like the one below:

<commands>
    ...
    <command>
      <umbracoAlias>codesample</umbracoAlias>
      <name>Code Sample</name>
      <icon>notused</icon>
      <tinyMceCommand value="" userInterface="true" frontendCommand="codesample">codesample</tinyMceCommand>
      <priority>75</priority>
    </command>
</commands>
umbracoAlias defines a unique alias within Umbraco for the command. This alias should not contain any spaces.

icon 
defines the path to an image file to be used on the formatting toolbar. This image should be 16px x 16px in size.

tinyMceCommand 
defines the tinyMceCommand properties.To further break this down, the value attribute is usually an empty value as most commands perform formatting tasks as opposed to returning values. The userInterface attribute takes a boolean value indicating whether or not the command has an additional UI component to display e.g. a new dialog to assist with inserting images. frontendCommand defines the name of the command to execute. This value usually matches the value of the tinyMceCommand node.

priority 
defines the sort order for the commands and should be sequentially incremented for each new command.

Umbraco Documentation

Note the priority setting: the values must be in sequential order. Meaning if the last value was 123 the next section's value should be 124. If you want to move the button somewhere else in the toolbar you can reorder the command sequence.

Next add the codesample plugin to the list of plugins as shown below:

  <plugins>
    <plugin loadOnFrontend="true">code</plugin>
    <plugin loadOnFrontend="true">codemirror</plugin>
    <plugin loadOnFrontend="true">paste</plugin>
    <plugin loadOnFrontend="true">anchor</plugin>
    <plugin loadOnFrontend="true">charmap</plugin>
    <plugin loadOnFrontend="true">table</plugin>
    <plugin loadOnFrontend="true">lists</plugin>
    <plugin loadOnFrontend="true">hr</plugin>
    <plugin loadOnFrontend="true">codesample</plugin>
  </plugins>

Where's the Button?

The CodeSample button still won't show up in the RTE toolbar until you add it. To do this go to Dev > Data Types and select the RTE data type you'd like to add the button to. This is where you add and remove TinyMCE supported plugins to your Umbraco RTE so that editors can you them. Browse down the list of plugins until you find CodeSample and tick it.

Now if you save your file and restart the web app the button will be there, and will work for the back-office, but won't have an icon yet. We'll add that next.

Adding A Toolbar Icon

The TinyMCE font used to display icons in the toolbar can be found under <yourprojectdir>\Umbraco\lib\tinymce\skins\umbraco\fonts. To view the icons available you'll need to load the font in to a font editor, the easiest way I found was to install the .ttf font on to Windows and open it with Charmap. Icon 0xE603 looked good to me so I chose that.

Create CSS to display the icon. You'll need to modify the TinyMCE lib installed with umbraco under <yourprojectdir>\Umbraco\lib\tinymce\skins\umbraco\skin.min.css and

add the following:

i.mce-ico.mce-i-codesample:before {
    content: "\e603";
}

Ths should match the name of your command.

Refresh the web app and you'll see the icon. The back-office should now let you add code samples but the styling may be bit off.

Fix Backoffice Styling

If the Umbraco RTE isn't set-up to allow <code> elements it'll strip them out when you save the code snippet and break the styling. If your noticing problems open the TinyMCEConfig.config again and check the ValidElements note includes an entry for <code>. If you checkout the example below you'll see,code tacked on to the end - that's the important but.

  <validElements>
    <![CDATA[+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],
-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],
img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],
-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],
-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],
thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],
-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],
-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],
-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],
dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],
param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],code]]>
  </validElements>
The validElements node defines which elements will remain in the edited text when the TinyMce Rich Text Editor (RTE) saves. You can use this to limit the returned HTML to a subset.

Umbraco Documentation

If you'd like to know a little more about how these validelements are configured checkout https://www.tinymce.com/docs/configure/content-filtering/#valid_elements.

The Frontend

At the moment the front-end doesn't now how to display the code snippet. It'll wrap it in <pre><code> but won't provide any Prism styling or language highlighting. To do that you need to download a Prism.js and Prism.css file from http://prismjs.com/download.html#them where you can specify your desired theme, language support and additional plugins. I selected to add the HIghlight Keyword plugin and ASP.NET (C#) language to support to the defaults.

Once you've downloaded the files, added and included them to your project the codesamples will start displaying properly on the frontend.

<link href="prism.css" rel="stylesheet">
<script src="prism.js"></script>

Highlighting

You'll notice highlighting just works. Yet if you read the Prism Highlight documentation (http://prismjs.com/plugins/highlight-keywords/) it indicates you need to include some Javascript. The reason it works is because you ticked the Highlight Keyword plugin when you created your download and it was automatically added to the Prism.js you downloaded. If you find yourself wanting more control over when and how plugin Javascript is downloaded you can always download the core Prism.js components and link to the plugin JS when and if you need it.

(function(){

if (
	typeof self !== 'undefined' && !self.Prism ||
	typeof global !== 'undefined' && !global.Prism
) {
	return;
}

Prism.hooks.add('wrap', function(env) {
	if (env.type !== "keyword") {
		return;
	}
	env.classes.push('keyword-' + env.content);
});

})();

TroubleShooting

It doesn't work: When adding a code snippet using the CodeSample plugin with the Umbraco RTE you need to click the CodeSample button and then add the code and select the language; if you try to highlight code after you've pasted it in to the RTE control CodeSample will not work.

That's Wrap

We're talked about Umbraco RTE property editors, how they're implemented using TinyMCE, how Umbraco configures TinyMCE, how to add a new toolbar icon to your Umbraco RTE, how to extend your Umbraco RTE to include support for code samples (code snippets), how to display the code sample in the fronted using Prism and Prism plugins, now you know how to add additional TinyMCE plugins to your RTE property editor and hopefully where to go looking for more information. Happy coding.

Want to Thank Me?

Did you like this article? Was it helpful? Why not buy me a coffee on Paypal? Buy me a coffee?