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' );
}