diff --git a/package.json b/package.json index ef6167e..412b365 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "preinstall": "npx npm-force-resolutions" }, "dependencies": { + "@wordpress/escape-html": "1.9.0", "cgb-scripts": "1.23.0", "npm-force-resolutions": "0.0.3" }, diff --git a/src/blocks.js b/src/blocks.js index 092e66e..ce0459a 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -16,6 +16,11 @@ import { } from '@wordpress/components'; import { PlainText, InspectorControls } from '@wordpress/editor'; +/** + * Internal dependencies + */ +import save from './save'; + registerBlockType( 'syntaxhighlighter/code', { title: __( 'SyntaxHighlighter Code', 'syntaxhighlighter' ), @@ -38,7 +43,7 @@ registerBlockType( 'syntaxhighlighter/code', { content: { type: 'string', source: 'text', - selector: 'pre', + selector: 'code', }, language: { @@ -296,9 +301,5 @@ registerBlockType( 'syntaxhighlighter/code', { return [ blockSettings, editView ]; }, - save( { attributes } ) { - const { content } = attributes; - - return(
{ content }
); - }, + save, } ); diff --git a/src/save.js b/src/save.js new file mode 100644 index 0000000..06ee8a2 --- /dev/null +++ b/src/save.js @@ -0,0 +1,12 @@ +/** + * Internal dependencies + */ +import { escape } from './utils'; + +export default function save( { attributes } ) { + return ( +
+			{ escape( attributes.content ) }
+		
+ ); +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..30609b1 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,61 @@ +/** + * External dependencies + */ +import { flow } from 'lodash'; + +/** + * WordPress dependencies + */ +import { escapeEditableHTML } from '@wordpress/escape-html'; + +/** + * Escapes ampersands, shortcodes, and links. + * + * @param {string} content The content of a code block. + * @return {string} The given content with some characters escaped. + */ +export function escape( content ) { + return flow( + escapeEditableHTML, + escapeOpeningSquareBrackets, + escapeProtocolInIsolatedUrls + )( content || '' ); +} + +/** + * Returns the given content with all opening shortcode characters converted + * into their HTML entity counterpart (i.e. [ => [). For instance, a + * shortcode like [embed] becomes [embed] + * + * This function replicates the escaping of HTML tags, where a tag like + * becomes <strong>. + * + * @param {string} content The content of a code block. + * @return {string} The given content with its opening shortcode characters + * converted into their HTML entity counterpart + * (i.e. [ => [) + */ +function escapeOpeningSquareBrackets( 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 + * becomes https://youtube.com/watch?x. + * + * 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 its ampersands converted into + * their HTML entity counterpart (i.e. & => &) + */ +function escapeProtocolInIsolatedUrls( content ) { + return content.replace( + /^(\s*https?:)\/\/([^\s<>"]+\s*)$/m, + '$1//$2' + ); +} diff --git a/syntaxhighlighter.php b/syntaxhighlighter.php index c151bd7..8f16749 100644 --- a/syntaxhighlighter.php +++ b/syntaxhighlighter.php @@ -524,10 +524,11 @@ public function render_block( $attributes, $content ) { } } - $code = preg_replace( '#
]+>([^<]+)?
#', '$1', $content ); + $code = preg_replace( '#
]+>([^<]+)?
#', '$1', $content ); // Undo escaping done by WordPress $code = str_replace( '<', '<', $code ); + $code = str_replace( '&', '&', $code ); return $this->shortcode_callback( $attributes, $code, 'code' ); }