I'm using CKEditor version 3.6
I want to automatically add class="newsleft" to any image tag added through the WYSIWYG.
I've seen a few posts that mention dataProcessor but have no idea which file this should be added or how to do it.
Can someone tell me where I would place the following code?
editor.dataProcessor.htmlFilter.addRules(
{
elements:
{
img: function( element )
{
if ( !element.attributes.alt )
element.attributes.alt = 'An image';
}
}
} );
Basically put it in instanceReady listener and it will be fine (3.x and 4.x) (fiddle):
CKEDITOR.replace( 'editor', {
plugins: 'wysiwygarea,toolbar,sourcearea,image,basicstyles',
on: {
instanceReady: function() {
this.dataProcessor.htmlFilter.addRules( {
elements: {
img: function( el ) {
// Add an attribute.
if ( !el.attributes.alt )
el.attributes.alt = 'An image';
// Add some class.
el.addClass( 'newsleft' );
}
}
} );
}
}
} );
CKEDITOR.htmlParser.element.addClass is available since CKEditor 4.4. You can use this.attributes[ 'class' ] prior to that version.
Here's another approach:
CKEDITOR.on( 'instanceReady', function( evt ) {
evt.editor.dataProcessor.htmlFilter.addRules( {
elements: {
img: function(el) {
el.addClass('img-responsive');
}
}
});
});
this worked for me in 3.6
add the following code to config.js
CKEDITOR.on('dialogDefinition', function (ev) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is image dialog window
if (dialogName == 'image') {
// Get a reference to the "Advanced" tab.
var advanced = dialogDefinition.getContents('advanced');
// Set the default value CSS class
var styles = advanced.get('txtGenClass');
styles['default'] = 'newsleft';
}
});
Because of this topic is being found in Google very high, I might help people by answering the question: how to add a default class when inserting an image in CKeditor. This answer is for CKeditor 4.5.1. as this is the latest version right now.
Look up image.js in /ckeditor/plugins/image/dialogs/image.js
Search for d.lang.common.cssClass
You will find: d.lang.common.cssClass,"default":""
Edit it with your class name(s) such as: d.lang.common.cssClass,"default":"img-responsive"
I've tried this and it works!
in Version: 4.5.3
Look up image.js in /ckeditor/plugins/image/dialogs/image.js
Search for editor.lang.common.cssClass
You will find: editor.lang.common.cssClass,"default":""
Edit it with your class name(s) such as: editor.lang.common.cssClass,"default":"your-class-name"
Related
I created a custom link button and want it appear highlighted/selected when you select/click on the link in the text editor, just like clicking on bold text shows the Bold icon as selected. In TinyMCE 4 you could simply use "stateSelector" to have it highlight when that kind of DOM element was selected, like this:
editor.ui.registry.addButton('SpecialLink', {
icon: 'link',
onAction: makeSpecialLink(),
**stateSelector: 'a[href]'**
});
I can't find anything about what stateSelector was replaced with in TinyMCE 5 and so far all I've been able to do is recreate some of that functionality inside tinymce.init:
init_instance_callback: function(editor) {
editor.on("SelectionChange", function(e){
let elem = editor.selection.getNode();
if( $(elem).is("a") )
console.log("Highlight the Special Link button");
else
console.log("Deselect the Special Link button");
})
}
I can reference myMCE.plugins.SpecialLink, but I can't call setActive(true) on it.
Any help would be greatly appreciated!
You can use addToggleButton rather than addButton, then call setActive.
Here's a snippet of my code.
editor.ui.registry.addToggleButton('my-action', {
icon: null,
text: 'My action',
onAction: function onAction() {
// ...do stuff
},
onSetup: function(api) {
function nodeChangeHandler(){
const selectedNode = editor.selection.getNode();
return api.setActive(selectedNode.id === constants.id);
}
editor.on('NodeChange', nodeChangeHandler);
}
});
}
https://www.tiny.cloud/docs/ui-components/typesoftoolbarbuttons/#togglebutton
#Muki's answer is similar to what I did, but I referenced the core code for anchor button from tinymce's git repo here https://github.com/tinymce/tinymce/blob/develop/modules/tinymce/src/plugins/anchor/main/ts/ui/Buttons.ts
I changed editor.ui.registry.addButton to editor.ui.registry.addToggleButton and added onSetup: (buttonApi) => editor.selection.selectorChangedWithUnbind('a:not([href])', buttonApi.setActive).unbind instead of stateSelector after onAction.
Something like below:
editor.ui.registry.addToggleButton('SpecialLink', {
icon: 'link',
onAction: makeSpecialLink(),
onSetup: (buttonApi) => editor.selection.selectorChangedWithUnbind('a:not([href])', buttonApi.setActive).unbind
});
I'm playing around with CKEditor4 and I want to add a custom class to all images that I inserted. Unfortunately it seems as the dataFilter rule which works for adding classes on inserted <p> does not work on <img>-images. Image plugin I use is enhancedImage ("image2") and I do not upload the image, I pick it from a fileBrowser instance.
My code:
$(function(){
var editor = CKEDITOR.replace( 'article-ckeditor', {
customConfig: '/vendor/ckeditor/ckeditor.config.js',
filebrowserImageBrowseUrl: '/laravel-filemanager?type=Images',
filebrowserImageUploadUrl: '',
filebrowserBrowseUrl: '',
filebrowserUploadUrl: '',
on: {
instanceReady: function (ev) {
ev.editor.dataProcessor.dataFilter.addRules( {
elements : {
img: function( el ) {
el.attributes['class'] = 'img-fluid';
}
}
});
}
}
});
});
For me it seems as the event is just not fired. But I cannot find anything about it in the documentation...
Thanks for your help!
If you just want to add a custom class for inserted images, you could easily listen to dialogHide event:
editor.on( 'dialogHide', function( evt ) {
var widget = evt.data.widget;
if ( widget && widget.name === 'image' && widget.element ) {
widget.element.addClass( 'img-fluid' );
}
} );
I'm using CKEditor v.4.5.8, looking at the htmldataprocessor plugin.
I'm seeing that on* attributes (onclick, onmouseover, etc.) are transformed to data-cke-pa-on* protected attributes for display in CKEditor's editable regions, which is a useful way to prevent scripts from executing in the editor context.
Is it possible, without turning on Advanced Content Filtering (leaving config.allowedContent = true), to also transform any formaction attributes of <button> elements, added in the Source editor, into non-executable data-cke-pa-formaction attributes in the WYSIWYG editor? And can this be accomplished within a config.js file, rather than editing compiled ckeditor.js or htmldataprocessor.js directly?
I've tried adding the following to config.js:
CKEDITOR.on('instanceLoaded', function(e) {
e.editor.dataProcessor.dataFilter.addRules( {
elements: {
button: function( el ) {
if( el.attributes && el.attributes['formaction'] ){
el.attributes['data-cke-pa-formaction'] = el.attributes['formaction'];
delete el.attributes['formaction'];
}
}
}
} );
e.editor.dataProcessor.htmlFilter.addRules( {
elements: {
button: function( el ) {
}
}
} );
});
This works perfectly when switching back and forth between the source editor and the WYSIWYG editor, but on initial load, the element is still loading in as <button formaction="javascript:alert(document.domain)">Click me!</button>. I've tried using other events for CKEDITOR.on (beforeGetData, getData, etc.), but based on debugging, it appears that those functions are never called within the config script. Is there somewhere else I should be putting the addRules functions?
Please try below code (ACF needs to be enabled):
var editor = CKEDITOR.replace( 'editor1', {
extraAllowedContent : 'button[name,type,formaction]'
});
editor.on('pluginsLoaded', function( evt ){
evt.editor.filter.addTransformations( [
[
{
element:'button',
left: function( el ) {
return el.name == 'button';
},
right: function( el, tools ) {
if( el.attributes && el.attributes['formaction'] ){
el.attributes['data-cke-pa-formaction'] = el.attributes['formaction'];
delete el.attributes['formaction'];
}
}
}
]
]);
});
If you e.g. paste below HTML:
<button name="Test Button" type="submit" formaction="/action_page2.php" />Click me!</button>
You will get:
<button data-cke-pa-formaction="/action_page2.php" name="Test Button" type="submit">Click me!</button>
As I have explained at the start, ACF needs to be enabled thus if you are not familiar with it, please see below links:
http://docs.ckeditor.com/#!/guide/dev_acf
http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter
http://docs.ckeditor.com/#!/guide/dev_disallowed_content
http://docs.ckeditor.com/#!/api/CKEDITOR.filter-method-addTransformations
http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent
http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-extraAllowedContent
EDIT: If you can't use ACF, you cat try dataFilter and htmlFilter:
var editor = CKEDITOR.replace( 'editor1', {
extraAllowedContent : 'button[*]{*}(*)',
on: {
pluginsLoaded: function( evt ) {
evt.editor.dataProcessor.dataFilter.addRules( {
elements: {
button: function( el ) {
if( el.attributes && el.attributes['formaction'] ){
el.attributes['data-cked-pa-formaction'] = el.attributes['formaction'];
delete el.attributes['formaction'];
}
}
}
} );
//when you get data from editor
evt.editor.dataProcessor.htmlFilter.addRules( {
elements: {
button: function( el ) {
}
}
} );
}
}
});
NOTE: If you want permanent change, you can't use data-cke attributes because they get auto changed when getting raw HTML. For permanent change you should use e.g. data-cked-
I'm having trouble adding classes to selected image in ckeditor. What I came up with is this http://pokit.org/get/img/8d89802e1d6f6371f5bc326898d8b414.jpg.
I added 2 buttons for selecting whether whether a picture is in portrait or landscape mode. You can select either of them or none, and add costum height/width.
Here is my code:
CKEDITOR.replace('maindesc', {
"extraPlugins": "imgbrowse",
"filebrowserImageBrowseUrl": "/ckeditor/plugins/imgbrowse",
on: {
instanceReady: function() {
this.dataProcessor.htmlFilter.addRules( {
elements: {
img: function( el ) {
// Add an attribute.
if ( !el.attributes.alt ) {
el.attributes.alt = 'Img';
el.addClass('ckeditorImg');
if (Landscape == 1) {
el.addClass('ckLandscape');
el.attributes['style'] = '';
}
else if (Portrait == 1) {
el.addClass('ckPortrait');
el.attributes['style'] = '';
}
}
}
}
} );
}
}
});
So as far as I understand this goes through all, so I wrote that if the image has no alt attribute to add one and add the classes I want. Unfortunately this approach doesn't allow me to change the class on selected image when a user wants to change it, but instead he has to delete the image, select it again and then choose class.
My question is whether there is a way to get to currently selected image instead of going through all <img> tags in ckeditor and change its class.
Here is an example for how to add a new button to ckeditor that is enabled/disables based on the element that you currently select and add a class to that specific element (in this example it's for images, however you can use it in any way you want).
// Set the callback function
var setLandscapeClass = {
exec: function(editor) {
editor.getSelection().getStartElement().addClass('ckLandscape')
}
}
//Create the plugin
CKEDITOR.plugins.add('setLandscapeClass', {
init: function(editor) {
editor.addCommand('setLandscapeClass', setLandscapeClass);
editor.ui.addButton("setLandscapeClass", {
label: 'Set Landscape Class',
icon: '',
command: 'setLandscapeClass'
});
}
});
// Create the instance and add the plugin
CKEDITOR.replace( 'editor1', {
extraPlugins: 'setLandscapeClass',
allowedContent: true
});
// enable/disable the button based on the selection of the text in the editor
CKEDITOR.instances.editor1.on( 'selectionChange', function( evt ) {
var landscapeButton = this.getCommand( 'setLandscapeClass' );
if ( evt.data.path.lastElement.is( 'img' ) ) {
landscapeButton.enable();
} else {
landscapeButton.disable();
}
});
You can see a working demo here:
https://jsfiddle.net/7nm9q1qv/
I only created 1 button, and there is no icon there. I think you can use that code to create also the second button (for portrait class).
Update - add item to the context menu
In order to add a new item to the context-menu you should add this code:
// Add the context-menu
if (editor.addMenuItem) {
editor.addMenuGroup('testgroup');
editor.addMenuItem('setLandscapeItem', {
label: 'Set landscape class',
command: 'setLandscapeClass',
group: 'testgroup'
});
}
// On contextmenu - set the item as "visible" by the menu
if (editor.contextMenu) {
editor.contextMenu.addListener(function(element, selection) {
if (element.hasClass('ckLandscape') === false) {
return { setLandscapeItem: CKEDITOR.TRISTATE_ON };
}
});
}
Inside the init function of the plugin you add.
You can see that I added this line:
if (element.hasClass('ckLandscape') === false) {
(Which you can remove) only to give you an example of how to show the item in the menu only if the ckLandscape class doesn't exists for this image.
The updated version of the jsfiddle is here:
https://jsfiddle.net/7nm9q1qv/1/
Inside a Wordpress theme I am developing, i've a TinyMCEPopup to add shortcode to the editor, some shortcode requires images. Can i add an "Add media" button which opens the Wordpress media uploader and allow the user to select or upload an image even if i'm inside a TinyMCEPopup?
Don't know if it will help, but I had the same issue and solved it like this.
In functions.php add
add_action( 'after_setup_theme', 'mytheme_theme_setup' );
if ( ! function_exists( 'mytheme_theme_setup' ) ) {
function mytheme_theme_setup() {
add_action( 'init', 'mytheme_buttons' );
}
}
/********* TinyMCE Buttons ***********/
if ( ! function_exists( 'mytheme_buttons' ) ) {
function mytheme_buttons() {
if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) {
return;
}
if ( get_user_option( 'rich_editing' ) !== 'true' ) {
return;
}
add_filter( 'mce_external_plugins', 'mytheme_add_buttons' );
add_filter( 'mce_buttons', 'mytheme_register_buttons' );
}
}
if ( ! function_exists( 'mytheme_add_buttons' ) ) {
function mytheme_add_buttons( $plugin_array ) {
$plugin_array['mybutton'] = get_template_directory_uri().'/js/tinymce_buttons.js';
return $plugin_array;
}
}
if ( ! function_exists( 'mytheme_register_buttons' ) ) {
function mytheme_register_buttons( $buttons ) {
array_push( $buttons, 'mybutton' );
return $buttons;
}
}
This will initialize the button you need. Now in the tinymce_buttons.js you'll add something like
(function() {
tinymce.PluginManager.add('mybutton', function( editor, url ) {
editor.addButton( 'mybutton', {
text: 'My button for media upload',
icon: false,
onclick: function() {
editor.windowManager.open( {
title: 'Insert Media',
body: [
{
type: 'textbox',
name: 'img',
label: 'Image',
value: '',
classes: 'my_input_image',
},
{
type: 'button',
name: 'my_upload_button',
label: '',
text: 'Upload image',
classes: 'my_upload_button',
},
],
onsubmit: function( e ) {
editor.insertContent( '[shortcode-name img="' + e.data.img + '"]');
}
});
},
});
});
})();
jQuery(document).ready(function($){
$(document).on('click', '.mce-my_upload_button', upload_image_tinymce);
function upload_image_tinymce(e) {
e.preventDefault();
var $input_field = $('.mce-my_input_image');
var custom_uploader = wp.media.frames.file_frame = wp.media({
title: 'Add Image',
button: {
text: 'Add Image'
},
multiple: false
});
custom_uploader.on('select', function() {
var attachment = custom_uploader.state().get('selection').first().toJSON();
$input_field.val(attachment.url);
});
custom_uploader.open();
}
});
First you add the button functionality. The list of options to put in popup is given here. It's not 100% complete, but better than the official documentation which is crap.
The first part is the button initialization. This gives you a 'My button for media upload' button, and on click you should get a modal with input field and a button.
On button click the media upload will open and you can select your image. On select the url will be in the input field, and you'll get it in your shortcode.
Hope it helps :)
There was another question like this (Open/Access WP Media library from tinymce plugin popup window) , so I'm pasting my answer here since this is similar:
Hi - I had the same problem just now and found the solution so I'm sharing it here. I hope it's not too late.
First to be able to use WP Add Media button you would have to enqueue the needed script. This is easy, just call the wp_enqueue_media() function like so:
add_action('admin_enqueue_scripts', 'enqueue_scripts_styles_admin');
function enqueue_scripts_styles_admin(){
wp_enqueue_media();
}
This call ensures you have the needed libraries to use the WP Media button.
Of course you should also have the HTML elements to hold the uploaded/selected media file URL, something like this:
<input type="text" class="selected_image" />
<input type="button" class="upload_image_button" value="Upload Image">
The first text field will hold the URL of the media file while the second is a button to open the media popup window itself.
Then in your jscript, you'd have something like this:
var custom_uploader;
$('.upload_image_button').click(function(e) {
e.preventDefault();
var $upload_button = $(this);
//Extend the wp.media object
custom_uploader = wp.media.frames.file_frame = wp.media({
title: 'Choose Image',
button: {
text: 'Choose Image'
},
multiple: false
});
//When a file is selected, grab the URL and set it as the text field's value
custom_uploader.on('select', function() {
var attachment = custom_uploader.state().get('selection').first().toJSON();
$upload_button.siblings('input[type="text"]').val(attachment.url);
});
//Open the uploader dialog
custom_uploader.open();
});
Now I'm not going to explain every line because it's not that hard to understand. The most important part is the one that uses the wp object to make all these to work.
The tricky part is making all these work on a TinyMCE popup(which is the problem I faced). I've searched hi and lo for the solution and here's what worked for me. But before that, I'll talk about what problem I encountered first. When I first tried to implement this, I encountered the "WP is undefined" problem on the popup itself. To solve this, you just have to pass the WP object to the script like so:
(function() {
tinymce.create('tinymce.plugins.someplugin', {
init : function(ed, url) {
// Register commands
ed.addCommand('mcebutton', function() {
ed.windowManager.open(
{
file : url + '/editor_button.php', // file that contains HTML for our modal window
width : 800 + parseInt(ed.getLang('button.delta_width', 0)), // size of our window
height : 600 + parseInt(ed.getLang('button.delta_height', 0)), // size of our window
inline : 1
},
{
plugin_url : url,
wp: wp
}
);
});
// Register buttons
ed.addButton('someplugin_button', {title : 'Insert Seomthing', cmd : 'mcebutton', image: url + '/images/some_button.gif' });
}
});
// Register plugin
// first parameter is the button ID and must match ID elsewhere
// second parameter must match the first parameter of the tinymce.create() function above
tinymce.PluginManager.add('someplugin_button', tinymce.plugins.someplugin);
})();
What we're interested in is this line => "wp: wp" . This line ensures that we are passing the wp object to the popup window (an iframe really...) that is to be opened when we click the tinymce button. You can actually pass anything to the popup window via this object (the 2nd parameter of the ed.windowManager.open method)!
Last but not the least you'd have to reference that passed wp object on your javascript like so:
var args = top.tinymce.activeEditor.windowManager.getParams();
var wp = args.wp;
Make sure you do that before calling/using the WP object.
That's all you have to do to make this work. It worked for me, I hope it works for you :)