Adventures in Tokyo (SN): Disabling the TinyMCE5 Context Menu

Table of Contents

Adventures in Tokyo on ServiceNow

This is the first blog in a series about my experiences with the Tokyo release, both from a feature/development perspective and from a strategic standpoint.

We’re starting with an adventure in modifying functionality in the TinyMCE5 editor on HTML fields.


Why we’re here

TinyMCE5 Context Menu

In the Tokyo release, ServiceNow upgraded from TinyMCE4 to TinyMCE5 as the preferred HTML editor for HTML field types. An example of a HTML field frequently used is the Article Body(.text) field on the Knowledge table (kb_knowledge) and it’s extensions. Apart from migrating to a supported version, this changed a few core features of the tool. One of the most impactful changes was the contextmenu plugin being added to the core editor by default. This changed the default behaviour of a right-click on these fields.

I came across this initially through the patch notes, and started to see some commentary on the issue in both the Community and SNDevs Slack. As my organisation heavily uses HTML fields through our extensive knowledge base, I wanted to get my head around this problem and what could be done.


TinyMCE5 context menu and workaround

The contextmenu plugin changes the default experience of a right-click to a much-reduced and software-defineable context menu. Follow these links to learn more about the Context Menu and the possible Menu Items.

The problem is that ServiceNow provides no baseline support for modifying the Context Menu or disabling it. They provide support for setting height and toolbar layouts and some other select values, but have configured this information to be passed into a script that gets inserted into the window on runtime, meaning that this is not easily extended by crafting other values or objects.

In order to use the native context menu, you need to hold CTRL/CMD while right-clicking to get this. This workaround is sufficient for some users and organisations.


Changing the TinyMCE5 config with client scripts

It is preferable to keep the baseline functionality from ServiceNow as opposed to implementing custom changes, so that you stay up to date with any changes they make. However, if you need to disable the context menu to return to parity with TinyMCE4’s right-click functionality, I can confirm that the code below will provide this functionality in Core UI & UI 16 in either a:

  • OnLoad Client Script, with Isolate Script set to false
  • UI Script, set to Global

What the below code snippets do is, once the Window object emits the load event, it:

  1. finds the active editor (or editors)
  2. Sets the contextmenu flag to false in the settings object of the editor
  3. Loads the content (and settings object) of the editor.

The reason for doing this in the settings object and not intercepting the init object was due primarily to not wanting to interact with the initialisation of this field alongside the form.

Disclaimer: This has not been tested on Workspaces, so I cannot confirm how this will function under those UIs. I’m also new to the TinyMCE editor methods, I may update this if I learn of a more suitable method (possibly .render())

This first snippet will update the first (or only) editor if one is present on the form:

window.onload = function() {
	if(tinyMCE !== undefined && tinyMCE.editors !== undefined){ //TinyMCE is present and has editors (has been initialised)
		tinyMCE.editors[tinyMCE.activeEditor.id].settings.contextmenu = false;
		tinyMCE.editors[tinyMCE.activeEditor.id].load(); // Triggers reload
    }
};

In the above example the editor gets referenced by id, which is the name of the table and field in ServiceNow (i.e. kb_knowledge.text). It’s also possible to reference these via index as per the second snippet below.

This second snippet will iterate through all editors instantiated in the form by ServiceNow:

window.onload = function() {
	if(tinyMCE !== undefined && tinyMCE.editors !== undefined){ //TinyMCE is present and has editors (has been initialised)
        for(var i = 0; i < tinyMCE.editors.length; i++){
            tinyMCE.editors[i].settings.contextmenu = false;
            tinyMCE.editors[i].load(); // Triggers reload
        }
    }
};

Both examples have been tested in both Client Script and UI Scripts. Using the response time indicator to benchmark, neither implementation incurred a cost sufficient to raise the execution time above 0ms on the machine I was using. As such, this should have negligible performance impact - however, as this is working with client-side scripting, always be considered and careful in how and when you implement functionality like this.


Thoughts and Extensions

In troubleshooting this I found that ServiceNow had built their TinyMCE invocation to support only a small subset of configuration options available. While understandable for maintaining a consistent UX, I believe that there is an opportunity for a few more system properties/dictionary attributes in this space. I also see value and opportunity in leveraging the TinyMCE context menu, but again this would currently require updating the editor object to add the additional menu options with scripts much like the above.

Good luck in Tokyo!