diff --git a/package-lock.json b/package-lock.json index e489d2de0e3937..7d9582d1c90066 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7288,6 +7288,7 @@ "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", + "@wordpress/escape-html": "file:packages/escape-html", "@wordpress/i18n": "file:packages/i18n", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keycodes": "file:packages/keycodes", @@ -22199,7 +22200,7 @@ "dependencies": { "clone-deep": { "version": "0.2.4", - "resolved": "http://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", "dev": true, "requires": { @@ -22233,7 +22234,7 @@ "dependencies": { "kind-of": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", "dev": true, "requires": { diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 8d4cd8ba268545..185e0396858875 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -37,6 +37,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", + "@wordpress/escape-html": "file:../escape-html", "@wordpress/i18n": "file:../i18n", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "@wordpress/keycodes": "file:../keycodes", diff --git a/packages/block-library/src/code/edit.js b/packages/block-library/src/code/edit.js index 2926774f46ff22..5aa4b337bd3f12 100644 --- a/packages/block-library/src/code/edit.js +++ b/packages/block-library/src/code/edit.js @@ -7,14 +7,13 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import { PlainText } from '@wordpress/block-editor'; -import { escape, unescape } from './utils'; export default function CodeEdit( { attributes, setAttributes, className } ) { return (
{ attributes.content }
;
+ return { escape( attributes.content ) }
;
}
diff --git a/packages/block-library/src/code/test/utils.js b/packages/block-library/src/code/test/utils.js
index 4926eef16d5282..d3e9de573cf77f 100644
--- a/packages/block-library/src/code/test/utils.js
+++ b/packages/block-library/src/code/test/utils.js
@@ -1,15 +1,10 @@
/**
* Internal dependencies
*/
-import { escape, unescape } from '../utils';
+import { escape } from '../utils';
describe( 'core/code', () => {
describe( 'escape()', () => {
- it( 'should escape ampersands', () => {
- const text = escape( '&' );
- expect( text ).toBe( '&' );
- } );
-
it( 'should escape opening square brackets', () => {
const text = escape( '[shortcode][/shortcode]' );
expect( text ).toBe( '[shortcode][/shortcode]' );
@@ -24,39 +19,5 @@ describe( 'core/code', () => {
const text = escape( 'Text https://example.com/test/' );
expect( text ).toBe( 'Text https://example.com/test/' );
} );
-
- it( 'should escape ampersands last', () => {
- const text = escape( '[shortcode][/shortcode]' );
- expect( text ).toBe( '[shortcode][/shortcode]' );
- expect( text ).not.toBe( '[shortcode][/shortcode]' );
- } );
- } );
-
- describe( 'unescape()', () => {
- it( 'should unescape escaped ampersands', () => {
- const text = unescape( '&' );
- expect( text ).toBe( '&' );
- } );
-
- it( 'should unescape escaped opening square brackets', () => {
- const text = unescape( '[shortcode][/shortcode]' );
- expect( text ).toBe( '[shortcode][/shortcode]' );
- } );
-
- it( 'should unescape the escaped protocol of an isolated url', () => {
- const text = unescape( 'https://example.com/test/' );
- expect( text ).toBe( 'https://example.com/test/' );
- } );
-
- it( 'should revert the result of escape()', () => {
- const ampersand = unescape( escape( '&' ) );
- expect( ampersand ).toBe( '&' );
-
- const squareBracket = unescape( escape( '[shortcode][/shortcode]' ) );
- expect( squareBracket ).toBe( '[shortcode][/shortcode]' );
-
- const url = unescape( escape( 'https://example.com/test/' ) );
- expect( url ).toBe( 'https://example.com/test/' );
- } );
} );
} );
diff --git a/packages/block-library/src/code/utils.js b/packages/block-library/src/code/utils.js
index fb8557a95b18ee..5178e3d0135fca 100644
--- a/packages/block-library/src/code/utils.js
+++ b/packages/block-library/src/code/utils.js
@@ -3,6 +3,11 @@
*/
import { flow } from 'lodash';
+/**
+ * WordPress dependencies
+ */
+import { escapeEditableHTML } from '@wordpress/escape-html';
+
/**
* Escapes ampersands, shortcodes, and links.
*
@@ -11,49 +16,12 @@ import { flow } from 'lodash';
*/
export function escape( content ) {
return flow(
- escapeAmpersands,
+ escapeEditableHTML,
escapeOpeningSquareBrackets,
escapeProtocolInIsolatedUrls
)( content || '' );
}
-/**
- * Unescapes escaped ampersands, shortcodes, and links.
- *
- * @param {string} content Content with (maybe) escaped ampersands, shortcodes, and links.
- * @return {string} The given content with escaped characters unescaped.
- */
-export function unescape( content ) {
- return flow(
- unescapeProtocolInIsolatedUrls,
- unescapeOpeningSquareBrackets,
- unescapeAmpersands
- )( content || '' );
-}
-
-/**
- * Returns the given content with all its ampersand characters converted
- * into their HTML entity counterpart (i.e. & => &)
- *
- * @param {string} content The content of a code block.
- * @return {string} The given content with its ampersands converted into
- * their HTML entity counterpart (i.e. & => &)
- */
-function escapeAmpersands( content ) {
- return content.replace( /&/g, '&' );
-}
-
-/**
- * Returns the given content with all & HTML entities converted into &.
- *
- * @param {string} content The content of a code block.
- * @return {string} The given content with all & HTML entities
- * converted into &.
- */
-function unescapeAmpersands( content ) {
- return content.replace( /&/g, '&' );
-}
-
/**
* Returns the given content with all opening shortcode characters converted
* into their HTML entity counterpart (i.e. [ => [). For instance, a
@@ -71,16 +39,6 @@ function escapeOpeningSquareBrackets( content ) {
return content.replace( /\[/g, '[' );
}
-/**
- * Returns the given content translating all [ into [.
- *
- * @param {string} content The content of a code block.
- * @return {string} The given content with all [ into [.
- */
-function unescapeOpeningSquareBrackets( content ) {
- return content.replace( /[/g, '[' );
-}
-
/**
* Converts the first two forward slashes of any isolated URL into their HTML
* counterparts (i.e. // => //). For instance, https://youtube.com/watch?x
@@ -98,20 +56,3 @@ function unescapeOpeningSquareBrackets( content ) {
function escapeProtocolInIsolatedUrls( content ) {
return content.replace( /^(\s*https?:)\/\/([^\s<>"]+\s*)$/m, '$1//$2' );
}
-
-/**
- * Converts the first two forward slashes of any isolated URL from the HTML entity
- * I into /.
- *
- * An isolated URL is a URL that sits in its own line, surrounded only by spacing
- * characters.
- *
- * See https://github.com/WordPress/wordpress-develop/blob/5.1.1/src/wp-includes/class-wp-embed.php#L403
- *
- * @param {string} content The content of a code block.
- * @return {string} The given content with the first two forward slashes of any
- * isolated URL from the HTML entity I into /.
- */
-function unescapeProtocolInIsolatedUrls( content ) {
- return content.replace( /^(\s*https?:)//([^\s<>"]+\s*)$/m, '$1//$2' );
-}
diff --git a/packages/escape-html/README.md b/packages/escape-html/README.md
index 764a6052d7e79c..6f112276748131 100644
--- a/packages/escape-html/README.md
+++ b/packages/escape-html/README.md
@@ -64,6 +64,20 @@ _Returns_
- `string`: Escaped attribute value.
+# **escapeEditableHTML**
+
+Returns an escaped Editable HTML element value. This is different from
+`escapeHTML`, because for editable HTML, ALL ampersands must be escaped in
+order to render the content correctly on the page.
+
+_Parameters_
+
+- _value_ `string`: Element value.
+
+_Returns_
+
+- `string`: Escaped HTML element value.
+
# **escapeHTML**
Returns an escaped HTML element value.
diff --git a/packages/escape-html/src/index.js b/packages/escape-html/src/index.js
index d31f43f63f70bb..8b72830a74c4b9 100644
--- a/packages/escape-html/src/index.js
+++ b/packages/escape-html/src/index.js
@@ -96,6 +96,19 @@ export function escapeHTML( value ) {
return escapeLessThan( escapeAmpersand( value ) );
}
+/**
+ * Returns an escaped Editable HTML element value. This is different from
+ * `escapeHTML`, because for editable HTML, ALL ampersands must be escaped in
+ * order to render the content correctly on the page.
+ *
+ * @param {string} value Element value.
+ *
+ * @return {string} Escaped HTML element value.
+ */
+export function escapeEditableHTML( value ) {
+ return escapeLessThan( value.replace( /&/g, '&' ) );
+}
+
/**
* Returns true if the given attribute name is valid, or false otherwise.
*
diff --git a/packages/escape-html/src/test/index.js b/packages/escape-html/src/test/index.js
index 9558c58c7e23ab..2c564a771f4bdc 100644
--- a/packages/escape-html/src/test/index.js
+++ b/packages/escape-html/src/test/index.js
@@ -8,6 +8,7 @@ import {
escapeAttribute,
escapeHTML,
isValidAttributeName,
+ escapeEditableHTML,
} from '../';
import __unstableEscapeGreaterThan from '../escape-greater';
@@ -94,3 +95,11 @@ describe( 'isValidAttributeName', () => {
expect( result ).toBe( true );
} );
} );
+
+describe( 'escapeEditableHTML', () => {
+ it( 'should escape < and all ampersands', () => {
+ const result = escapeEditableHTML( 'WP & <strong>' );
+
+ expect( result ).toBe( '<a href="https://w.org">WP</a> & <strong>' );
+ } );
+} );
diff --git a/packages/rich-text/src/to-html-string.js b/packages/rich-text/src/to-html-string.js
index bf6c60fda0442d..06aa0434992028 100644
--- a/packages/rich-text/src/to-html-string.js
+++ b/packages/rich-text/src/to-html-string.js
@@ -3,7 +3,7 @@
*/
import {
- escapeHTML,
+ escapeEditableHTML,
escapeAttribute,
isValidAttributeName,
} from '@wordpress/escape-html';
@@ -106,6 +106,6 @@ function createElementHTML( { type, attributes, object, children } ) {
function createChildrenHTML( children = [] ) {
return children.map( ( child ) => {
- return child.text === undefined ? createElementHTML( child ) : escapeHTML( child.text );
+ return child.text === undefined ? createElementHTML( child ) : escapeEditableHTML( child.text );
} ).join( '' );
}