Adding custom shortcode placeholders to TinyMCE 5 - tinymce

i have some system codes in documents that i edit in tinymce. that codes should be represented by an interactive placeholder in the WYSIWYG editor.
It is like normal images but the result should be this Code instead (and not parsed to HTML):
[img]id=123&text=bla[/img]
I could not find any information on how to create this interactive placeholder element for custom elements...
any suggestions?

Like this, and set as a toolbar item
editor.ui.registry.addMenuButton('plugins', {
text: 'Plugin',
fetch: function (callback) {
var items = [
{type: 'menuitem', text: 'Image with description', onAction: function() {editor.insertContent('[img]Hier de code[/img]');}},
];
callback(items);
}
});

Related

selection.getContent({format: 'html'}) returns text unless I select all text in the editor

I am trying to manipulate the html content of text that is selected in the editor. tinymce.activeEditor.getContent({format: 'html'}); grabs all of the html content in the editor which is great but I want the html of selected/highlighted text. I was under the impression that using tinymce.activeEditor.selection.getContent({format: 'html'}); would achieve this but it only returns html if all of the text in the editor is selected.
Here is my code:
setup: function (ed) {
ed.addButton('customFormatSelect', {
type: 'menubutton',
text: 'Formats',
icon: false,
menu: [{
text: 'Get HTML',
icon: false,
onselect: function() {
alert(tinymce.activeEditor.selection.getContent({format: 'html'}));
}
}]
});
}
I have tried using onclick instead of onselect and still get the same result.
Here are some screenshots of the issue:
Here is me selecting the text I want the HTML from and clicking my 'Get HTML' button:
Image 1
Here is the alert that I get:
Image 2

Is it possible to have a date input in a w2ui toolbar

I am wondering if there is any way one could place a date input control in a toolbar similar to the one used for date input on a form?
Yes, it's possible, but it's quirky.
You will have to define the input field as a toolbar button:
{ type: 'html', id: 'roles', html: '<input id="id_role">' },
and in the toolbar's onRefresh() event you will have to cast the input to the desired w2filed:
onRefresh: function(event) {
if(event.target == 'roles'){
// w2field in toolbar must be initialized during refresh
// see: https://github.com/vitmalina/w2ui/issues/886
event.onComplete = function(ev){
$("#id_role").w2field('list', { items: roles });
};
}
},
In my example I'm inserting a drop down list, but you can adjust it to your needs.
Please see https://github.com/vitmalina/w2ui/issues/886 for an "official" reply.

Dynamically updating a TinyMCE 4 ListBox

