Recently, while making a website I found that it's impossible to add custom buttons to TinyMCE editor in Admin UI without changing keystone code located in node_modules directory.
Is there any ideas/ways how to add custom buttons with custom behavior?
does this help? https://github.com/keystonejs/keystone/pull/285
@dkarbayev If you are saying that you can't add your own plugins, then I think you are corrent.
@morenoh149 That provides the info on how to add other buttons - ones that already exist - per the documentation. Which is useful, but I'm not sure if that is what dkarbayev is trying to do.
I have previously managed to load new plugins into tinyMCE without editing keystone source. The key is to add the plugin name using:
keystone.set('wysiwyg additional plugins', 'customplugin');
This will then cause keystoneJS to request /keystone/js/lib/tinymce/plugins/customplugin/plugin.min.js. Of course, this file doesnt exist. However, you can setup a route that matches this URL to serve the files you need:
app.get('/keystone/js/lib/tinymce/plugins/customplugin/:splat*', function (req, res, next) {
// Use req.params.splat and res.sendFile to pass the plugin back to the client.
});
Ideally though, it would be nice if there was an option to set custom plugin location, and keystone would check in there if it couldn't find it in the default plugins it already has bundled.
Since we used Brunch.io, we configured it to replace keystone's files on prebuilding phase:
plugins:
beforeBrunch: [
'cp addons/HtmlField.js node_modules/keystone/fields/types/html/'
]
i.e. we had modified HtmlField.js file in addons directory and put it before starting node.js server.
And all of this (and few another) movements took place because we needed a toolbar button that could convert text to button-styled link (to be honest, we added a little more custom buttons):
(part of HtmlField.js)
editor.addButton('greenbutton', {
title: 'Button',
image: '/images/button.png',
onclick: function () {
var selection = editor.selection.getContent() || '';
editor.windowManager.open({
title: 'Insert button',
body: [
{type: 'textbox', name: 'url', label: 'URL'},
{type: 'textbox', name: 'title', label: 'Title', value: selection}
],
onsubmit: function(e) {
editor.focus();
editor.selection.setContent('<a class="green-button" href="' + e.data.url +
'">' + e.data.title + '</a>');
}
});
}
});
I think this method and method proposed by @adamscybot are pretty ugly and nonnatural. The perfect way would be passing something to keystone.init() function.
That's why I'm requesting this feature.
In the past here is how I specified a custom location for a tinyMCE plugins (inside keystone main init() function):
'wysiwyg additional options': {
skin : 'lightgray',
menubar : 'file edit format view insert',
relative_urls: false,
content_css: '/assets/css/styles.min.css',
visualblocks_default_state: true,
external_plugins: {
'tinyvision':'/assets/plugins/tinyvision/build/plugin.min.js'
},
tinyvision: {
source: '/api/images'
}
}
@nanymor best solution so far. I guess this ticket could be closed, but its worth noting this in the docs.
+1 for @nanymor
@nanymor @mariohmol What is required to hook in the upload function of TinyVision?
I didnt use the upload function.. just the image gallery.
Good to know.
We're closing all questions and support requests to keep the issue tracker unpolluted. Please ask this question on Stackoverflow or Gitter instead!
I tried to use the upload option and for some reason Keystone seems to not pass it. If you pass anything other than a function, the values is passed; otherwise the upload option is removed altogether.
Found a solution to my problem by poking around the init function, my use case was to add a custom plugin I developed into the toolbar:
My plugin code at /js/apester/plugin.js
tinymce.PluginManager.add('apester', function(editor, url) {
// Add a button that opens a window
editor.addButton('apester', {
text: 'Insert Quiz',
icon: false,
onclick: function() {
// Open window
editor.windowManager.open({
title: 'Insert the Apester Quiz ID here',
body: [
{type: 'textbox', name: 'id', label: 'ID'},
{type: 'textbox', name: 'height', label: 'Height'}
],
onsubmit: function(e) {
// Insert content when the window form is submitted
editor.insertContent(`
<div class="apester-media" data-media-id=${e.data.id} height=${e.data.height}>
`);
}
});
}
});
return {
getMetadata: function () {
return {
name: "Apester Quiz Plugin",
url: "https://thespotlyte.com"
};
}
};
});
My keystone.js init code:
keystone.init({
'name': 'Spotlyte',
'brand': 'Spotlyte',
'signin logo': ['../SpotlyteLogo.svg'],
'static': 'public',
'admin path': 'keystone',
'auto update': true,
'session': true,
'session store': 'mongo',
'auth': true,
'user model': 'User',
'wysiwyg override toolbar': false,
'wysiwyg menubar': true,
'wysiwyg skin': 'keystone',
'wysiwyg importcss': '/main.css',
'wysiwyg cloudinary images': true,
'wysiwyg cloudinary images filenameAsPublicID': true,
'wysiwyg additional buttons': 'image | styleselect | apester',
'wysiwyg additional plugins': 'advlist autolink'
+ ' searchreplace visualblocks code fullscreen lists link charmap print preview anchor'
+ ' insertdatetime media table contextmenu paste image imagetools wordcount',
'wysiwyg additional options': {
external_plugins: {
apester: '/js/apester/plugin.js',
uploadimage: '/js/uploadimage/plugin.min.js',
},
image_caption: true,
},
});
Notice on the wysiwyg additional options, I added the apester plugin by attaching its source location, but that was not enough to display the button on the toolbar in wysiwyg. I also had to add to wysiwyg additional buttons, where at the end of the line i added | apester which is the name of the plugin. And everything magically worked.
This was not documented anywhere on keystone, I was poking around the init function the whole afternoon and randomly found this solution.
Most helpful comment
In the past here is how I specified a custom location for a tinyMCE plugins (inside keystone main init() function):