Classes for Apostrophe Rich Text Widget - ckeditor

I've run into an issue passing classes to the Apostrophe Rich Text Widget. I've updated the sanitizeHtml document, and I'm able to pass one custom class, but as soon as I try to add multiple classes in the widget stops working (at least the style selector does).
{{ apos.singleton(data.page, 'servicesSubtitle', 'apostrophe-rich-text', {
toolbar: [ 'Styles' ],
styles: [
{
name: 'Services Subtitle',
element: 'h3',
attributes: {
class: 'sub-title primary-color'
}
}
],
class
controls: {
movable: false,
removable: false
}
}) }}
This works if I only have:
attributes: {
class: 'sub-title'
}
but doesn't when trying to pass two classes. I'm assuming there's something wonky with passing a space to that parameter, since it always grabs the first chunk of text, but I could be totally wrong about that. I figure I'm missing something really obvious, but cant figure it out or find any docs about it.
Any help would be appreciated!

This is a known CKEditor bug that the Apostrophe team hasn't quite pinned down. Multiple classes WILL work in certain cases (change the order, try different phrases)
This is a crummy answer. We'd love a contribution!

Related

CKEditor 5 - Using HTML inside mentions

I have been using mentions in CKEditor 5 to tag certain system variables. A typical tag looks like as:
<span contenteditable="false" class="mention document_variable" style="color:var(--ck-color-mention-text);" data-mention="#ApprovedCosts" data-documentid="185" data-version="-1" data-container="#Variable-tab-textarea" href="javascript:void(0)">
#ApprovedCosts
</span>
When I try to render the following (the idea is to show the variable value when the user clicks preview, while he continues editing):
<span contenteditable="false" class="mention document_variable" style="color:var(--ck-color-mention-text);" data-mention="#ApprovedCosts" data-documentid="185" data-version="-1" data-container="#Variable-tab-textarea" href="javascript:void(0)">
<p>Nice rendered <b>html</b></p>
</span>
CKEditor goes bonkers.
My requirement is to show a nicely formatted variable name inside the tag. I know I can control via CSS, but there could be a situation where I might end-up rendering a small table (if variable points to a data set), etc.
Help will be appreciated.
Cheers.
Generally speaking, CKEditor 5 documentations refrain from going with your basic plain HTML approach, as seen in the comments:
This plugin customizes the way mentions are handled in the editor model and data.
Instead of a classic <span class="mention"></span>,
Within their mentions documentation (by the way, highly suggested to take a look at - it's very well documented with lots of useful examples in case you're stuck!), they're defining a ClassicEditor (to be precise, they want to mimic a chat platform in which you can tag a user and will receive a list of users in return).
ClassicEditor
.create( document.querySelector( '.chat__editor' ), {
extraPlugins: [ Essentials, Paragraph, Mention, MentionLinks, Bold, Italic, Underline, Strikethrough, Link ],
toolbar: {
items: [
'bold', 'italic', 'underline', 'strikethrough', '|', 'link', '|', 'undo', 'redo'
]
},
mention: {
feeds: [
{
marker: '#',
feed: [
{ id: '#cflores', avatar: 'm_1', name: 'Charles Flores' },
[...]
],
itemRenderer: customItemRenderer
[...]
After the mention object is created, they're calling the customItemRenderer function. This infrastructure could nearly identical be copied. For your part the function MentionLinks is important, as you can customize how mentions are handled, i.e. let's take a look at their example:
function MentionLinks( editor ) {
editor.conversion.for( 'upcast' ).elementToAttribute( {
view: {
name: 'a',
key: 'data-mention',
classes: 'mention',
attributes: {
href: true
}
},
model: {
key: 'mention',
value: viewItem => editor.plugins.get( 'Mention' ).toMentionAttribute( viewItem )
},
converterPriority: 'high'
} );
Basically, you can customize all the properties within the MentionLinks function. CKEditor 5 did a good job documenting the mention plugin very hierarchically:
The documentation can be found here.
On a slight off-topic note I noticed your code passage <p>Nice rendered <b>html</b><p> contains little formality issues, i.e. you need to close the <p> tag by assigning a close tag </p>:
<p>Nice rendered <b>html</b></p>
Though I'm pretty sure that's not the error as you're talking about HTML in general and not only this code passage.
I think your main issue is your tags. Your paragraph tags (<p>) don't have a closing tag, only two opening tags. It should be <p>***your text here***</p>. If this doesn't fix the issue, please leave a comment.

Elegant way to change view schema in CKEditor5

I'm looking for a way to change the view schema/tags used by CKE5 while trying not to reimplement everything. So basically the question is what is the best way to change for example the <strong> element to <b> in the editor.
My current solution is to change the *editing.js file, and the base plugin file to include the modified Editing plugin instead of the original. This works nicely, however, I'm wondering if there is a way to reduce the number of lines of code needed to accomplish this task.
So my solution currently looks like this:
newbold.js:
static get requires() {
return [ NewBoldEditing, BoldUI ];
}
and newboldediting.js:
editor.conversion.attributeToElement({
model: 'bold',
view: 'b'
});
Is there a better way of doing this (that preferably wouldn't involve reimplementing this many classes)?
You could provide only a very simple plugin that overwrites the default bold attribute conversion.
class BoldToB extends Plugin {
init() {
this.editor.conversion.attributeToElement( {
model: 'bold',
view: 'b',
converterPriority: 'high'
} );
}
}
Here's a fiddle for you to test: https://jsfiddle.net/u3zyw67v/
Note that in the fiddle I don't have access to Plugin class so I had to add constructor(). You don't need to do that if you extend Plugin class.

Ckeditor plugin configuration not working

I have tried to add justify plugin to be able to align text right, left or centre. But after following the instructions in the documentation (http://apostrophecms.org/docs/tutorials/howtos/ckeditor.html), I wonder if the plugin should be located in a specific folder (mine is at public/modules/apostrophe-areas/js/ckeditorPlugins/justify/), as it disappears when the site is loaded, but if I include it in some other folder such as public/plugins/justify still doesn't work.
This is my code just in case: (located at lib/modules/apostrophe-areas/public/js/user.js)
apos.define('apostrophe-areas', {
construct: function(self, options) {
// Use the super pattern - don't forget to call the original method
var superEnableCkeditor = self.enableCkeditor;
self.enableCkeditor = function() {
superEnableCkeditor();
// Now do as we please
CKEDITOR.plugins.addExternal('justify', '/modules/apostrophe-areas/js/ckeditorPlugins/justify/', 'plugin.js');
};
}
});
Also, it would be nice to know how the plugin should be called at the Toolbar settings for editable widgets.
Thanks!
The URL you need is:
/modules/my-apostrophe-areas/js/ckeditorPlugins/justify/
The my- prefix is automatically prepended so that the public folders of both the original apostrophe-areas module and your project-level extension of it can have a distinct URL. Otherwise there would be no way for both to access their user.js, for instance.
I'll add this note to the HOWTO in question, which currently handwaves the issue by stubbing in a made-up URL.
As for how the plugin should be called, use the toolbar control name exported by that plugin — that part is a ckeditor question, not really an Apostrophe one. But looking at the source code of that plugin they are probably JustifyLeft, JustifyCenter, JustifyRight and JustifyBlock.
It turns out that it's not enough to simply call CKEDITOR.plugins.addExternal inside apostophe-areas. You also need to override self.beforeCkeditorInline of the apostrophe-rich-text-widgets-editor module and explicitly call self.config.extraPlugins = 'your_plugin_name';.
Here's what I ended up with:
In lib/modules/apostrophe-areas/public/js/user.js:
apos.define('apostrophe-areas', {
construct: function(self, options) {
// Use the super pattern - don't forget to call the original method
var superEnableCkeditor = self.enableCkeditor;
self.enableCkeditor = function() {
superEnableCkeditor();
// Now do as we please
CKEDITOR.plugins.addExternal('justify', '/modules/my-apostrophe-areas/js/ckeditorPlugins/justify/', 'plugin.js');
};
}
});
then in in lib/modules/apostrophe-rich-text-widgets/public/js/editor.js:
apos.define('apostrophe-rich-text-widgets-editor', {
construct: function(self, options) {
self.beforeCkeditorInline = function() {
self.config.extraPlugins = 'justify';
};
}
});
For some reason doing CKEDITOR.config.extraPlugins = 'justify' inside apostrophe-areas does not work, probably due to the way how CKEDITOR is initialized;
One more thing: this particular plug-in (justify, that is) does not seem to follow the button definition logic. It has button icons defined as images, whereas CKEditor 4.6 used in Apostrophe CMS 2.3 uses font-awesome to display icons. It means that the icons that ship with the justify module won't be displayed and you'll have to write your own css for each button individually.
There is another issue which you'll probably face when you finally enable the justify buttons. The built-in html sanitizer will be strip off the styles justify adds to align the content.
Apostrophe CMS seems to be using sanitize-html to sanitize the input, so changing CKEditor settings won't have any effect. To solve the issue, add the following to your app.js:
'apostrophe-rich-text-widgets': {
// The standard list copied from the module, plus sup and sub
sanitizeHtml: {
allowedAttributes: {
a: ['href', 'name', 'target'],
img: ['src'],
'*': ['style'] //this will make sure the style attribute is not stripped off
}
}
}
Thank you both for your help. After following both approaches of: locating the plugin at my-apostrophe-areas folder as well as editing editor.js on the apostrophe-rich-text widget (the sanitize.html file was already using that configuration), I got the plugin working. However, I was still having the issue with the icons.
I fixed that adding the Font Awesome icons that correspond to align-justify, align-right, align-left and align-center at the end of public/modules/apostrophe-areas/js/vendor/ckeditor/skins/apostrophe/editor.less

Ckeditor bullets and numberedlists are not displaying in result

I have applied ckeditor on a textarea . it display bold and italic txext as it is but does not display numbered lists and bulletlist in results when i fetch from database. Instaead it encapsulte whole text in ol/ul tags.
A secretary, personal assistant, or administrative assistant is a person whose work consists of supporting management, including executives, using a variety of project management,
communication, or organizational skills. These functions may be entirely carried out to assist one other employee or may be for the benefit of
more than one. In other situations a secretary is an officer of a society or organization
who deals with correspondence, admits new members, and organizes official meetings and events
Jquery code to apply ckeditor:
$(document).ready(function(){
//Applying ckeditor on provider msg textarea.
CKEDITOR.replace( 'responsibilities',
{
uiColor: '#6C518F',
toolbar: [
{ name: 'basicstyles', items : [ 'Bold','Italic','TextColor',"BGColor", 'NumberedList','BulletedList' ] },
{ name: 'tools', items : [ 'Maximize','-' ] }
],
removePlugins : 'elementspath'
});
})
It's caused most likely by:
Either some backend filtering which strips lists.
Or styling of your webpage, which sets list-style-type to none.
If none of these is true, please clarify your question.
I also faced same issue while using ckeditor in Angular. When we fetch the data from the editor it shows with ul/li tag. Actually it is applying the bulleted list to the editor but it is not visible to us. So we just need to increase the padding so that it can be visible to us.
.ck-editor__editable_inline {
padding: 0 30px !important;
}
Removing list-style-type to none resolved my issues.

Mootools: inject vs adopt

I want to dynamically add some preconfigured HTML-Elements in use of a 'click'-event with mootools.
So I can make it work with my basic knowledge, although it isn´t very nifty. I coded this so far...
This is my preconfigured element, with some text, a classname and some event, cause i wanna have events already added, when it´s inserted into my container:
var label = new Element('label', {
'text': 'Label',
'class': 'label',
'events': {
'click': function(el){
alert('click');
}
}
});
Here is my function, which adds the label-Element:
function addText(){
$('fb-buildit').addEvent('click', function(){
row.adopt(label, textinput, deletebtn);
$('the-form').adopt(row.clone());
row.empty();
/*
label.clone().inject($('the-form'));
textinput.inject($('the-form'));
deletebtn.inject($('the-form'));
*/
});
}
The second part which uses inject also works, but there, my click-Event, which fires the "alert('click')" works too. The method with adopt doesn´t add any event to my label Object, when its inserted in the dom.
Can anyone help me with this. I just wanna know why adobt ignores my "events" settings and inject doesn´t.
Thanks in advance.
(sorry for my english ^^)
you go label.clone().inject but row.adopt(label) and not row.adopt(label.clone()) -
either way. .clone() does not cloneEvents for you - you need to do that manually.
var myclone = label.clone();
myclone.cloneEvents(label);
row.adopt(label);
this is how it will work
as for why that is, events are stored in the Element storage - which is unique per element. mootools assigns a uid to each element, eg, label = 1, label.clone() -> 2, label.clone() -> 3 etc.
this goes to Storage[1] = { events: ... } and so forth. cloning an element makes for a new element.uid so events don't work unless you implicitly use .cloneEvents()
you are sometimes not doing .clone() which works because it takes the ORIGINAL element along with its storage and events.
suggestion consider looking into event delegation. you could do
formElement.addEvent("click:relay(label.myLabel)", function(e, el) {
alert("hi from "+ el.uid);
});
this means no matter how many new elements you add, as long as they are of type label and class myLabel and children of formElement, the click will always work as the event bubbles to the parent.

Resources