Bläddra i källkod

Merge pull request #8292 from ckeditor/i/8079

Fix (link): Auto link feature will now use `link.defaultProtocol` if set. Closes #8079.
Maciej 5 år sedan
förälder
incheckning
9a9f9c3671

+ 5 - 11
packages/ckeditor5-link/src/autolink.js

@@ -10,6 +10,7 @@
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import TextWatcher from '@ckeditor/ckeditor5-typing/src/textwatcher';
 import getLastTextLine from '@ckeditor/ckeditor5-typing/src/utils/getlasttextline';
+import { addLinkProtocolIfApplicable } from './utils';
 
 const MIN_LINK_LENGTH_WITH_SPACE_AT_END = 4; // Ie: "t.co " (length 5).
 
@@ -49,9 +50,6 @@ const URL_REG_EXP = new RegExp(
 
 const URL_GROUP_IN_MATCH = 2;
 
-// Simplified email test - should be run over previously found URL.
-const EMAIL_REG_EXP = /^[\S]+@((?![-_])(?:[-\w\u00a1-\uffff]{0,63}[^-_]\.))+(?:[a-z\u00a1-\uffff]{2,})$/i;
-
 /**
  * The autolink plugin.
  *
@@ -213,7 +211,7 @@ export default class AutoLink extends Plugin {
 	 * @param {module:engine/model/range~Range} range The text range to apply the link attribute to.
 	 * @private
 	 */
-	_applyAutoLink( url, range ) {
+	_applyAutoLink( link, range ) {
 		const model = this.editor.model;
 
 		if ( !this.isEnabled || !isLinkAllowedOnRange( range, model ) ) {
@@ -222,9 +220,9 @@ export default class AutoLink extends Plugin {
 
 		// Enqueue change to make undo step.
 		model.enqueueChange( writer => {
-			const linkHrefValue = isEmail( url ) ? `mailto:${ url }` : url;
-
-			writer.setAttribute( 'linkHref', linkHrefValue, range );
+			const defaultProtocol = this.editor.config.get( 'link.defaultProtocol' );
+			const parsedUrl = addLinkProtocolIfApplicable( link, defaultProtocol );
+			writer.setAttribute( 'linkHref', parsedUrl, range );
 		} );
 	}
 }
@@ -240,10 +238,6 @@ function getUrlAtTextEnd( text ) {
 	return match ? match[ URL_GROUP_IN_MATCH ] : null;
 }
 
-function isEmail( linkHref ) {
-	return EMAIL_REG_EXP.exec( linkHref );
-}
-
 function isLinkAllowedOnRange( range, model ) {
 	return model.schema.checkAttributeInSelection( model.createSelection( range ), 'linkHref' );
 }

+ 3 - 13
packages/ckeditor5-link/src/linkui.js

@@ -9,7 +9,7 @@
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver';
-import { isLinkElement, LINK_KEYSTROKE } from './utils';
+import { addLinkProtocolIfApplicable, isLinkElement, LINK_KEYSTROKE } from './utils';
 
 import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
 
@@ -21,8 +21,6 @@ import LinkActionsView from './ui/linkactionsview';
 
 import linkIcon from '../theme/icons/link.svg';
 
-const protocolRegExp = /^((\w+:(\/{2,})?)|(\W))/i;
-const emailRegExp = /[\w-]+@[\w-]+\.+[\w-]+/i;
 const VISUAL_SELECTION_MARKER_NAME = 'link-ui';
 
 /**
@@ -176,16 +174,8 @@ export default class LinkUI extends Plugin {
 		// Execute link command after clicking the "Save" button.
 		this.listenTo( formView, 'submit', () => {
 			const { value } = formView.urlInputView.fieldView.element;
-
-			// The regex checks for the protocol syntax ('xxxx://' or 'xxxx:')
-			// or non-word characters at the beginning of the link ('/', '#' etc.).
-			const isProtocolNeeded = !!defaultProtocol && !protocolRegExp.test( value );
-			const isEmail = emailRegExp.test( value );
-
-			const protocol = isEmail ? 'mailto:' : defaultProtocol;
-			const parsedValue = value && isProtocolNeeded ? protocol + value : value;
-
-			editor.execute( 'link', parsedValue, formView.getDecoratorSwitchesState() );
+			const parsedUrl = addLinkProtocolIfApplicable( value, defaultProtocol );
+			editor.execute( 'link', parsedUrl, formView.getDecoratorSwitchesState() );
 			this._closeFormView();
 		} );
 

+ 36 - 0
packages/ckeditor5-link/src/utils.js

@@ -12,6 +12,13 @@ import { upperFirst } from 'lodash-es';
 const ATTRIBUTE_WHITESPACES = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; // eslint-disable-line no-control-regex
 const SAFE_URL = /^(?:(?:https?|ftps?|mailto):|[^a-z]|[a-z+.-]+(?:[^a-z+.:-]|$))/i;
 
+// Simplified email test - should be run over previously found URL.
+const EMAIL_REG_EXP = /^[\S]+@((?![-_])(?:[-\w\u00a1-\uffff]{0,63}[^-_]\.))+(?:[a-z\u00a1-\uffff]{2,})$/i;
+
+// The regex checks for the protocol syntax ('xxxx://' or 'xxxx:')
+// or non-word characters at the beginning of the link ('/', '#' etc.).
+const PROTOCOL_REG_EXP = /^((\w+:(\/{2,})?)|(\W))/i;
+
 /**
  * A keystroke used by the {@link module:link/linkui~LinkUI link UI feature}.
  */
@@ -135,3 +142,32 @@ export function isImageAllowed( element, schema ) {
 
 	return element.is( 'element', 'image' ) && schema.checkAttribute( 'image', 'linkHref' );
 }
+
+/**
+ * Returns `true` if the specified `value` is an email.
+ *
+ * @params {String} value
+ * @returns {Boolean}
+ */
+export function isEmail( value ) {
+	return EMAIL_REG_EXP.test( value );
+}
+
+/**
+ * Adds protocol prefix to the specified `link` when:
+ *
+ * * it doesn't contain it already, and there is a {@link module:link/link~LinkConfig#defaultProtocol `defaultProtocol` }
+ * config value provided
+ * * or the link is an email address
+ *
+ *
+ * @params {String} link
+ * @params {String} defaultProtocol
+ * @returns {Boolean}
+ */
+export function addLinkProtocolIfApplicable( link, defaultProtocol ) {
+	const protocol = isEmail( link ) ? 'mailto:' : defaultProtocol;
+	const isProtocolNeeded = !!protocol && !PROTOCOL_REG_EXP.test( link );
+
+	return link && isProtocolNeeded ? protocol + link : link;
+}

+ 9 - 0
packages/ckeditor5-link/tests/autolink.js

@@ -215,6 +215,15 @@ describe( 'AutoLink', () => {
 			);
 		} );
 
+		it( 'adds default protocol to link of detected www addresses', () => {
+			editor.config.set( 'link.defaultProtocol', 'http://' );
+			simulateTyping( 'www.cksource.com ' );
+
+			expect( getData( model ) ).to.equal(
+				'<paragraph><$text linkHref="http://www.cksource.com">www.cksource.com</$text> []</paragraph>'
+			);
+		} );
+
 		// Some examples came from https://mathiasbynens.be/demo/url-regex.
 		describe( 'supported URL', () => {
 			const supportedURLs = [

+ 13 - 0
packages/ckeditor5-link/tests/linkui.js

@@ -1321,6 +1321,19 @@ describe( 'LinkUI', () => {
 				} );
 			} );
 
+			it( 'should detect an email on submitting the form and add "mailto:" protocol automatically to the provided value ' +
+				'even when defaultProtocol is undefined', () => {
+				setModelData( editor.model, '<paragraph>[email@example.com]</paragraph>' );
+
+				formView.urlInputView.fieldView.value = 'email@example.com';
+				formView.fire( 'submit' );
+
+				expect( formView.urlInputView.fieldView.value ).to.equal( 'mailto:email@example.com' );
+				expect( getModelData( editor.model ) ).to.equal(
+					'<paragraph>[<$text linkHref="mailto:email@example.com">email@example.com</$text>]</paragraph>'
+				);
+			} );
+
 			it( 'should not add an email protocol when given provided within the value' +
 				'even when `config.link.defaultProtocol` configured', () => {
 				return createEditorWithDefaultProtocol( 'mailto:' ).then( ( { editor, formView } ) => {

+ 35 - 1
packages/ckeditor5-link/tests/utils.js

@@ -10,7 +10,9 @@ import ContainerElement from '@ckeditor/ckeditor5-engine/src/view/containereleme
 import Text from '@ckeditor/ckeditor5-engine/src/view/text';
 import Schema from '@ckeditor/ckeditor5-engine/src/model/schema';
 import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
-import { createLinkElement, isLinkElement, ensureSafeUrl, normalizeDecorators, isImageAllowed } from '../src/utils';
+import {
+	createLinkElement, isLinkElement, ensureSafeUrl, normalizeDecorators, isImageAllowed, isEmail, addLinkProtocolIfApplicable
+} from '../src/utils';
 
 describe( 'utils', () => {
 	describe( 'isLinkElement()', () => {
@@ -246,4 +248,36 @@ describe( 'utils', () => {
 			expect( isImageAllowed( element, schema ) ).to.equal( true );
 		} );
 	} );
+
+	describe( 'isEmail()', () => {
+		it( 'should return true for email string', () => {
+			expect( isEmail( 'newsletter@cksource.com' ) ).to.be.true;
+		} );
+
+		it( 'should return false for not email string', () => {
+			expect( isEmail( 'test' ) ).to.be.false;
+			expect( isEmail( 'test.test' ) ).to.be.false;
+			expect( isEmail( 'test@test' ) ).to.be.false;
+		} );
+	} );
+
+	describe( 'addLinkProtocolIfApplicable()', () => {
+		it( 'should return link with email protocol for email string', () => {
+			expect( addLinkProtocolIfApplicable( 'foo@bar.com' ) ).to.equal( 'mailto:foo@bar.com' );
+			expect( addLinkProtocolIfApplicable( 'foo@bar.com', 'http://' ) ).to.equal( 'mailto:foo@bar.com' );
+		} );
+
+		it( 'should return link with http protocol for url string if defaultProtocol is provided', () => {
+			expect( addLinkProtocolIfApplicable( 'www.ckeditor.com', 'http://' ) ).to.equal( 'http://www.ckeditor.com' );
+		} );
+
+		it( 'should return unmodified link if not applicable', () => {
+			expect( addLinkProtocolIfApplicable( 'test' ) ).to.equal( 'test' );
+			expect( addLinkProtocolIfApplicable( 'www.ckeditor.com' ) ).to.equal( 'www.ckeditor.com' );
+			expect( addLinkProtocolIfApplicable( 'http://www.ckeditor.com' ) ).to.equal( 'http://www.ckeditor.com' );
+			expect( addLinkProtocolIfApplicable( 'http://www.ckeditor.com', 'http://' ) ).to.equal( 'http://www.ckeditor.com' );
+			expect( addLinkProtocolIfApplicable( 'mailto:foo@bar.com' ) ).to.equal( 'mailto:foo@bar.com' );
+			expect( addLinkProtocolIfApplicable( 'mailto:foo@bar.com', 'http://' ) ).to.equal( 'mailto:foo@bar.com' );
+		} );
+	} );
 } );