Skip to content

Commit

Permalink
Merge pull request #12958 from ckeditor/ck/12912-use-default-protocol…
Browse files Browse the repository at this point in the history
…-on-pasted-data

Feature (link): Apply `config.link.defaultProtocol` on pasted links. Closes #12912.
  • Loading branch information
niegowski authored Dec 6, 2022
2 parents f5d3a02 + b887bf8 commit 2dc2e20
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/ckeditor5-link/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@ckeditor/ckeditor5-enter": "^35.3.2",
"@ckeditor/ckeditor5-image": "^35.3.2",
"@ckeditor/ckeditor5-paragraph": "^35.3.2",
"@ckeditor/ckeditor5-table": "^35.3.2",
"@ckeditor/ckeditor5-theme-lark": "^35.3.2",
"@ckeditor/ckeditor5-typing": "^35.3.2",
"@ckeditor/ckeditor5-undo": "^35.3.2",
Expand Down
41 changes: 40 additions & 1 deletion packages/ckeditor5-link/src/linkediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ import { keyCodes, env } from 'ckeditor5/src/utils';
import LinkCommand from './linkcommand';
import UnlinkCommand from './unlinkcommand';
import ManualDecorator from './utils/manualdecorator';
import { createLinkElement, ensureSafeUrl, getLocalizedDecorators, normalizeDecorators, openLink } from './utils';
import {
createLinkElement,
ensureSafeUrl,
getLocalizedDecorators,
normalizeDecorators,
openLink,
addLinkProtocolIfApplicable
} from './utils';

import '../theme/link.css';

Expand Down Expand Up @@ -121,6 +128,9 @@ export default class LinkEditing extends Plugin {

// Handle removing the content after the link element.
this._handleDeleteContentAfterLink();

// Handle adding default protocol to pasted links.
this._enableClipboardIntegration();
}

/**
Expand Down Expand Up @@ -578,6 +588,35 @@ export default class LinkEditing extends Plugin {
} );
}, { priority: 'low' } );
}

/**
* Enables URL fixing on pasting.
*
* @private
*/
_enableClipboardIntegration() {
const editor = this.editor;
const model = editor.model;
const defaultProtocol = this.editor.config.get( 'link.defaultProtocol' );

if ( !defaultProtocol ) {
return;
}

this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'contentInsertion', ( evt, data ) => {
model.change( writer => {
const range = writer.createRangeIn( data.content );

for ( const item of range.getItems() ) {
if ( item.hasAttribute( 'linkHref' ) ) {
const newLink = addLinkProtocolIfApplicable( item.getAttribute( 'linkHref' ), defaultProtocol );

writer.setAttribute( 'linkHref', newLink, item );
}
}
} );
} );
}
}

// Make the selection free of link-related model attributes.
Expand Down
164 changes: 164 additions & 0 deletions packages/ckeditor5-link/tests/integrations/clipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

import LinkEditing from '../../src/linkediting';

import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import TableEditing from '@ckeditor/ckeditor5-table/src/tableediting';
import ClipboardPipeline from '@ckeditor/ckeditor5-clipboard/src/clipboardpipeline';
import { getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
import { parse as parseView } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';

describe( 'Link integration: clipboard paste', () => {
let editor, model;

describe( 'when default protocol is not set', () => {
beforeEach( () => {
return VirtualTestEditor
.create( {
plugins: [ Paragraph, ClipboardPipeline, LinkEditing ]
} )
.then( newEditor => {
editor = newEditor;
model = editor.model;
} );
} );

afterEach( () => {
return editor.destroy();
} );

it( 'should not change a link if link has protocol', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView( '<a href="http://ckedtior.com">foo</a>' )
} );

expect( getModelData( model ) ).to.equalMarkup(
'<paragraph><$text linkHref="http://ckedtior.com">foo</$text>[]</paragraph>'
);
} );

it( 'should not change a link if link doesn\'t have protocol', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView( '<a href="ckedtior.com">foo</a>' )
} );

expect( getModelData( model ) ).to.equal(
'<paragraph><$text linkHref="ckedtior.com">foo</$text>[]</paragraph>'
);
} );
} );

describe( 'when default protocol is set', () => {
beforeEach( () => {
return VirtualTestEditor
.create( {
plugins: [ Paragraph, ClipboardPipeline, LinkEditing, TableEditing ],
link: {
defaultProtocol: 'http://'
}
} )
.then( newEditor => {
editor = newEditor;
model = editor.model;
} );
} );

afterEach( () => {
return editor.destroy();
} );

it( 'should not change a link if link has protocol', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView( '<a href="http://ckedtior.com">foo</a>' )
} );

expect( getModelData( model ) ).to.equalMarkup(
'<paragraph><$text linkHref="http://ckedtior.com">foo</$text>[]</paragraph>'
);
} );

it( 'should add protocol to link if link doesn\'t have protocol', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView( '<a href="www.ckedtior.com">foo</a>' )
} );

expect( getModelData( model ) ).to.equal(
'<paragraph><$text linkHref="http://www.ckedtior.com">foo</$text>[]</paragraph>'
);
} );

it( 'should add protocol to all links where it\'s possible', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView( '<a href="ckedtior.com">foo</a><a href="www.ckedtior.com">bar</a>' )
} );

expect( getModelData( model ) ).to.equal(
'<paragraph>' +
'<$text linkHref="http://ckedtior.com">foo</$text>' +
'<$text linkHref="http://www.ckedtior.com">bar</$text>[]' +
'</paragraph>'
);
} );

it( 'should add protocol to link which is nested in table', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView(
'<figure><table><tbody><tr><td>' +
'<a href="ckedtior.com">foo</a>' +
'</td></tr></tbody></table></figure>'
)
} );

expect( getModelData( model ) ).to.equal(
'[<table><tableRow><tableCell><paragraph>' +
'<$text linkHref="http://ckedtior.com">foo</$text>' +
'</paragraph></tableCell></tableRow></table>]'
);
} );

it( 'should add protocol to multiple links which are nested in table', () => {
const clipboard = editor.plugins.get( 'ClipboardPipeline' );

clipboard.fire( 'inputTransformation', {
content: parseView(
'<table><tr><td>' +
'<a href="ckedtior.com">foo</a>' +
'</td>' +
'<td>' +
'<a href="ckedtior.com">foo</a>' +
'<a href="ckedtior2.com">foo</a>' +
'</td></tr></table>'
)
} );

expect( getModelData( model ) ).to.equal(
'[<table><tableRow>' +
'<tableCell><paragraph>' +
'<$text linkHref="http://ckedtior.com">foo</$text>' +
'</paragraph></tableCell>' +
'<tableCell><paragraph>' +
'<$text linkHref="http://ckedtior.com">foo</$text>' +
'<$text linkHref="http://ckedtior2.com">foo</$text>' +
'</paragraph></tableCell>' +
'</tableRow></table>]'
);
} );
} );
} );

0 comments on commit 2dc2e20

Please sign in to comment.