From bfa77b3b255ba3336bb69d299d90eaee966912ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 15:59:00 +0100 Subject: [PATCH 01/21] Unblock cut inside exception marker. --- src/restrictededitingmodeediting.js | 4 +++- tests/restrictededitingmodeediting.js | 33 ++++++++++----------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 2d32f396..c9462b5d 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -90,7 +90,9 @@ export default class RestrictedEditingModeEditing extends Plugin { }, { priority: 'highest' } ); this.listenTo( this.editor.editing.view.document, 'clipboardOutput', ( evt, data ) => { if ( data.method == 'cut' ) { - evt.stop(); + if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { + evt.stop(); + } } }, { priority: 'highest' } ); diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index a17c211b..a3e7ea81 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -590,7 +590,7 @@ describe( 'RestrictedEditingModeEditing', () => { } ); describe( 'clipboard', () => { - let model, viewDoc; + let viewDoc; beforeEach( async () => { editor = await VirtualTestEditor.create( { plugins: [ Paragraph, Typing, Clipboard, RestrictedEditingModeEditing ] } ); @@ -598,8 +598,8 @@ describe( 'RestrictedEditingModeEditing', () => { viewDoc = editor.editing.view.document; } ); - afterEach( () => { - return editor.destroy(); + afterEach( async () => { + await editor.destroy(); } ); describe( 'cut', () => { @@ -619,25 +619,17 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo []bar baz' ); } ); - it( 'should be blocked inside exception marker', () => { + it( 'should cut selected content inside exception marker', () => { setModelData( model, '[]foo bar baz' ); const firstParagraph = model.document.getRoot().getChild( 0 ); - const spy = sinon.spy(); - viewDoc.on( 'clipboardOutput', spy, { priority: 'high' } ); - model.change( writer => { - writer.addMarker( 'restrictedEditingException:1', { - range: writer.createRange( - writer.createPositionAt( firstParagraph, 4 ), - writer.createPositionAt( firstParagraph, 7 ) - ), - usingOperation: true, - affectsData: true - } ); - } ); + addExceptionMarker( 4, 7, firstParagraph, 1 ); model.change( writer => { - writer.setSelection( firstParagraph, 5 ); + writer.setSelection( writer.createRange( + writer.createPositionAt( firstParagraph, 5 ), + writer.createPositionAt( firstParagraph, 6 ) + ) ); } ); viewDoc.fire( 'clipboardOutput', { @@ -647,8 +639,7 @@ describe( 'RestrictedEditingModeEditing', () => { method: 'cut' } ); - sinon.assert.notCalled( spy ); - assertEqualMarkup( getModelData( model ), 'foo b[]ar baz' ); + assertEqualMarkup( getModelData( model ), 'foo b[]r baz' ); } ); } ); @@ -752,7 +743,7 @@ describe( 'RestrictedEditingModeEditing', () => { } ); describe( 'exception highlighting', () => { - let model, view; + let view; beforeEach( async () => { editor = await VirtualTestEditor.create( { @@ -985,7 +976,7 @@ describe( 'RestrictedEditingModeEditing', () => { } ); describe( 'exception cycling with the keyboard', () => { - let model, view, domEvtDataStub; + let view, domEvtDataStub; beforeEach( async () => { editor = await VirtualTestEditor.create( { From e671ee28263c8a8e00eef02352a43cc766947774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 16:03:48 +0100 Subject: [PATCH 02/21] Add test cases. --- tests/restrictededitingmodeediting.js | 38 +++++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index a3e7ea81..e69816f2 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -619,19 +619,41 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo []bar baz' ); } ); - it( 'should cut selected content inside exception marker', () => { - setModelData( model, '[]foo bar baz' ); + it( 'should cut selected content inside exception marker (selection inside marker)', () => { + setModelData( model, 'foo b[a]r baz' ); const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph, 1 ); + viewDoc.fire( 'clipboardOutput', { + content: { + isEmpty: true + }, + method: 'cut' + } ); + + assertEqualMarkup( getModelData( model ), 'foo b[]r baz' ); + } ); + + it( 'should cut selected content inside exception marker (selection touching marker start)', () => { + setModelData( model, 'foo [ba]r baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); addExceptionMarker( 4, 7, firstParagraph, 1 ); - model.change( writer => { - writer.setSelection( writer.createRange( - writer.createPositionAt( firstParagraph, 5 ), - writer.createPositionAt( firstParagraph, 6 ) - ) ); + viewDoc.fire( 'clipboardOutput', { + content: { + isEmpty: true + }, + method: 'cut' } ); + assertEqualMarkup( getModelData( model ), 'foo []r baz' ); + } ); + + it( 'should cut selected content inside exception marker (selection touching marker end)', () => { + setModelData( model, 'foo b[ar] baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph, 1 ); + viewDoc.fire( 'clipboardOutput', { content: { isEmpty: true @@ -639,7 +661,7 @@ describe( 'RestrictedEditingModeEditing', () => { method: 'cut' } ); - assertEqualMarkup( getModelData( model ), 'foo b[]r baz' ); + assertEqualMarkup( getModelData( model ), 'foo b[] baz' ); } ); } ); From 23676c1c7b596e97e411e8a0f8944f2538e90c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 16:04:34 +0100 Subject: [PATCH 03/21] Remove default value. --- tests/restrictededitingmodeediting.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index e69816f2..8a858f88 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -622,7 +622,7 @@ describe( 'RestrictedEditingModeEditing', () => { it( 'should cut selected content inside exception marker (selection inside marker)', () => { setModelData( model, 'foo b[a]r baz' ); const firstParagraph = model.document.getRoot().getChild( 0 ); - addExceptionMarker( 4, 7, firstParagraph, 1 ); + addExceptionMarker( 4, 7, firstParagraph ); viewDoc.fire( 'clipboardOutput', { content: { @@ -637,7 +637,7 @@ describe( 'RestrictedEditingModeEditing', () => { it( 'should cut selected content inside exception marker (selection touching marker start)', () => { setModelData( model, 'foo [ba]r baz' ); const firstParagraph = model.document.getRoot().getChild( 0 ); - addExceptionMarker( 4, 7, firstParagraph, 1 ); + addExceptionMarker( 4, 7, firstParagraph ); viewDoc.fire( 'clipboardOutput', { content: { @@ -652,7 +652,7 @@ describe( 'RestrictedEditingModeEditing', () => { it( 'should cut selected content inside exception marker (selection touching marker end)', () => { setModelData( model, 'foo b[ar] baz' ); const firstParagraph = model.document.getRoot().getChild( 0 ); - addExceptionMarker( 4, 7, firstParagraph, 1 ); + addExceptionMarker( 4, 7, firstParagraph ); viewDoc.fire( 'clipboardOutput', { content: { From 17d4084046614f1fbf86b5dd4691d646f9baed22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 16:14:15 +0100 Subject: [PATCH 04/21] Add failing tests for pasting content. --- tests/restrictededitingmodeediting.js | 44 +++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index 8a858f88..205e5289 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -17,6 +17,7 @@ import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard'; import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting'; import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand'; +import { createDataTransfer } from '@ckeditor/ckeditor5-paste-from-office/tests/_utils/utils'; describe( 'RestrictedEditingModeEditing', () => { let editor, model; @@ -731,35 +732,32 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo []bar baz' ); } ); - it( 'should be blocked inside exception marker', () => { - setModelData( model, '[]foo bar baz' ); - const firstParagraph = model.document.getRoot().getChild( 0 ); - const spy = sinon.spy(); - viewDoc.on( 'clipboardInput', spy, { priority: 'high' } ); + describe( 'collapsed selection', () => { + it( 'should paste text inside exception marker', () => { + setModelData( model, 'foo b[]ar baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); - model.change( writer => { - writer.addMarker( 'restrictedEditingException:1', { - range: writer.createRange( - writer.createPositionAt( firstParagraph, 4 ), - writer.createPositionAt( firstParagraph, 7 ) - ), - usingOperation: true, - affectsData: true + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) } ); - } ); - model.change( writer => { - writer.setSelection( firstParagraph, 5 ); + assertEqualMarkup( getModelData( model ), 'foo b[XXX]ar baz' ); } ); + } ); - viewDoc.fire( 'clipboardInput', { - dataTransfer: { - getData: sinon.spy() - } - } ); + describe( 'non-collapsed selection', () => { + it( 'should paste text inside exception marker', () => { + setModelData( model, 'foo b[a]r baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); - sinon.assert.notCalled( spy ); - assertEqualMarkup( getModelData( model ), 'foo b[]ar baz' ); + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( getModelData( model ), 'foo b[XXX]ar baz' ); + } ); } ); } ); } ); From 5137ee367970ed0c06ed6cd5999245dfb7425cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 16:16:27 +0100 Subject: [PATCH 05/21] Use editor variable. --- src/restrictededitingmodeediting.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index c9462b5d..4f800e4d 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -85,10 +85,10 @@ export default class RestrictedEditingModeEditing extends Plugin { editor.keystrokes.set( 'Shift+Tab', getCommandExecuter( editor, 'goToPreviousRestrictedEditingException' ) ); // Block clipboard completely in restricted mode. - this.listenTo( this.editor.editing.view.document, 'clipboardInput', evt => { + this.listenTo( editor.editing.view.document, 'clipboardInput', evt => { evt.stop(); }, { priority: 'highest' } ); - this.listenTo( this.editor.editing.view.document, 'clipboardOutput', ( evt, data ) => { + this.listenTo( editor.editing.view.document, 'clipboardOutput', ( evt, data ) => { if ( data.method == 'cut' ) { if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { evt.stop(); From 776a230b2862c0a4c48b58b8efd0ef4812c196a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 16:17:47 +0100 Subject: [PATCH 06/21] Extract some variables. --- src/restrictededitingmodeediting.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 4f800e4d..5d4b7010 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -69,6 +69,8 @@ export default class RestrictedEditingModeEditing extends Plugin { */ init() { const editor = this.editor; + const editingView = editor.editing.view; + const viewDoc = editingView.document; const allowedCommands = editor.config.get( 'restrictedEditing.allowedCommands' ); @@ -85,10 +87,10 @@ export default class RestrictedEditingModeEditing extends Plugin { editor.keystrokes.set( 'Shift+Tab', getCommandExecuter( editor, 'goToPreviousRestrictedEditingException' ) ); // Block clipboard completely in restricted mode. - this.listenTo( editor.editing.view.document, 'clipboardInput', evt => { + this.listenTo( viewDoc, 'clipboardInput', evt => { evt.stop(); }, { priority: 'highest' } ); - this.listenTo( editor.editing.view.document, 'clipboardOutput', ( evt, data ) => { + this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { if ( data.method == 'cut' ) { if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { evt.stop(); @@ -96,8 +98,8 @@ export default class RestrictedEditingModeEditing extends Plugin { } }, { priority: 'highest' } ); - editor.editing.view.change( writer => { - for ( const root of editor.editing.view.document.roots ) { + editingView.change( writer => { + for ( const root of viewDoc.roots ) { writer.addClass( 'ck-restricted-editing_mode_restricted', root ); } } ); From 39b6cd178829407907f4616ccfd17c1792fcef5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 20 Dec 2019 16:37:08 +0100 Subject: [PATCH 07/21] Remove paste from office dependency. --- tests/restrictededitingmodeediting.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index 205e5289..bc1a5866 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -17,7 +17,6 @@ import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard'; import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting'; import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand'; -import { createDataTransfer } from '@ckeditor/ckeditor5-paste-from-office/tests/_utils/utils'; describe( 'RestrictedEditingModeEditing', () => { let editor, model; @@ -1140,4 +1139,12 @@ describe( 'RestrictedEditingModeEditing', () => { } ); } ); } + + function createDataTransfer( data ) { + return { + getData( type ) { + return data[ type ]; + } + }; + } } ); From 81c6c20d39573843361d145a0a03b99c71004403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 13:24:48 +0100 Subject: [PATCH 08/21] Allow pasting text inside exception marker. --- src/restrictededitingmodeediting.js | 4 +++- tests/restrictededitingmodeediting.js | 28 +++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 5d4b7010..e038cc2f 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -88,7 +88,9 @@ export default class RestrictedEditingModeEditing extends Plugin { // Block clipboard completely in restricted mode. this.listenTo( viewDoc, 'clipboardInput', evt => { - evt.stop(); + if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { + evt.stop(); + } }, { priority: 'highest' } ); this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { if ( data.method == 'cut' ) { diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index bc1a5866..389d7c08 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -74,10 +74,7 @@ describe( 'RestrictedEditingModeEditing', () => { expect( model.markers.has( 'restrictedEditingException:1' ) ).to.be.true; - const marker = model.markers.get( 'restrictedEditingException:1' ); - - expect( marker.getStart().path ).to.deep.equal( [ 0, 4 ] ); - expect( marker.getEnd().path ).to.deep.equal( [ 0, 7 ] ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 7 ] ); } ); it( 'should convert multiple ', () => { @@ -90,10 +87,7 @@ describe( 'RestrictedEditingModeEditing', () => { expect( model.markers.has( 'restrictedEditingException:2' ) ).to.be.true; // Data for the first marker is the same as in previous tests so no need to test it again. - const secondMarker = model.markers.get( 'restrictedEditingException:2' ); - - expect( secondMarker.getStart().path ).to.deep.equal( [ 1, 6 ] ); - expect( secondMarker.getEnd().path ).to.deep.equal( [ 1, 11 ] ); + assertMarkerRangePaths( [ 1, 6 ], [ 1, 11 ], 2 ); } ); it( 'should not convert other elements', () => { @@ -716,6 +710,11 @@ describe( 'RestrictedEditingModeEditing', () => { } ); describe( 'paste', () => { + beforeEach( () => { + // Required when testing without DOM using VirtualTestEditor - Clipboard feature scrolls after paste event. + sinon.stub( editor.editing.view, 'scrollToTheSelection' ); + } ); + it( 'should be blocked outside exception markers', () => { setModelData( model, 'foo []bar baz' ); const spy = sinon.spy(); @@ -741,7 +740,8 @@ describe( 'RestrictedEditingModeEditing', () => { dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) } ); - assertEqualMarkup( getModelData( model ), 'foo b[XXX]ar baz' ); + assertEqualMarkup( getModelData( model ), 'foo bXXX[]ar baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); } ); } ); @@ -755,7 +755,8 @@ describe( 'RestrictedEditingModeEditing', () => { dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) } ); - assertEqualMarkup( getModelData( model ), 'foo b[XXX]ar baz' ); + assertEqualMarkup( getModelData( model ), 'foo bXXX[]r baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 9 ] ); } ); } ); } ); @@ -1147,4 +1148,11 @@ describe( 'RestrictedEditingModeEditing', () => { } }; } + + function assertMarkerRangePaths( startPath, endPath, markerId = 1 ) { + const marker = model.markers.get( `restrictedEditingException:${ markerId }` ); + + expect( marker.getStart().path ).to.deep.equal( startPath ); + expect( marker.getEnd().path ).to.deep.equal( endPath ); + } } ); From 84c76fcd19dbf9445aa1c047d8329356a5e835db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 13:31:07 +0100 Subject: [PATCH 09/21] Allow pasting text with attributes tests. --- tests/restrictededitingmodeediting.js | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index 389d7c08..e93b0e0f 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -587,7 +587,9 @@ describe( 'RestrictedEditingModeEditing', () => { let viewDoc; beforeEach( async () => { - editor = await VirtualTestEditor.create( { plugins: [ Paragraph, Typing, Clipboard, RestrictedEditingModeEditing ] } ); + editor = await VirtualTestEditor.create( { + plugins: [ Paragraph, BoldEditing, Typing, Clipboard, RestrictedEditingModeEditing ] + } ); model = editor.model; viewDoc = editor.editing.view.document; } ); @@ -743,6 +745,19 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo bXXX[]ar baz' ); assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); } ); + + it( 'should paste allowed text attributes inside exception marker', () => { + setModelData( model, 'foo b[]ar baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( getModelData( model ), 'foo b<$text bold="true">XXX[]ar baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); + } ); } ); describe( 'non-collapsed selection', () => { @@ -758,6 +773,19 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo bXXX[]r baz' ); assertMarkerRangePaths( [ 0, 4 ], [ 0, 9 ] ); } ); + + it( 'should paste allowed text attributes inside exception marker', () => { + setModelData( model, 'foo b[a]r baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( getModelData( model ), 'foo b<$text bold="true">XXX[]r baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 9 ] ); + } ); } ); } ); } ); From d62bc3ffc79c33f79074f3d3b7e3694dd4d5a5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 13:43:35 +0100 Subject: [PATCH 10/21] Add configuration option to allow attributes in pasted text. --- src/restrictededitingmodeediting.js | 7 ++- tests/restrictededitingmodeediting.js | 64 ++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index e038cc2f..2b771abc 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -41,7 +41,8 @@ export default class RestrictedEditingModeEditing extends Plugin { super( editor ); editor.config.define( 'restrictedEditing', { - allowedCommands: [ 'bold', 'italic', 'link', 'unlink' ] + allowedCommands: [ 'bold', 'italic', 'link', 'unlink' ], + allowedAttributes: [ 'bold', 'italic', 'link' ] } ); /** @@ -92,6 +93,10 @@ export default class RestrictedEditingModeEditing extends Plugin { evt.stop(); } }, { priority: 'highest' } ); + + const allowedAttributes = editor.config.get( 'restrictedEditing.allowedAttributes' ); + editor.model.schema.addAttributeCheck( ( context, attributeName ) => allowedAttributes.includes( attributeName ) ); + this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { if ( data.method == 'cut' ) { if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index e93b0e0f..ae07eac0 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -9,14 +9,16 @@ import { getData as getModelData, setData as setModelData } from '@ckeditor/cked import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view'; import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; +import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting'; -import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils'; +import StrikethroughEditing from '@ckeditor/ckeditor5-basic-styles/src/strikethrough/strikethroughediting'; import Typing from '@ckeditor/ckeditor5-typing/src/typing'; import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard'; import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting'; import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand'; +import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting'; describe( 'RestrictedEditingModeEditing', () => { let editor, model; @@ -588,7 +590,7 @@ describe( 'RestrictedEditingModeEditing', () => { beforeEach( async () => { editor = await VirtualTestEditor.create( { - plugins: [ Paragraph, BoldEditing, Typing, Clipboard, RestrictedEditingModeEditing ] + plugins: [ Paragraph, BoldEditing, ItalicEditing, StrikethroughEditing, Typing, Clipboard, RestrictedEditingModeEditing ] } ); model = editor.model; viewDoc = editor.editing.view.document; @@ -758,6 +760,35 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo b<$text bold="true">XXX[]ar baz' ); assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); } ); + + it( 'should not allow to paste disallowed text attributes inside exception marker', () => { + setModelData( model, 'foo b[]ar baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( getModelData( model ), 'foo bXXX[]ar baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); + } ); + + it( 'should filter out disallowed attributes from other text attributes when pasting inside exception marker', () => { + setModelData( model, 'foo b[]ar baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( + getModelData( model ), + 'foo b<$text bold="true" italic="true">XXX[]ar baz' + ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); + } ); } ); describe( 'non-collapsed selection', () => { @@ -786,6 +817,35 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo b<$text bold="true">XXX[]r baz' ); assertMarkerRangePaths( [ 0, 4 ], [ 0, 9 ] ); } ); + + it( 'should not allow to paste disallowed text attributes inside exception marker', () => { + setModelData( model, 'foo b[a]r baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( getModelData( model ), 'foo bXXX[]r baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 9 ] ); + } ); + + it( 'should filter out disallowed attributes from other text attributes when pasting inside exception marker', () => { + setModelData( model, 'foo b[a]r baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( + getModelData( model ), + 'foo b<$text bold="true" italic="true">XXX[]r baz' + ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 9 ] ); + } ); } ); } ); } ); From 1e36e9cdabfd1ad2b7a85c71ded880bb84fce69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 14:28:34 +0100 Subject: [PATCH 11/21] Add pasting integration tests for non-collapsed selection. --- tests/restrictededitingmodeediting.js | 47 ++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index ae07eac0..1071f2dd 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -719,7 +719,7 @@ describe( 'RestrictedEditingModeEditing', () => { sinon.stub( editor.editing.view, 'scrollToTheSelection' ); } ); - it( 'should be blocked outside exception markers', () => { + it( 'should be blocked outside exception markers (collapsed selection)', () => { setModelData( model, 'foo []bar baz' ); const spy = sinon.spy(); viewDoc.on( 'clipboardInput', spy, { priority: 'high' } ); @@ -734,6 +734,51 @@ describe( 'RestrictedEditingModeEditing', () => { assertEqualMarkup( getModelData( model ), 'foo []bar baz' ); } ); + it( 'should be blocked outside exception markers (non-collapsed selection)', () => { + setModelData( model, '[foo bar baz]' ); + const spy = sinon.spy(); + viewDoc.on( 'clipboardInput', spy, { priority: 'high' } ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: { + getData: sinon.spy() + } + } ); + + sinon.assert.notCalled( spy ); + assertEqualMarkup( getModelData( model ), '[foo bar baz]' ); + } ); + + it( 'should be blocked outside exception markers (non-collapsed selection, starts inside exception marker)', () => { + setModelData( model, 'foo b[ar baz]' ); + const spy = sinon.spy(); + viewDoc.on( 'clipboardInput', spy, { priority: 'high' } ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: { + getData: sinon.spy() + } + } ); + + sinon.assert.notCalled( spy ); + assertEqualMarkup( getModelData( model ), 'foo b[ar baz]' ); + } ); + + it( 'should be blocked outside exception markers (non-collapsed selection, ends inside exception marker)', () => { + setModelData( model, '[foo ba]r baz' ); + const spy = sinon.spy(); + viewDoc.on( 'clipboardInput', spy, { priority: 'high' } ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: { + getData: sinon.spy() + } + } ); + + sinon.assert.notCalled( spy ); + assertEqualMarkup( getModelData( model ), '[foo ba]r baz' ); + } ); + describe( 'collapsed selection', () => { it( 'should paste text inside exception marker', () => { setModelData( model, 'foo b[]ar baz' ); From 9693eda7210cdbdf7a1eb81c96112cb6110fa1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 14:47:19 +0100 Subject: [PATCH 12/21] Allow pasting only text inside restricted editing exception. --- src/restrictededitingmodeediting.js | 5 +++++ tests/restrictededitingmodeediting.js | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 2b771abc..d86ae273 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -96,6 +96,11 @@ export default class RestrictedEditingModeEditing extends Plugin { const allowedAttributes = editor.config.get( 'restrictedEditing.allowedAttributes' ); editor.model.schema.addAttributeCheck( ( context, attributeName ) => allowedAttributes.includes( attributeName ) ); + editor.model.schema.addChildCheck( ( context, childDefinition ) => { + if ( Array.from( context.getNames() ).includes( '$clipboardHolder' ) ) { + return childDefinition.name === '$text'; + } + } ); this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { if ( data.method == 'cut' ) { diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index 1071f2dd..0a633ff9 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -19,6 +19,7 @@ import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard'; import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting'; import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand'; import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting'; +import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting'; describe( 'RestrictedEditingModeEditing', () => { let editor, model; @@ -590,7 +591,9 @@ describe( 'RestrictedEditingModeEditing', () => { beforeEach( async () => { editor = await VirtualTestEditor.create( { - plugins: [ Paragraph, BoldEditing, ItalicEditing, StrikethroughEditing, Typing, Clipboard, RestrictedEditingModeEditing ] + plugins: [ Paragraph, BoldEditing, ItalicEditing, StrikethroughEditing, BlockQuoteEditing, Typing, Clipboard, + RestrictedEditingModeEditing + ] } ); model = editor.model; viewDoc = editor.editing.view.document; @@ -834,6 +837,19 @@ describe( 'RestrictedEditingModeEditing', () => { ); assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); } ); + + it( 'should not allow pasting block elements other then paragraph', () => { + setModelData( model, 'foo b[]ar baz' ); + const firstParagraph = model.document.getRoot().getChild( 0 ); + addExceptionMarker( 4, 7, firstParagraph ); + + viewDoc.fire( 'clipboardInput', { + dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + } ); + + assertEqualMarkup( getModelData( model ), 'foo bXXX[]ar baz' ); + assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); + } ); } ); describe( 'non-collapsed selection', () => { From 3161c60c420dc3003e5205dcc3c8a84b11ae28cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 15:00:21 +0100 Subject: [PATCH 13/21] Move clipboard restrictions to _setupRestrictions method. --- src/restrictededitingmodeediting.js | 62 +++++++++++++++-------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index d86ae273..8c0a5b69 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -71,8 +71,6 @@ export default class RestrictedEditingModeEditing extends Plugin { init() { const editor = this.editor; const editingView = editor.editing.view; - const viewDoc = editingView.document; - const allowedCommands = editor.config.get( 'restrictedEditing.allowedCommands' ); allowedCommands.forEach( commandName => this._allowedInException.add( commandName ) ); @@ -87,31 +85,8 @@ export default class RestrictedEditingModeEditing extends Plugin { editor.keystrokes.set( 'Tab', getCommandExecuter( editor, 'goToNextRestrictedEditingException' ) ); editor.keystrokes.set( 'Shift+Tab', getCommandExecuter( editor, 'goToPreviousRestrictedEditingException' ) ); - // Block clipboard completely in restricted mode. - this.listenTo( viewDoc, 'clipboardInput', evt => { - if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { - evt.stop(); - } - }, { priority: 'highest' } ); - - const allowedAttributes = editor.config.get( 'restrictedEditing.allowedAttributes' ); - editor.model.schema.addAttributeCheck( ( context, attributeName ) => allowedAttributes.includes( attributeName ) ); - editor.model.schema.addChildCheck( ( context, childDefinition ) => { - if ( Array.from( context.getNames() ).includes( '$clipboardHolder' ) ) { - return childDefinition.name === '$text'; - } - } ); - - this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { - if ( data.method == 'cut' ) { - if ( !isRangeInsideSingleMarker( editor, editor.model.document.selection.getFirstRange() ) ) { - evt.stop(); - } - } - }, { priority: 'highest' } ); - editingView.change( writer => { - for ( const root of viewDoc.roots ) { + for ( const root of editingView.document.roots ) { writer.addClass( 'ck-restricted-editing_mode_restricted', root ); } } ); @@ -189,22 +164,51 @@ export default class RestrictedEditingModeEditing extends Plugin { } /** - * Setups additional editing restrictions beyond command toggling. + * Setups additional editing restrictions beyond command toggling: + * + * * delete content range trimming + * * disabling input command outside exception marker + * * restricting clipboard holder to text only + * * restricting text attributes in content * * @private */ _setupRestrictions() { const editor = this.editor; + const model = editor.model; + const viewDoc = editor.editing.view.document; - this.listenTo( editor.model, 'deleteContent', restrictDeleteContent( editor ), { priority: 'high' } ); + this.listenTo( model, 'deleteContent', restrictDeleteContent( editor ), { priority: 'high' } ); - const inputCommand = this.editor.commands.get( 'input' ); + const inputCommand = editor.commands.get( 'input' ); // The restricted editing might be configured without input support - ie allow only bolding or removing text. // This check is bit synthetic since only tests are used this way. if ( inputCommand ) { this.listenTo( inputCommand, 'execute', disallowInputExecForWrongRange( editor ), { priority: 'high' } ); } + + // Block clipboard completely in restricted mode. + this.listenTo( viewDoc, 'clipboardInput', function( evt ) { + if ( !isRangeInsideSingleMarker( editor, model.document.selection.getFirstRange() ) ) { + evt.stop(); + } + }, { priority: 'highest' } ); + this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { + if ( data.method == 'cut' ) { + if ( !isRangeInsideSingleMarker( editor, model.document.selection.getFirstRange() ) ) { + evt.stop(); + } + } + }, { priority: 'highest' } ); + + const allowedAttributes = editor.config.get( 'restrictedEditing.allowedAttributes' ); + model.schema.addAttributeCheck( ( context, attributeName ) => allowedAttributes.includes( attributeName ) ); + model.schema.addChildCheck( ( context, childDefinition ) => { + if ( Array.from( context.getNames() ).includes( '$clipboardHolder' ) ) { + return childDefinition.name === '$text'; + } + } ); } /** From aaa1a78d396ddde611ba4db53f5b988156c1c5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 16:21:16 +0100 Subject: [PATCH 14/21] Refactor code to make it clearer. --- src/restrictededitingmodeediting.js | 43 +++++++++++++++++++---------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 8c0a5b69..7c10c06a 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -176,6 +176,7 @@ export default class RestrictedEditingModeEditing extends Plugin { _setupRestrictions() { const editor = this.editor; const model = editor.model; + const selection = model.document.selection; const viewDoc = editor.editing.view.document; this.listenTo( model, 'deleteContent', restrictDeleteContent( editor ), { priority: 'high' } ); @@ -188,27 +189,23 @@ export default class RestrictedEditingModeEditing extends Plugin { this.listenTo( inputCommand, 'execute', disallowInputExecForWrongRange( editor ), { priority: 'high' } ); } - // Block clipboard completely in restricted mode. + // Block clipboard outside exception marker on paste. this.listenTo( viewDoc, 'clipboardInput', function( evt ) { - if ( !isRangeInsideSingleMarker( editor, model.document.selection.getFirstRange() ) ) { + if ( !isRangeInsideSingleMarker( editor, selection.getFirstRange() ) ) { evt.stop(); } - }, { priority: 'highest' } ); + }, { priority: 'high' } ); + + // Block clipboard outside exception marker on cut. this.listenTo( viewDoc, 'clipboardOutput', ( evt, data ) => { - if ( data.method == 'cut' ) { - if ( !isRangeInsideSingleMarker( editor, model.document.selection.getFirstRange() ) ) { - evt.stop(); - } + if ( data.method == 'cut' && !isRangeInsideSingleMarker( editor, selection.getFirstRange() ) ) { + evt.stop(); } - }, { priority: 'highest' } ); + }, { priority: 'high' } ); const allowedAttributes = editor.config.get( 'restrictedEditing.allowedAttributes' ); - model.schema.addAttributeCheck( ( context, attributeName ) => allowedAttributes.includes( attributeName ) ); - model.schema.addChildCheck( ( context, childDefinition ) => { - if ( Array.from( context.getNames() ).includes( '$clipboardHolder' ) ) { - return childDefinition.name === '$text'; - } - } ); + model.schema.addAttributeCheck( onlyAllowAttributesFromList( allowedAttributes ) ); + model.schema.addChildCheck( allowTextOnlyInClipboardHolder ); } /** @@ -397,3 +394,21 @@ function isRangeInsideSingleMarker( editor, range ) { return markerAtStart && markerAtEnd && markerAtEnd === markerAtStart; } + +function onlyAllowAttributesFromList( allowedAttributes ) { + return ( context, attributeName ) => { + if ( isClipboardContext( context ) ) { + return allowedAttributes.includes( attributeName ); + } + }; +} + +function allowTextOnlyInClipboardHolder( context, childDefinition ) { + if ( isClipboardContext( context ) ) { + return childDefinition.name === '$text'; + } +} + +function isClipboardContext( context ) { + return Array.from( context.getNames() ).includes( '$clipboardHolder' ); +} From c911cbd7e40afc04ea68ae1deaf97750bbcc4844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 16:23:57 +0100 Subject: [PATCH 15/21] Write docs for the allowedAttributes configuration. --- src/restrictededitingmode.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/restrictededitingmode.js b/src/restrictededitingmode.js index fbd53ced..db7d708a 100644 --- a/src/restrictededitingmode.js +++ b/src/restrictededitingmode.js @@ -83,3 +83,15 @@ export default class RestrictedEditingMode extends Plugin { * * @member {Array.} module:restricted-editing/restrictededitingmode~RestrictedEditingModeConfig#allowedCommands */ + +/** + * The text attribute names allowed when pasting content ot non-restricted areas. + * + * The default value is: + * + * const restrictedEditingConfig = { + * allowedAttributes: [ 'bold', 'italic', 'link' ] + * }; + * + * @member {Array.} module:restricted-editing/restrictededitingmode~RestrictedEditingModeConfig#allowedAttributes + */ From 7873654d80366bb7e681c7f9b7b750b47fd384a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 16:28:30 +0100 Subject: [PATCH 16/21] Add ckeditor5-block-quote dependency. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 8aec6d23..ae253606 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@ckeditor/ckeditor5-basic-styles": "^16.0.0", + "@ckeditor/ckeditor5-block-quote": "^16.0.0", "@ckeditor/ckeditor5-clipboard": "^16.0.0", "@ckeditor/ckeditor5-editor-classic": "^16.0.0", "@ckeditor/ckeditor5-engine": "^16.0.0", From d6bea097b2c55dd6609343c2a15a956dc6623662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Mon, 30 Dec 2019 16:30:57 +0100 Subject: [PATCH 17/21] Add allowedAttributes to RestrictedEditingModeConfig docs. --- src/restrictededitingmode.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/restrictededitingmode.js b/src/restrictededitingmode.js index db7d708a..fa5ba4ee 100644 --- a/src/restrictededitingmode.js +++ b/src/restrictededitingmode.js @@ -56,7 +56,8 @@ export default class RestrictedEditingMode extends Plugin { * ClassicEditor * .create( { * restrictedEditing: { - * allowedCommands: [ 'bold', 'italic' ] + * allowedCommands: [ 'bold', 'italic' ], + * allowedAttributes: [ 'bold', 'italic' ] * } * } ) * .then( ... ) From 638ff5b431adcdc88fda9ae25c965d1a5b67a4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 3 Jan 2020 12:18:22 +0100 Subject: [PATCH 18/21] Use SchemaContex.startsWith() to check clipboard context. --- src/restrictededitingmodeediting.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 196a731c..3b9a6a24 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -426,18 +426,14 @@ function ensureNewMarkerIsFlat( editor ) { function onlyAllowAttributesFromList( allowedAttributes ) { return ( context, attributeName ) => { - if ( isClipboardContext( context ) ) { + if ( context.startsWith( '$clipboardHolder' ) ) { return allowedAttributes.includes( attributeName ); } }; } function allowTextOnlyInClipboardHolder( context, childDefinition ) { - if ( isClipboardContext( context ) ) { + if ( context.startsWith( '$clipboardHolder' ) ) { return childDefinition.name === '$text'; } } - -function isClipboardContext( context ) { - return Array.from( context.getNames() ).includes( '$clipboardHolder' ); -} From 7206b2efe02aec525fd2d78677863675cb99d4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 3 Jan 2020 12:27:21 +0100 Subject: [PATCH 19/21] Fix link attribute name used in default configuration. --- src/restrictededitingmode.js | 6 +++--- src/restrictededitingmodeediting.js | 2 +- tests/restrictededitingmodeediting.js | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/restrictededitingmode.js b/src/restrictededitingmode.js index fa5ba4ee..a1e53c9c 100644 --- a/src/restrictededitingmode.js +++ b/src/restrictededitingmode.js @@ -56,8 +56,8 @@ export default class RestrictedEditingMode extends Plugin { * ClassicEditor * .create( { * restrictedEditing: { - * allowedCommands: [ 'bold', 'italic' ], - * allowedAttributes: [ 'bold', 'italic' ] + * allowedCommands: [ 'bold', 'link', 'unlink' ], + * allowedAttributes: [ 'bold', 'linkHref' ] * } * } ) * .then( ... ) @@ -91,7 +91,7 @@ export default class RestrictedEditingMode extends Plugin { * The default value is: * * const restrictedEditingConfig = { - * allowedAttributes: [ 'bold', 'italic', 'link' ] + * allowedAttributes: [ 'bold', 'italic', 'linkHref' ] * }; * * @member {Array.} module:restricted-editing/restrictededitingmode~RestrictedEditingModeConfig#allowedAttributes diff --git a/src/restrictededitingmodeediting.js b/src/restrictededitingmodeediting.js index 3b9a6a24..05a4c207 100644 --- a/src/restrictededitingmodeediting.js +++ b/src/restrictededitingmodeediting.js @@ -42,7 +42,7 @@ export default class RestrictedEditingModeEditing extends Plugin { editor.config.define( 'restrictedEditing', { allowedCommands: [ 'bold', 'italic', 'link', 'unlink' ], - allowedAttributes: [ 'bold', 'italic', 'link' ] + allowedAttributes: [ 'bold', 'italic', 'linkHref' ] } ); /** diff --git a/tests/restrictededitingmodeediting.js b/tests/restrictededitingmodeediting.js index 5b1d7c87..617c1a5c 100644 --- a/tests/restrictededitingmodeediting.js +++ b/tests/restrictededitingmodeediting.js @@ -13,6 +13,7 @@ import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils' import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting'; import StrikethroughEditing from '@ckeditor/ckeditor5-basic-styles/src/strikethrough/strikethroughediting'; +import LinkEditing from '@ckeditor/ckeditor5-link/src/linkediting'; import Typing from '@ckeditor/ckeditor5-typing/src/typing'; import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard'; @@ -688,7 +689,7 @@ describe( 'RestrictedEditingModeEditing', () => { beforeEach( async () => { editor = await VirtualTestEditor.create( { - plugins: [ Paragraph, BoldEditing, ItalicEditing, StrikethroughEditing, BlockQuoteEditing, Typing, Clipboard, + plugins: [ Paragraph, BoldEditing, ItalicEditing, StrikethroughEditing, BlockQuoteEditing, LinkEditing, Typing, Clipboard, RestrictedEditingModeEditing ] } ); @@ -899,10 +900,15 @@ describe( 'RestrictedEditingModeEditing', () => { addExceptionMarker( 4, 7, firstParagraph ); viewDoc.fire( 'clipboardInput', { - dataTransfer: createDataTransfer( { 'text/html': '

XXX

', 'text/plain': 'XXX' } ) + dataTransfer: createDataTransfer( { + 'text/html': '

XXX

', + 'text/plain': 'XXX' + } ) } ); - assertEqualMarkup( getModelData( model ), 'foo b<$text bold="true">XXX[]ar baz' ); + assertEqualMarkup( getModelData( model ), + 'foo b<$text bold="true" italic="true" linkHref="foo">XXX[]ar baz' + ); assertMarkerRangePaths( [ 0, 4 ], [ 0, 10 ] ); } ); From 353ffaf73e9c04898d37e4343eb4738e8ae828bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?= Date: Fri, 3 Jan 2020 12:57:24 +0100 Subject: [PATCH 20/21] Added missing devdep. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ae253606..5f5c4d85 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "@ckeditor/ckeditor5-block-quote": "^16.0.0", "@ckeditor/ckeditor5-clipboard": "^16.0.0", "@ckeditor/ckeditor5-editor-classic": "^16.0.0", - "@ckeditor/ckeditor5-engine": "^16.0.0", + "@ckeditor/ckeditor5-engine": "^16.0.0", + "@ckeditor/ckeditor5-link": "^16.0.0", "@ckeditor/ckeditor5-paragraph": "^16.0.0", "@ckeditor/ckeditor5-table": "^16.0.0", "@ckeditor/ckeditor5-typing": "^16.0.0", From 5150f7091b9b7af35e284eee22918df38d5988c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82aszewski?= Date: Fri, 3 Jan 2020 13:01:10 +0100 Subject: [PATCH 21/21] Fix code style in package.json. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5f5c4d85..9245cbf9 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "@ckeditor/ckeditor5-block-quote": "^16.0.0", "@ckeditor/ckeditor5-clipboard": "^16.0.0", "@ckeditor/ckeditor5-editor-classic": "^16.0.0", - "@ckeditor/ckeditor5-engine": "^16.0.0", - "@ckeditor/ckeditor5-link": "^16.0.0", + "@ckeditor/ckeditor5-engine": "^16.0.0", + "@ckeditor/ckeditor5-link": "^16.0.0", "@ckeditor/ckeditor5-paragraph": "^16.0.0", "@ckeditor/ckeditor5-table": "^16.0.0", "@ckeditor/ckeditor5-typing": "^16.0.0",