I'm trying to modify the TinyMCE 4 "link" plugin to allow users to select content from ListBox elements that are dynamically updated by AJAX requests.
I'm creating the ListBox elements in advance of editor.windowManager.open(), so they are initially rendered properly. I have an onselect handler that performs the AJAX request, and gets a response in JSON format.
What I need to do with the JSON response is to have it update another ListBox element, replacing the existing items with the new results.
I'm baffled, and the documentation is terribly unclear. I don't know if I should replace the entire control, or delete items and then add new ones. I don't know if I need to instantiate a new ListBox control, or render it to HTML, etc.
Basically, I have access to the original rendered ListBox (name: "module"} with
win.find('#module');
I have the new values from the AJAX request:
var data = tinymce.util.JSON.parse(text).data;
And I've tried creating a new Control configuration object, like
newCtrlconfig = {
type: 'listbox',
label: 'Class',
values: data
};
but I wouldn't know how to render it, much less have it replace the existing one.
I tried
var newList = tinymce.ui.Factory.create(newCtrlconfig);
and then
newList.renderHtml()
but even then, the rendered HTML did not contain any markup for the items. And examining these objects is just frustrating: there are "settings", "values", "_values", "items" all of which will happily store my values, but it isn't even clear which of them will work.
Since it's a ListBox and not a simple SELECT menu, I can't even easily use the DOM to manipulate the values.
Has anyone conquered the TinyMCE ListBox in 4.x?
I found this on the TinyMCE forum and I have confirmed that it works:
tinymce.PluginManager.add('myexample', function(editor, url) {
var self = this, button;
function getValues() {
return editor.settings.myKeyValueList;
}
// Add a button that opens a window
editor.addButton('myexample', {
type: 'listbox',
text: 'My Example',
values: getValues(),
onselect: function() {
//insert key
editor.insertContent(this.value());
//reset selected value
this.value(null);
},
onPostRender: function() {
//this is a hack to get button refrence.
//there may be a better way to do this
button = this;
},
});
self.refresh = function() {
//remove existing menu if it is already rendered
if(button.menu){
button.menu.remove();
button.menu = null;
}
button.settings.values = button.settings.menu = getValues();
};
});
Call following code block from ajax success method
//Set new values to myKeyValueList
tinyMCE.activeEditor.settings.myKeyValueList = [{text: 'newtext', value: 'newvalue'}];
//Call plugin method to reload the dropdown
tinyMCE.activeEditor.plugins.myexample.refresh();
The key here is that you need to do the following:
Get the 'button' reference by taking it from 'this' in the onPostRender method
Update the button.settings.values and button.settings.menu with the values you want
To update the existing list, call button.menu.remove() and button.menu = null
I tried the solution from TinyMCE forum, but I found it buggy. For example, when I tried to alter the first ListBox multiple times, only the first time took effect. Also first change to that box right after dialogue popped up didn't take any effect.
But to the solution:
Do not call button.menu.remove();
Also, the "hack" for getting button reference is quite unnecessary. Your job can be done simply using:
var button = win.find("#button")[0];
With these modification, my ListBoxes work just right.
Whole dialogue function:
function ShowDialog() {
var val;
win = editor.windowManager.open({
title: 'title',
body: {type: 'form',
items: [
{type: 'listbox',
name: 'categorybox',
text: 'pick one',
value: 0,
label: 'Section: ',
values: categories,
onselect: setValuebox(this.value())
},
{type: 'listbox',
name: 'valuebox',
text:'pick one',
value: '',
label: 'Page: ',
values: pagelist[0],
onselect: function(e) {
val = this.value();
}
}
]
},
onsubmit: function(e) {
//do whatever
}
});
var valbox = win.find("#valuebox")[0];
function setValuebox(i){
//feel free to call ajax
valbox.value(null);
valbox.menu = null;
valbox.settings.menu = pagelist[i];
// you can also set a value from pagelist[i]["values"][0]
}
}
categories and pagelist are JSONs generated from DB before TinyMCE load. pagelist[category] = data for ListBox for selected category. category=0 means all.
Hope I helped somebody, because I've been struggling this for hours.
It looks like the tinyMCE version that is included in wordpress 4.3 changed some things, and added a state object that caches the initial menu, so changing the menu is not enough anymore.
One will probably have to update the state object as well. Here is an example of updating the menu with data coming from an ajax request:
editor.addButton('shortcodes', {
icon: 'icon_shortcodes',
tooltip: 'Your tooltip',
type: 'menubutton',
onPostRender: function() {
var ctrl = this;
$.getJSON( ajaxurl , function( menu) {
// menu is the array containing your menu items
ctrl.state.data.menu = ctrl.settings.menu = menu;
});
}
});
As far as I can tell, these other approaches are broken in TinyMCE 4.9.
After spending most of the day tinkering to fix my own usage of these approaches, this is the working function I've found:
function updateListbox(win, data) { // win is a tinymce.ui.Window
listbox = win.find('#listbox'); // Substitute your listbox 'name'
formItem = listbox.parent();
listbox.remove();
formItem.append({
label: 'Dynamic Listbox',
type: 'listbox',
name: 'listbox',
values: data
});
}

ExtJS4: Add field to form panel but not want it to be rendered by panel

