CKEditor - add in custom attribute - ckeditor

In CKEditor I'm having issues adding some dataProcessor rules.
I have a custom plugin defined in ckeditor/plugsin/x
I have added the plugin name to config.js extraPlugins
My plugin looks like
CKEDITOR.plugins.add('x',
{
init:function(editor)
{
editor.dataProcessor.htmlFilter.addRules(
{
elements :
{
div : function( element )
{
element.setAttribute("x","y");
}
}
});
editor.dataProcessor.dataFilter.addRules(
{
elements :
{
div : function( element )
{
element.setAttribute("x","y");
}
}
});
});
However it doesn't insert the attribute.
Am I doing something wrong here?

CKEDITOR.dataProcessor works with CKEDITOR.htmlParser.element instead of CKEDITOR.dom.element. CKEDITOR.htmlParser.element is not a real DOM element but an abstract object to make parsing and filtering much easier. It has own set of methods and attributes.
Also note that dataFilter works on input data (what comes to the editor) while htmlFilter deals with output data (content produced by the editor).
You should also get used to Allowed Content Filter because quite likely you need to configure it to have the editor working properly, i.e. config.extraAllowedContent = 'div[x]'.
See the fiddle.
CKEDITOR.replace( 'editor', {
extraAllowedContent: 'div',
on: {
pluginsLoaded: function() {
this.dataProcessor.dataFilter.addRules( {
elements: {
div: function( el ) {
console.log( 'processing', el, 'in dataFilter' );
el.attributes.datafilter = 'x';
}
}
} );
this.dataProcessor.htmlFilter.addRules( {
elements: {
div: function( el ) {
console.log( 'processing', el, 'in htmlFilter' );
el.attributes.htmlfilter = 'y';
}
}
} );
}
}
} );

Related

Adding custom validation on checkout field country

I need to add a validation on the country field in all place where it can be used.
For registration or address editing it work fine but in checkout I tried several method but nothing worked. I want the validation on the field of the new shipping address form.
I add my validation in an js file countryValidation.js :
Same script for registration or edit address and it work fine
define([
'jquery',
'jquery/ui',
'mage/validation',
'mage/translate',
'domReady!'
], function($){
'use strict';
return function(validator) {
$.validator.addMethod(
"validate-country",
function(value, element) {
if (value === "FR") {
var zipValue = $('input[name="postcode"]').val();
if (zipValue) {
return !(zipValue.startsWith("97") || zipValue.startsWith("98"));
}
}
return true;
},
$.mage.__("You cannot choose France for DOM-TOM Zip Code")
);
return validator;
}
});
I registered it in requirejs-config.js in my module :
var config = {
config: {
mixins: {
'Magento_Ui/js/lib/validation/validator': {
'Gone_Customer/js/countryValidation': true
}
}
}
};
For adding validation to checkout method I tried different method
-> Method A : With a plugin
class AddCountryValidation
{
public function afterProcess(
\Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
array $jsLayout
) {
// Country ID
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children']['country_id']['validation']['validate-country'] = true;
return $jsLayout;
}
}
-> Method B : add validation rule in attribute
In customer_eav_attribute in attribute country_id for validate_rules I added {"validate-country": true}
When I validate the form I have no validation error when I should have one.
Can you tell me if I'm missing something please?
Thank you for your response in the meantime I found something that worked.
I had to do a seperate js validation only for checkout :
define(['mage/translate', "jquery"], function($t, $) {
'use strict';
return function(rules) {
rules['validate-country'] = {
handler: function (value) {
if (value === "FR") {
var zipValue = $('input[name="postcode"]').val();
if (zipValue) {
return !(zipValue.startsWith("97") || zipValue.startsWith("98"));
}
}
return true;
},
message: $t('You cannot choose France for DOM-TOM Zip Code')
};
return rules;
};
});
And in my mixin I change Magento_Ui/js/lib/validation/validator for Magento_Ui/js/lib/validation/rules then my plugin method was working !
Please try this
define([
'jquery',
'jquery/validate'
], function($){
'use strict';
return function(validator) {
validator.addRule(
"validate-country",
function(value) {
if (value === "FR") {
var zipValue = $('input[name="postcode"]').val();
if (zipValue) {
return !(zipValue.startsWith("97") || zipValue.startsWith("98"));
}
}
return false;
},
$.mage.__("You cannot choose France for DOM-TOM Zip Code")
);
return validator;
}
});
I have used return false you can modify according to your need.Tested on version 2.2.2

In CKEditor5, In a downcast converter, how to insert a view element at the beginning of another view element?

I want to add "decoration" (non semantic) elements into the editing view at specific positions.
I made a small change in the Block-Widget Plugin given in the CKEditor5 tutorial
https://ckeditor.com/docs/ckeditor5/latest/framework/guides/tutorials/implementing-a-block-widget.html
I added 3 lines ("const badge" etc...) into the downcast converter code:
_defineConverters() {
const conversion = this.editor.conversion;
// <simpleBox> converters
conversion.for( 'upcast' ).elementToElement( {
model: 'simpleBox',
view: {
name: 'section',
classes: 'simple-box'
}
} );
conversion.for( 'dataDowncast' ).elementToElement( {
model: 'simpleBox',
view: {
name: 'section',
classes: 'simple-box'
}
} );
conversion.for( 'editingDowncast' ).elementToElement( {
model: 'simpleBox',
view: ( modelElement, viewWriter ) => {
const section = viewWriter.createContainerElement( 'section', { class: 'simple-box' } );
const badge = viewWriter.createContainerElement( 'span', { class: 'cause-badge', style: 'font-weight: bold' } );
viewWriter.insert( viewWriter.createPositionAt( badge, 0 ), viewWriter.createText('Simple Box') );
viewWriter.insert( viewWriter.createPositionAt( section, 0), badge );
return toWidget( section, viewWriter, { label: 'simple box widget' } );
}
} );
//....
I was expecting the "Simple box" at the begining (top) of the widget, since I use createPositionAt( section, 0).
But instead, I get it at the end (bottom) of the widget (on Firefox).
screenshot on Firefox
I think that the easiest approach is to define this star rating element in the model and add proper conversion for it.
For editing pipeline you'd need and UIElement that allows to render in the editor editing area arbitrary HTML using render function. You can check some examples of using render function in custom UI elements POC.
Simple stub of such functionality below:
// Define schema element
schema.register( 'simpleBoxStars', {
allowIn: 'simpleBox',
allowAttributes: [ 'rating' ]
} );
conversion.for( 'upcast' ).elementToElement( {
model: ( viewElement, writer ) => {
return writer.createElement( 'simpleBoxStars', { rating: viewElement.getAttribute( 'data-rating' ) } );
},
view: {
name: 'span',
classes: 'simple-box-stars'
}
} );
conversion.for( 'editingDowncast' ).elementToElement( {
model: 'simpleBoxStars',
view: ( modelElement, writer ) => {
const uiSpan = writer.createUIElement( 'span', { class: 'simple-box-stars' }, function( domDocument ) {
const domElement = this.toDomElement( domDocument );
// Customize this for you needs
domElement.innerHTML = `This many stars: <span class="stars">${ modelElement.getAttribute( 'rating' ) }</span>`;
//
domElement.addEventListener( 'click', evt => {
// detect which star was clicked
editor.model.change( writer => {
// Change model elment value, ie by using .setAttribute()
} );
} );
return domElement;
} );
return uiSpan;
}
} );
// For editor.getData() just output the data normally:
conversion.for( 'dataDowncast' ).elementToElement( {
model: 'simpleBoxStars',
view: {
name: 'span',
classes: 'simple-box-stars'
}
} );

CKEditor: strip element without attributes?

I've configured extraAllowedContent in "config.js" to allow div elements with specific classes, per the Advanced Content Filtering guide, which is working.
However, I need to strip any div elements that have no attributes. Is this possible?
You can specify config.disallowedContent with match function:
CKEDITOR.replace( 'editor1', {
disallowedContent: {
div: {
match: function( el ) {
return CKEDITOR.tools.isEmpty( el.attributes );
}
}
}
} );
While it correctly filters out the contents, for some reason (a bug), it also disables the Div plugin and its dialog. Thus I'd rather suggest something like this at the moment:
CKEDITOR.replace( 'editor1', {
on: {
pluginsLoaded: function( evt ) {
var editor = evt.editor,
rules = {
elements: {
div: function( el ) {
if ( CKEDITOR.tools.isEmpty( el.attributes ) ) {
// Return false to get rid of an element with children.
return false;
// The element can also be removed preserving children.
// el.replaceWithChildren();
}
},
}
};
// Filter what comes out of an editor.
editor.dataProcessor.htmlFilter.addRules( rules );
// Filter what comes into an editor.
editor.dataProcessor.dataFilter.addRules( rules );
}
}
} );

TypeScript kendoTreeView change / select class method callback

I've been searching around to find why Kendo callbacks are not working with TypeScript but I couldn't find something useful.
So my question is how can kendo use class method as a callback?
Ex:
$("#ipt_tree").kendoTreeView(
{
dataSpriteCssClassField: "sprite",
dataSource: data,
template: "<span data-oid='#= item.oid#'>#= item.text#</span>",
//change: this.Tree_Item_Selected, //doens't get even called
//change: ( item: any ): void => //'this' is not 'this' of the class
//{
// this.Tree_Item_Selected( item );
//}
change: function( item: any ) //using compiler this variable
{
_this.Tree_Item_Selected( item );
}
});
The only solution I've found is to use the _this variable that compiler makes.
Now for the jquery the method callbacks work perfectly.
$( "#ipb_aci_button_edit" ).show().on( "click", this.Info_OnClick_Edit );
private Info_OnClick_Edit = (): void =>
{
//'this' is correct
}
If using the current version of TypeScript (1.0RC), you create a class:
class Demo {
private Tree_Item_Selected(item:any) { }
public Create_Tree(data:any) {
var kendoSettings = {
dataSpriteCssClassField: "sprite",
dataSource: data,
template: "<span>#= item.text#</span>",
change: ( item: any ): void =>
{
this.Tree_Item_Selected( item );
},
change2: function( item: any )
{
_this.Tree_Item_Selected( item );
}
};
}
}
And compile that to JavaScript, the functions change and change2 in the example code both produce the exact same code block:
change: function (item) {
_this.Tree_Item_Selected(item);
},
change2: function (item) {
_this.Tree_Item_Selected(item);
}
The only difference is that the second one produces an error that _this is not found.
In the second example, it's working because it's captured the this correctly. However, you might want to consider a different syntax:
$( "#ipb_aci_button_edit" ).show().on( "click",
(e:JQueryEventObject) => { this.Handle_Info_OnClick_Edit(e) } );
private Handle_Info_OnClick_Edit(e:JQueryEventObject): void
{
// 'this' is correct as it was captured by the event handler code
}

How to default p element to "Normal" style in CKEditor

If I add a paragraph style to the CKEdtior
eg:
format_p: { element : 'p', attributes : { 'style' : 'FONT-SIZE:16px;color:#000000;FONT-STYLE:normal;FONT-FAMILY:Arial, Helvetica, sans-serif;font-weight:normal;' } }
The default style when pressing the enter key is blank. However, if I set the style to "Normal" the style is applied and subsequent p's created by clicking the enter key include the style above.
What I want is for all paragraphs (tag 'p') to use the "Normal" style by default. Is there a way to achieve this?
I think you have use 'contentsCss', have you try 'dataProcessor' like this :
CKEDITOR.on('pluginsLoaded', function (event) {
event.editor.dataProcessor.dataFilter.addRules({
elements: {
p: function (element) {
// element.attributes
}
}
});
event.editor.dataProcessor.htmlFilter.addRules ({
elements: {
p: function (element) {
// element.attributes ...
}
}
});
});

Resources