I have a static html form layout where i add extjs form fields using the "renderTo" config. In order to have form validation and simple submit methods i want to add the fields to a form panel. As the layout is managed by the html frame i don't want the form to be rendered by the panel (panel has html frame as contentEl and this should be used as is).
In extjs3 i could achieve this by adding the field not to the panel but to the BasicForm (formpanel.getForm().add(...)) but in extjs4 this method seems to be gone.
How can i do this using extjs4?
Thanks in advance.
Since you already have a Panel that uses the contentEl to render HTML into its body, I recommend to stick with this approach:
Replace the panel with an Ext.form.Panel instance - the configuration, particularly the contentEl config - can remain unchanged.
The code provided here will override a standard Ext class (Ext.layout.Layout) and introduce support for a 'renderItemTo' config property on child items of any Ext container or panel instance (including Ext.form.Panel).
The value of the config property should be the ID of an already rendered DOM node, e.g. a DIV element that is part of the HTML fragment used in as the contentEl of the parent container's body.
Ext.require(['Ext.layout.Layout'], function() {
Ext.override(Ext.layout.Layout, {
renderItem: function (item, target, position) {
if(item.renderItemTo) {
// render 'renderItemTo' components into the specified DOM element
item.render(item.renderItemTo, 1);
// don't allow container layout to seize the component
item.layoutManagedHeight = 2;
item.layoutManagedWidth = 2;
} else {
// just use standard Ext code for non-renderItemTo components
this.callOverridden(arguments);
}
},
isValidParent: function(item, target, position) {
// signal Ext that we are OK with were our 'renderItemTo' component is right now
// otherwise it would get moved during the layout process
return item.renderItemTo ? true : this.callOverridden(arguments);
}
});
});
Usage:
var panel = Ext.create('Ext.form.Panel', {
contentEl: 'form', // the DOM element ID that holds the HTML fragment for the body
title: 'My FormPanel with special FX',
items: [
{
xtype: 'textfield',
renderItemTo: 'text1', // the ID of a DOM element inside the HTML fragment
fieldLabel: 'Label 1',
},
{
xtype: 'textfield',
renderItemTo: 'text2', // the ID of a DOM element inside the HTML fragment
fieldLabel: 'Label 2'
}
]
});
I uploaded a working example to JSFiddle (note: resize the window if you experience a render problem - this is related to JSFiddle, not my override).
After digging through the layout system of ExtJS 4.1 i implemented a custom layout which moves the items after rendering to the desired position in the fixed markup. The result is the same as for the ExtJS 4.0.7 version from this thread. It seams to work for the ExtJS standard fields. I have some problems with my custom fields though.
Ext.define('Ext.ux.layout.Fixed', {
extend: 'Ext.layout.container.Auto',
alias: 'layout.uxfixed',
afterRenderItem: function(item) {
// move items with renderToFixedMarkup to desired position
if (item.renderToFixedMarkup) {
var target = Ext.getDom(item.renderToFixedMarkup);
this.moveItem(item, target);
}
},
isValidParent: function(item, target, position) {
// items with renderToFixedMarkup property are always positioned correctly
return (item.renderToFixedMarkup) ? true : this.callOverridden(arguments);
}
});
It can be used by setting "layout: 'uxfixed'" on the panel and the "renderToFixedMarkup" config on the items.

Extjs grid with multiselect feature to retrieve value of selected lists

Let's say I have a grid with multiselect option on, when user selects 4 lists and wants to get the values ( alerted on screen) how would I do that? And how would I disable buttons untill at least one list is selected?
All questions you've asked are answered many times already. Also there are good ExtJS examples on sencha.com. For example list view grid shows multiple select and editable grid with writable store shows button enable on click. But THE MOST important is documentation! Let me explain functionality on following code. Most of it is from list view example.
This grid gets JSON from list.php which has following structure
{"authors":[{"surname":"Autho1"},{"surname":"Autho2"}]}
And the grid:
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.panel.*'
]);
Ext.onReady(function(){
// Here i've definned simple model with just one field
Ext.define('ImageModel', {
extend: 'Ext.data.Model',
fields: ['surname']
});
var store = Ext.create('Ext.data.JsonStore', {
model: 'ImageModel',
proxy: {
type: 'ajax',
url: 'list.php',
reader: {
type: 'json',
root: 'authors'
}
}
});
store.load();
var listView = Ext.create('Ext.grid.Panel', {
id: 'myPanel', // Notice unique ID of panel
width:425,
height:250,
collapsible:true,
renderTo: Ext.getBody(),
store: store,
multiSelect: true,
viewConfig: {
emptyText: 'No authors to display'
},
columns: [{
text: 'File',
flex: 50,
// dataIndex means which field from model to load in column
dataIndex: 'surname'
}],
dockedItems: [{
xtype: 'toolbar',
items: [{
// This button will log to console authors surname who are selected
// (show via firebug or in chrome js console for example)
text: 'Show selected',
handler: function() {
// Notice that i'm using getCmp(unique Id of my panel)
// to get panel regerence. I could also use
// this.up('toolbar').up('myPanel')
// see documentation for up() meaning
var selection = Ext.getCmp('myPanel').getSelectionModel().getSelection();
for (var i=0; i < selection.length; i++) {
console.log(selection[i].data.surname);
}
}
},{
text: 'Disabled btn',
id: 'myHiddenBtn', // Notice unique ID of my button
disabled: true // disabled by default
}]
}]
});
// Here i'm waiting for event which is fired
// by grid panel automatically when you click on
// any item of grid panel. Then I lookup
// my button via unique ID and set 'disabled' property to false
listView.on('itemclick', function(view, nodes){
Ext.getCmp('myHiddenBtn').setDisabled(false);
});
});
I didn't knew how to do this from top of my head, but I used documentation and the result works ;-). See Grid panel docs for more information.