SyntaxHighlighter. Includes a new editor block. Author: Alex Mills (Viper007Bond) Author URI: https://alex.blog/ Text Domain: syntaxhighlighter License: GPL2 License URI: https://www.gnu.org/licenses/gpl-2.0.html Requires at least: 5.7 Tested up to: 6.7 Requires PHP: 7.0 **************************************************************************/ class SyntaxHighlighter { // All of these variables are private. Filters are provided for things that can be modified. public $pluginver = '3.7.0'; // Plugin version public $agshver = false; // Alex Gorbatchev's SyntaxHighlighter version (dynamically set below due to v2 vs v3) public $shfolder = false; // Controls what subfolder to load SyntaxHighlighter from (v2 or v3) public $settings = array(); // Contains the user's settings public $defaultsettings = array(); // Contains the default settings public $brushes = array(); // Array of aliases => brushes public $shortcodes = array(); // Array of shortcodes to use public $themes = array(); // Array of themes public $usedbrushes = array(); // Stores used brushes so we know what to output public $encoded = false; // Used to mark that a character encode took place public $codeformat = false; // If set, SyntaxHighlighter::get_code_format() will return this value public $content_save_pre_ran = false; // It's possible for the "content_save_pre" filter to run multiple times, so keep track public $brush_names = array(); // Array of brush names for use. public $specialchars = array(); // Array of special characters to be encoded. function __construct() { if ( ! function_exists( 'do_shortcodes_in_html_tags' ) ) return; // Load localization domain load_plugin_textdomain( 'syntaxhighlighter' ); // Display hooks add_filter( 'the_content', array( $this, 'parse_shortcodes' ), 7 ); // Posts add_filter( 'comment_text', array( $this, 'parse_shortcodes_comment' ), 7 ); // Comments add_filter( 'bp_get_the_topic_post_content', array( $this, 'parse_shortcodes' ), 7 ); // BuddyPress // Into the database add_filter( 'content_save_pre', array( $this, 'encode_shortcode_contents_slashed_noquickedit' ), 1 ); // Posts add_filter( 'pre_comment_content', array( $this, 'encode_shortcode_contents_slashed' ), 1 ); // Comments add_filter( 'group_forum_post_text_before_save', array( $this, 'encode_shortcode_contents_slashed' ), 1 ); // BuddyPress add_filter( 'group_forum_topic_text_before_save', array( $this, 'encode_shortcode_contents_slashed' ), 1 ); // BuddyPress // Out of the database for editing add_filter( 'the_editor_content', array( $this, 'the_editor_content' ), 1 ); // Posts add_filter( 'comment_edit_pre', array( $this, 'decode_shortcode_contents' ), 1 ); // Comments add_filter( 'bp_get_the_topic_text', array( $this, 'decode_shortcode_contents' ), 1 ); // BuddyPress add_filter( 'bp_get_the_topic_post_edit_text', array( $this, 'decode_shortcode_contents' ), 1 ); // BuddyPress // Outputting SyntaxHighlighter's JS and CSS add_action( 'wp_footer', array( $this, 'maybe_output_scripts' ), 15 ); add_action( 'admin_footer', array( $this, 'maybe_output_scripts' ), 15 ); // For comments // Admin hooks add_action( 'admin_init', array( $this, 'register_setting' ) ); add_action( 'admin_menu', array( $this, 'register_settings_page' ) ); add_filter( 'mce_external_plugins', array( $this, 'add_tinymce_plugin' ) ); add_filter( 'save_post', array( $this, 'mark_as_encoded' ), 10, 2 ); add_filter( 'plugin_action_links', array( $this, 'settings_link' ), 10, 2 ); // Editor Blocks if ( function_exists( 'parse_blocks' ) // WordPress 5.0+ || function_exists( 'the_gutenberg_project' ) // Gutenberg plugin for older WordPress ) { add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) ); add_action( 'the_content', array( $this, 'enable_brushes_used_in_blocks' ), 0 ); register_block_type( 'syntaxhighlighter/code', array( 'render_callback' => array( $this, 'render_block' ), ) ); } // Register widget hooks add_filter( 'widget_text', array( $this, 'widget_text_output' ), 7, 2 ); add_filter( 'widget_update_callback', array( $this, 'widget_text_save' ), 1, 4 ); add_filter( 'widget_form_callback', array( $this, 'widget_text_form' ), 1, 2 ); // Create array of default settings (you can use the filter to modify these) $this->defaultsettings = (array) apply_filters( 'syntaxhighlighter_defaultsettings', array( 'theme' => 'default', 'loadallbrushes' => 0, 'shversion' => 3, 'title' => '', 'autolinks' => 1, 'classname' => '', 'collapse' => 0, 'firstline' => 1, 'gutter' => 1, 'htmlscript' => 0, 'light' => 0, 'padlinenumbers' => 'false', 'smarttabs' => 1, 'tabsize' => 4, 'toolbar' => 0, 'wraplines' => 1, // 2.x only 'quickcode' => 1, // 3.x only ) ); // Create the settings array by merging the user's settings and the defaults $usersettings = (array) get_option('syntaxhighlighter_settings'); $this->settings = wp_parse_args( $usersettings, $this->defaultsettings ); // Dynamically set folder and version names for SynaxHighlighter if ( 2 == $this->settings['shversion'] ) { $this->shfolder = 'syntaxhighlighter2'; $this->agshver = '2.1.364'; } else { $this->shfolder = 'syntaxhighlighter3'; $this->agshver = '3.0.9b'; } // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma, PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket // Register brush scripts wp_register_script( 'syntaxhighlighter-core', plugins_url( $this->shfolder . '/scripts/shCore.js', __FILE__ ), array(), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-as3', plugins_url( $this->shfolder . '/scripts/shBrushAS3.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-arduino', plugins_url( $this->shfolder . '/scripts/shBrushArduino.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-bash', plugins_url( $this->shfolder . '/scripts/shBrushBash.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-coldfusion', plugins_url( $this->shfolder . '/scripts/shBrushColdFusion.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-cpp', plugins_url( $this->shfolder . '/scripts/shBrushCpp.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-csharp', plugins_url( $this->shfolder . '/scripts/shBrushCSharp.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-css', plugins_url( $this->shfolder . '/scripts/shBrushCss.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-delphi', plugins_url( $this->shfolder . '/scripts/shBrushDelphi.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-diff', plugins_url( $this->shfolder . '/scripts/shBrushDiff.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-erlang', plugins_url( $this->shfolder . '/scripts/shBrushErlang.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-go', plugins_url( $this->shfolder . '/scripts/shBrushGo.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-groovy', plugins_url( $this->shfolder . '/scripts/shBrushGroovy.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-haskell', plugins_url( $this->shfolder . '/scripts/shBrushHaskell.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-java', plugins_url( $this->shfolder . '/scripts/shBrushJava.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-javafx', plugins_url( $this->shfolder . '/scripts/shBrushJavaFX.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-jscript', plugins_url( $this->shfolder . '/scripts/shBrushJScript.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-perl', plugins_url( $this->shfolder . '/scripts/shBrushPerl.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-php', plugins_url( $this->shfolder . '/scripts/shBrushPhp.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-plain', plugins_url( $this->shfolder . '/scripts/shBrushPlain.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-powershell', plugins_url( $this->shfolder . '/scripts/shBrushPowerShell.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-python', plugins_url( $this->shfolder . '/scripts/shBrushPython.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-ruby', plugins_url( $this->shfolder . '/scripts/shBrushRuby.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-scala', plugins_url( $this->shfolder . '/scripts/shBrushScala.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-sql', plugins_url( $this->shfolder . '/scripts/shBrushSql.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-swift', plugins_url( $this->shfolder . '/scripts/shBrushSwift.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-vb', plugins_url( $this->shfolder . '/scripts/shBrushVb.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-xml', plugins_url( $this->shfolder . '/scripts/shBrushXml.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_script( 'syntaxhighlighter-brush-yaml', plugins_url( $this->shfolder . '/scripts/shBrushYaml.js', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); // Register some popular third-party brushes wp_register_script( 'syntaxhighlighter-brush-clojure', plugins_url( 'third-party-brushes/shBrushClojure.js', __FILE__ ), array( 'syntaxhighlighter-core' ), '20090602' ); wp_register_script( 'syntaxhighlighter-brush-fsharp', plugins_url( 'third-party-brushes/shBrushFSharp.js', __FILE__ ), array( 'syntaxhighlighter-core' ), '20091003' ); wp_register_script( 'syntaxhighlighter-brush-latex', plugins_url( 'third-party-brushes/shBrushLatex.js', __FILE__ ), array( 'syntaxhighlighter-core' ), '20090613' ); wp_register_script( 'syntaxhighlighter-brush-matlabkey', plugins_url( 'third-party-brushes/shBrushMatlabKey.js', __FILE__ ), array( 'syntaxhighlighter-core' ), '20091209' ); wp_register_script( 'syntaxhighlighter-brush-objc', plugins_url( 'third-party-brushes/shBrushObjC.js', __FILE__ ), array( 'syntaxhighlighter-core' ), '20091207' ); wp_register_script( 'syntaxhighlighter-brush-r', plugins_url( 'third-party-brushes/shBrushR.js', __FILE__ ), array( 'syntaxhighlighter-core' ), '20100919' ); // Register theme stylesheets wp_register_style( 'syntaxhighlighter-core', plugins_url( $this->shfolder . '/styles/shCore.css', __FILE__ ), array(), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-default', plugins_url( $this->shfolder . '/styles/shThemeDefault.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-django', plugins_url( $this->shfolder . '/styles/shThemeDjango.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-eclipse', plugins_url( $this->shfolder . '/styles/shThemeEclipse.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-emacs', plugins_url( $this->shfolder . '/styles/shThemeEmacs.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-fadetogrey', plugins_url( $this->shfolder . '/styles/shThemeFadeToGrey.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-midnight', plugins_url( $this->shfolder . '/styles/shThemeMidnight.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); wp_register_style( 'syntaxhighlighter-theme-rdark', plugins_url( $this->shfolder . '/styles/shThemeRDark.css', __FILE__ ), array( 'syntaxhighlighter-core' ), $this->agshver ); // phpcs:enable // Create list of brush aliases and map them to their real brushes // The key is the language alias // The value is the script handle suffix: syntaxhighlighter-brush-ThisBitHere (your plugin needs to register the script itself) $this->brushes = (array) apply_filters( 'syntaxhighlighter_brushes', array( 'as3' => 'as3', 'actionscript3' => 'as3', 'arduino' => 'arduino', 'bash' => 'bash', 'shell' => 'bash', 'coldfusion' => 'coldfusion', 'cf' => 'coldfusion', 'clojure' => 'clojure', 'clj' => 'clojure', 'cpp' => 'cpp', 'c' => 'cpp', 'c-sharp' => 'csharp', 'csharp' => 'csharp', 'css' => 'css', 'delphi' => 'delphi', 'pas' => 'delphi', 'pascal' => 'delphi', 'diff' => 'diff', 'patch' => 'diff', 'erl' => 'erlang', 'erlang' => 'erlang', 'fsharp' => 'fsharp', 'go' => 'go', 'golang' => 'go', 'groovy' => 'groovy', 'haskell' => 'haskell', 'java' => 'java', 'jfx' => 'javafx', 'javafx' => 'javafx', 'js' => 'jscript', 'jscript' => 'jscript', 'javascript' => 'jscript', 'latex' => 'latex', // Not used as a shortcode 'tex' => 'latex', 'matlab' => 'matlabkey', 'matlabkey' => 'matlabkey', 'objc' => 'objc', 'obj-c' => 'objc', 'perl' => 'perl', 'pl' => 'perl', 'php' => 'php', 'plain' => 'plain', 'text' => 'plain', 'ps' => 'powershell', 'powershell' => 'powershell', 'py' => 'python', 'python' => 'python', 'r' => 'r', // Not used as a shortcode 'splus' => 'r', 'rails' => 'ruby', 'rb' => 'ruby', 'ror' => 'ruby', 'ruby' => 'ruby', 'scala' => 'scala', 'sql' => 'sql', 'swift' => 'swift', 'vb' => 'vb', 'vbnet' => 'vb', 'xml' => 'xml', 'xhtml' => 'xml', 'xslt' => 'xml', 'html' => 'xml', 'yaml' => 'yaml', 'yml' => 'yaml', ) ); // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma $this->brush_names = (array) apply_filters( 'syntaxhighlighter_brush_names', array( 'as3' => __( 'ActionScript', 'syntaxhighlighter' ), 'arduino' => __( 'Arduino', 'syntaxhighlighter' ), 'bash' => __( 'BASH / Shell', 'syntaxhighlighter' ), 'coldfusion' => __( 'ColdFusion', 'syntaxhighlighter' ), 'clojure' => __( 'Clojure', 'syntaxhighlighter' ), 'cpp' => __( 'C / C++', 'syntaxhighlighter' ), 'csharp' => __( 'C#', 'syntaxhighlighter' ), 'css' => __( 'CSS', 'syntaxhighlighter' ), 'delphi' => __( 'Delphi / Pascal', 'syntaxhighlighter' ), 'diff' => __( 'diff / patch', 'syntaxhighlighter' ), 'erlang' => __( 'Erlang', 'syntaxhighlighter' ), 'fsharp' => __( 'F#', 'syntaxhighlighter' ), 'go' => __( 'Go', 'syntaxhighlighter' ), 'groovy' => __( 'Groovy', 'syntaxhighlighter' ), 'haskell' => __( 'Haskell', 'syntaxhighlighter' ), 'java' => __( 'Java', 'syntaxhighlighter' ), 'javafx' => __( 'JavaFX', 'syntaxhighlighter' ), 'jscript' => __( 'JavaScript', 'syntaxhighlighter' ), 'latex' => __( 'LaTeX', 'syntaxhighlighter' ), 'matlabkey' => __( 'MATLAB', 'syntaxhighlighter' ), 'objc' => __( 'Objective-C', 'syntaxhighlighter' ), 'perl' => __( 'Perl', 'syntaxhighlighter' ), 'php' => __( 'PHP', 'syntaxhighlighter' ), 'plain' => __( 'Plain Text', 'syntaxhighlighter' ), 'powershell' => __( 'PowerShell', 'syntaxhighlighter' ), 'python' => __( 'Python', 'syntaxhighlighter' ), 'r' => __( 'R', 'syntaxhighlighter' ), 'ruby' => __( 'Ruby / Ruby on Rails', 'syntaxhighlighter' ), 'scala' => __( 'Scala', 'syntaxhighlighter' ), 'swift' => __( 'Swift', 'syntaxhighlighter' ), 'sql' => __( 'SQL', 'syntaxhighlighter' ), 'vb' => __( 'Visual Basic', 'syntaxhighlighter' ), 'xml' => __( 'HTML / XHTML / XML / XSLT', 'syntaxhighlighter' ), 'yaml' => __( 'YAML', 'syntaxhighlighter' ), ) ); // phpcs:enable // Add any custom brushes that aren't making use of the newer "syntaxhighlighter_brush_names" filter. foreach ( $this->brushes as $slug => $language ) { if ( ! isset( $this->brush_names[ $language ] ) ) { $this->brush_names[ $language ] = $slug; } } // Create a list of shortcodes to use. You can use the filter to add/remove ones. // If the language/lang parameter is left out, it's assumed the shortcode name is the language. // If that's invalid, then "plain" is used. $this->shortcodes = array( 'sourcecode', 'source', 'code' ); $this->shortcodes = array_merge( $this->shortcodes, array_keys( $this->brushes ) ); // Remove some shortcodes we don't want while still supporting them as language values unset( $this->shortcodes[array_search( 'latex', $this->shortcodes )] ); // Remove "latex" shortcode (it'll collide) unset( $this->shortcodes[array_search( 'r', $this->shortcodes )] ); // Remove "r" shortcode (too short) $this->shortcodes = (array) apply_filters( 'syntaxhighlighter_shortcodes', $this->shortcodes ); // Register each shortcode with a placeholder callback so that strip_shortcodes() will work // The proper callback and such is done in SyntaxHighlighter::shortcode_hack() foreach ( $this->shortcodes as $shortcode ) add_shortcode( $shortcode, '__return_empty_string' ); // Create list of themes and their human readable names // Plugins can add to this list: http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/adding-a-new-theme/ $this->themes = (array) apply_filters( 'syntaxhighlighter_themes', array( 'default' => __( 'Default', 'syntaxhighlighter' ), 'django' => __( 'Django', 'syntaxhighlighter' ), 'eclipse' => __( 'Eclipse', 'syntaxhighlighter' ), 'emacs' => __( 'Emacs', 'syntaxhighlighter' ), 'fadetogrey' => __( 'Fade to Grey', 'syntaxhighlighter' ), 'midnight' => __( 'Midnight', 'syntaxhighlighter' ), 'rdark' => __( 'RDark', 'syntaxhighlighter' ), 'none' => __( '[None]', 'syntaxhighlighter' ), ) ); // Other special characters that need to be encoded before going into the database (namely to work around kses) $this->specialchars = (array) apply_filters( 'syntaxhighlighter_specialchars', array( '\0' => '\0', ) ); } // Register the settings page function register_settings_page() { add_options_page( __( 'SyntaxHighlighter Settings', 'syntaxhighlighter' ), __( 'SyntaxHighlighter', 'syntaxhighlighter' ), 'manage_options', 'syntaxhighlighter', array( $this, 'settings_page' ) ); } // Register the plugin's setting function register_setting() { register_setting( 'syntaxhighlighter_settings', 'syntaxhighlighter_settings', array( $this, 'validate_settings' ) ); } // Enqueue block assets for the Editor function enqueue_block_editor_assets() { wp_enqueue_script( 'syntaxhighlighter-blocks', plugins_url( 'dist/blocks.build.js', __FILE__ ), array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' ), ( ( defined( 'WP_DEBUG' ) && WP_DEBUG ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) ? filemtime( plugin_dir_path( __FILE__ ) . 'dist/blocks.build.js' ) : $this->pluginver ); wp_enqueue_style( 'syntaxhighlighter-blocks-css', // Handle. plugins_url( 'dist/blocks.editor.build.css', __FILE__ ), [], ( ( defined( 'WP_DEBUG' ) && WP_DEBUG ) || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) ? filemtime( plugin_dir_path( __FILE__ ) . 'dist/blocks.editor.build.css' ) : $this->pluginver ); // WordPress 5.0+ only, no Gutenberg plugin support if ( function_exists( 'wp_set_script_translations' ) ) { wp_set_script_translations( 'syntaxhighlighter-blocks', 'syntaxhighlighter' ); } natcasesort( $this->brush_names ); $settings = (object) array( 'language' => (object) array( 'supported' => true, 'default' => 'plain' ), 'lineNumbers' => (object) array( 'supported' => true, 'default' => (bool) $this->settings['gutter'], ), 'firstLineNumber' => (object) array( 'supported' => true, 'default' => $this->settings['firstline'], ), 'highlightLines' => (object) array( 'supported' => true, 'default' => '', ), 'wrapLines' => (object) array( 'supported' => ( '2' == $this->settings['shversion'] ), 'default' => (bool) $this->settings['wraplines'], ), 'makeURLsClickable' => (object) array( 'supported' => true, 'default' => (bool) $this->settings['autolinks'], ), 'quickCode' => (object) array( 'supported' => ( '3' == $this->settings['shversion'] ), 'default' => (bool) $this->settings['quickcode'], ), 'tabSize' => (int) $this->settings['tabsize'], ); wp_add_inline_script( 'syntaxhighlighter-blocks', sprintf( ' var syntaxHighlighterData = { brushes: %s, settings: %s, };', json_encode( $this->brush_names ), json_encode( $settings ) ), 'before' ); } /** * Enable the brushes that are used in blocks. * * For the shortcode, brushes are activated by `shortcode_callback()`, but that won't detect brushes that were * used in blocks. For that, we need to extract the data from the block's attributes. * * @since 3.3.0 * * @param string $content The post content. * * @return string Unmodified $content. */ function enable_brushes_used_in_blocks( $content ) { /* * Return early on the backend because SyntaxHighlighting is only active on the front end, see * `syntaxHighlighterCode::edit()` block for details. */ if ( is_admin() ) { return $content; } // Lower overhead than a full parse. if ( ! has_block( 'syntaxhighlighter/code', $content ) && ! has_block( 'core/block', $content ) // Reusable ) { return $content; } if ( function_exists( 'parse_blocks' ) ) { // WP 5.0+ $blocks = parse_blocks( $content ); } elseif ( Function_exists( 'gutenberg_parse_blocks' ) ) { // Gutenberg plugin $blocks = gutenberg_parse_blocks( $content ); } else { return $content; } foreach ( $blocks as $block ) { if ( empty( $block['blockName'] ) ) { continue; } switch ( $block['blockName'] ) { // Normal block usage case 'syntaxhighlighter/code': $language = ( ! empty( $block['attrs']['language'] ) ) ? $block['attrs']['language'] : 'plain'; if ( in_array( $language, $this->brushes, true ) ) { $this->usedbrushes[ $this->brushes[ $language ] ] = true; } break; // But the block could also be used inside of a reusable block case 'core/block': /** * Rather than going down the block parsing rabbit hole of dynamic blocks, * let's just hook in post-render and look for our HTML. */ add_filter( 'the_content', array( $this, 'enable_brushes_via_raw_html_parsing' ), 10 ); break; } } return $content; } /** * Parses raw HTML looking for SyntaxHighlighter's
tags and queues brushes
* for the languages used.
*
* This filter only runs when a reusable block is used in a post.
* It was easier to implement than loading the block, which then could
* have child blocks and so forth. Technically this means that we're
* parsing any regular SyntaxHighlighter blocks again, but oh well. :)
*
* @since 3.4.2
*
* @param string $content The post content.
*
* @return string Unmodified $content.
*/
public function enable_brushes_via_raw_html_parsing( $content ) {
if ( false === strpos( $content,'brushes, true ) ) {
$this->usedbrushes[ $this->brushes[ $language ] ] = true;
}
}
}
return $content;
}
/**
* Renders the content of the Gutenberg block on the front end
* using the shortcode callback. This ensures one source of truth
* and allows for forward compatibility.
*
* @param string $content The block's content.
*
* @return string The rendered content.
*/
public function render_block( $attributes, $content ) {
$remaps = array(
'lineNumbers' => 'gutter',
'firstLineNumber' => 'firstline',
'highlightLines' => 'highlight',
'wrapLines' => 'wraplines',
'makeURLsClickable' => 'autolinks',
'quickCode' => 'quickcode',
);
$classnames = ! empty( $attributes['className'] ) ? [ esc_attr( $attributes['className'] ) ] : [];
if ( ! empty( $attributes['align'] ) ) {
$classnames[] = 'align' . esc_attr( $attributes['align'] );
}
foreach ( $remaps as $from => $to ) {
if ( isset( $attributes[ $from ] ) ) {
if ( is_bool( $attributes[ $from ] ) ) {
$attributes[ $to ] = ( $attributes[ $from ] ) ? '1' : '0';
} else {
$attributes[ $to ] = $attributes[ $from ];
}
unset( $attributes[ $from ] );
}
}
$code = preg_replace( '#]+>([^<]+)?
#', '$1', $content );
// Undo escaping done by WordPress
$code = htmlspecialchars_decode( $code );
$code = preg_replace( '/^(\s*https?:)?47;?47;([^\s<>"]+\s*)$/m', '$1//$2', $code );
$code = $this->shortcode_callback( $attributes, $code, 'code' );
return '' . $code . '';
}
// Add the custom TinyMCE plugin which wraps plugin shortcodes in in TinyMCE
function add_tinymce_plugin( $plugins ) {
global $tinymce_version;
add_action( 'admin_print_footer_scripts', array( $this, 'output_shortcodes_for_tinymce' ), 9 );
if ( substr( $tinymce_version, 0, 1 ) < 4 ) {
$plugins['syntaxhighlighter'] = plugins_url( 'syntaxhighlighter_mce.js', __FILE__ );
} else {
$plugins['syntaxhighlighter'] = add_query_arg( 'ver', $this->pluginver, plugins_url( 'syntaxhighlighter_mce-4.js', __FILE__ ) );
wp_enqueue_script( 'syntaxhighlighter', plugins_url( 'syntaxhighlighter.js', __FILE__ ), array(), false, true );
}
return $plugins;
}
// Break the TinyMCE cache
function break_tinymce_cache( $version ) {
return $version . '-sh' . $this->pluginver;
}
// Add a "Settings" link to the plugins page
function settings_link( $links, $file ) {
static $this_plugin;
if ( empty( $this_plugin ) ) {
$this_plugin = plugin_basename(__FILE__);
}
if ( $file == $this_plugin ) {
$links[] = '' . __( 'Settings', 'syntaxhighlighter' ) . '';
}
return $links;
}
// Output list of shortcode tags for the TinyMCE plugin
function output_shortcodes_for_tinymce() {
$shortcodes = array();
foreach ( $this->shortcodes as $shortcode )
$shortcodes[] = preg_quote( $shortcode );
echo "\n";
}
/**
* Returns all shortcodes not handled by SyntaxHighlighter unchanged, so they
* can be processed by their original handlers after SyntaxHighlighter has
* run.
*
* @param mixed $output The shortcode's returned value (false by default).
* @param string $tag The name of the shortcode.
* @param array|null $attr The shortcode attributes.
* @param array $m Regular expression match array.
* @return string|false Return the matched shortcode as-is for all shortcodes not handled by SyntaxHighlighter, returns $output otherwise.
*/
function pre_do_shortcode_shortcode_hack_skip_others( $output, $tag, $attr, $m ) {
if ( ! in_array( $tag, $this->shortcodes, true ) ) {
return $m[0];
}
return $output;
}
/**
* Process only this plugin's shortcodes.
*
* If we waited for the normal do_shortcode() call at priority 11,
* then wpautop() and maybe others would mangle all of the code.
*
* So instead we hook in earlier with this function and process
* just this plugins's shortcodes. To do this requires some trickery.
*
* First we need to clear out all existing shortcodes, then register
* just this plugin's ones, process them, and then restore the original
* list of shortcodes. Additionally, we have to add another hack to other
* shortcodes, such as [gallery], to return the entire shortcode string as
* it appears in the original content to allow SyntaxHighlighter's shortcode
* strings (e.g., [c]) be used within other shortcodes without interference
* from SyntaxHighlighter.
*
* To make matters more complicated, if someone has done [[code]foo[/code]]
* in order to display the shortcode (not render it), then do_shortcode()
* will strip the outside brackets and when do_shortcode() runs a second
* time later on, it will render it.
*
* So instead before do_shortcode() runs for the first time, we add
* even more brackets escaped shortcodes in order to result in
* the shortcodes actually being displayed instead rendered.
*
* Phew!
*
* @param string $content The post content.
* @param array $callback The callback function that should be used for add_shortcode()
* @param bool $ignore_html When true, shortcodes inside HTML elements will be skipped.
*
* @return string The filtered content, with this plugin's shortcodes parsed.
*/
function shortcode_hack( $content, $callback, $ignore_html = true ) {
global $shortcode_tags;
// Regex is slow. Let's do some strpos() checks first.
if ( ! $this->string_has_shortcodes( $content, $this->shortcodes ) ) {
return $content;
}
// Backup current registered shortcodes and clear them all out (we do not backup our own, because we will add and parse them below)
$orig_shortcode_tags = array_diff_key( $shortcode_tags, array_flip( $this->shortcodes ) );
remove_all_shortcodes();
// Register all of this plugin's shortcodes
foreach ( $this->shortcodes as $shortcode ) {
add_shortcode( $shortcode, $callback );
}
// Register all other shortcodes, ensuring their content remains unchanged using yet another hack.
foreach ( $orig_shortcode_tags as $shortcode_tagname => $shortcode ) {
add_shortcode( $shortcode_tagname, '__return_empty_string' );
}
add_filter( 'pre_do_shortcode_tag', array( $this, 'pre_do_shortcode_shortcode_hack_skip_others' ), 10, 4 );
$regex = '/' . get_shortcode_regex() . '/';
// Parse the shortcodes (only this plugins's are registered)
if ( $ignore_html ) {
// Extra escape escaped shortcodes because do_shortcode_tag() called by do_shortcode() is going to strip a pair of square brackets when it runs
$content = preg_replace_callback(
$regex,
array( $this, 'shortcode_hack_extra_escape_escaped_shortcodes' ),
$content
);
// Normal, safe parsing
$content = do_shortcode( $content, true );
} else {
// Extra escape escaped shortcodes because do_shortcode_tag() called by do_shortcode() is going to strip a pair of square brackets when it runs.
// Then call do_shortcode_tag(). This is basically do_shortcode() without calling do_shortcodes_in_html_tags() which breaks things.
// For context, see https://wordpress.org/support/topic/php-opening-closing-tags-break-code-blocks
$content = preg_replace_callback(
$regex,
array( $this, 'shortcode_hack_extra_escape_escaped_shortcodes_and_parse' ),
$content
);
}
// Put the original shortcodes back, and remove the hacky pre_do_shortcode_tag filter
$shortcode_tags = $orig_shortcode_tags;
remove_filter('pre_do_shortcode_tag', array($this, 'pre_do_shortcode_shortcode_hack_skip_others'), 10);
return $content;
}
/**
* A quick checker to see if any of this plugin's shortcodes are in use in a string.
* Since all of the tags can't be self-closing, we look for the closing tag.
*
* @param string $string The string to look through. This is a post's contents usually.
* @param array $shortcodes The array of shortcodes to look for.
*
* @return bool Whether any shortcode usage was found.
*/
function string_has_shortcodes( $string, $shortcodes ) {
foreach ( $shortcodes as $shortcode ) {
if ( false !== strpos( $string, "[/{$shortcode}]" ) ) {
return true;
}
}
return false;
}
/**
* Add extra square brackets around escaped shortcodes.
* This is to counteract the beginning of the do_shortcode_tag() function.
*
* @param array $match The array of matches generated by get_shortcode_regex()
*
* @return string What should be placed into the post content. In this case it's the raw match, or an extra-wrapped raw match.
*/
function shortcode_hack_extra_escape_escaped_shortcodes( $match ) {
if ( $match[1] == '[' && $match[6] == ']' ) {
return '[' . $match[0] . ']';
}
return $match[0];
}
/**
* This is a combination of this class's shortcode_hack_extra_escape_escaped_shortcodes()
* and do_shortcode_tag() for performance reasons so that we don't have to run some regex twice.
*
* @param array $match Regular expression match array.
*
* @return string|false False on failure, otherwise a parse shortcode tag.
*/
function shortcode_hack_extra_escape_escaped_shortcodes_and_parse( $match ) {
$match[0] = $this->shortcode_hack_extra_escape_escaped_shortcodes( $match );
return do_shortcode_tag( $match );
}
// The main filter for the post contents. The regular shortcode filter can't be used as it's post-wpautop().
function parse_shortcodes( $content ) {
return $this->shortcode_hack( $content, array( $this, 'shortcode_callback' ) );
}
// HTML entity encode the contents of shortcodes
function encode_shortcode_contents( $content ) {
return $this->shortcode_hack( $content, array( $this, 'encode_shortcode_contents_callback' ), false );
}
// HTML entity encode the contents of shortcodes. Expects slashed content.
function encode_shortcode_contents_slashed( $content ) {
return addslashes( $this->encode_shortcode_contents( stripslashes( $content ) ) );
}
// HTML entity encode the contents of shortcodes. Expects slashed content. Aborts if AJAX.
function encode_shortcode_contents_slashed_noquickedit( $content ) {
// In certain weird circumstances, the content gets run through "content_save_pre" twice
// Keep track and don't allow this filter to be run twice
// I couldn't easily figure out why this happens and didn't bother looking into it further as this works fine
if ( true == $this->content_save_pre_ran ) {
return $content;
}
$this->content_save_pre_ran = true;
// Post quick edits aren't decoded for display, so we don't need to encode them (again)
// This also aborts for (un)trashing to avoid extra encoding.
if ( empty( $_POST ) || ( ! empty( $_POST['action'] ) && 'inline-save' == $_POST['action'] ) ) {
return $content;
}
return $this->encode_shortcode_contents_slashed( $content );
}
// HTML entity decode the contents of shortcodes
function decode_shortcode_contents( $content ) {
return $this->shortcode_hack( $content, array( $this, 'decode_shortcode_contents_callback' ), false );
}
// The callback function for SyntaxHighlighter::encode_shortcode_contents()
function encode_shortcode_contents_callback( $atts, $code = '', $tag = false ) {
$this->encoded = true;
$code = str_replace( array_keys( $this->specialchars ), array_values( $this->specialchars ), htmlspecialchars( $code ) );
return '[' . $tag . $this->atts2string( $atts ) . "]{$code}[/$tag]";
}
// The callback function for SyntaxHighlighter::decode_shortcode_contents()
// Shortcode attribute values need to not be quoted with TinyMCE disabled for some reason (weird bug)
function decode_shortcode_contents_callback( $atts, $code = '', $tag = false ) {
$quotes = ( user_can_richedit() ) ? true : false;
$code = str_replace( array_values( $this->specialchars ), array_keys( $this->specialchars ), htmlspecialchars_decode( $code ) );
return '[' . $tag . $this->atts2string( $atts, $quotes ) . "]{$code}[/$tag]";
}
// Dynamically format the post content for the edit form
function the_editor_content( $content ) {
global $post;
// New code format (stored encoded in database)
if ( 2 == $this->get_code_format( $post ) ) {
// If TinyMCE is disabled or the HTML tab is set to be displayed first, we need to decode the HTML
if ( !user_can_richedit() || 'html' == wp_default_editor() )
$content = $this->decode_shortcode_contents( $content );
}
// Old code format (stored raw in database)
else {
// If TinyMCE is enabled and is set to be displayed first, we need to encode the HTML
if ( user_can_richedit() && 'html' != wp_default_editor() )
$content = $this->encode_shortcode_contents( $content );
}
return $content;
}
// Run SyntaxHighlighter::encode_shortcode_contents() on the contents of the text widget
function widget_text_save( $instance, $new_instance, $old_instance, $widgetclass ) {
if ( 'text' == $widgetclass->id_base ) {
// Re-save the widget settings but this time with the shortcode contents encoded
$new_instance['text'] = $this->encode_shortcode_contents( $new_instance['text'] );
$instance = $widgetclass->update( $new_instance, $old_instance );
// And flag it as encoded
$instance['syntaxhighlighter_encoded'] = true;
}
return $instance;
}
// Run SyntaxHighlighter::decode_shortcode_contents_callback() on the contents of the text widget form
function widget_text_form( $instance, $widgetclass ) {
if ( 'text' == $widgetclass->id_base && ! empty( $instance['syntaxhighlighter_encoded'] ) ) {
$instance['text'] = $this->shortcode_hack( $instance['text'], array( $this, 'decode_shortcode_contents_callback' ) );
}
return $instance;
}
// Run SyntaxHighlighter::parse_shortcodes() on the contents of a text widget
function widget_text_output( $content, $instance = false ) {
$this->codeformat = ( false === $instance || empty( $instance['syntaxhighlighter_encoded'] ) ) ? 1 : 2;
$content = $this->parse_shortcodes( $content );
$this->codeformat = false;
return $content;
}
// Run SyntaxHighlighter::parse_shortcodes() on the contents of a comment
function parse_shortcodes_comment( $content ) {
$this->codeformat = 2;
$content = $this->parse_shortcodes( $content );
$this->codeformat = false;
return $content;
}
// This function determines what version of SyntaxHighlighter was used when the post was written
// This is because the code was stored differently for different versions of SyntaxHighlighter
function get_code_format( $post ) {
if ( false !== $this->codeformat ) {
return $this->codeformat;
}
if ( empty( $post ) ) {
$post = new stdClass();
}
if ( null !== $version = apply_filters( 'syntaxhighlighter_pre_getcodeformat', null, $post ) )
return $version;
$version = ( empty( $post->ID ) || get_post_meta( $post->ID, '_syntaxhighlighter_encoded', true ) || get_post_meta( $post->ID, 'syntaxhighlighter_encoded', true ) ) ? 2 : 1;
return apply_filters( 'syntaxhighlighter_getcodeformat', $version, $post );
}
// Adds a post meta saying that HTML entities are encoded (for backwards compatibility)
function mark_as_encoded( $post_ID, $post ) {
if ( false == $this->encoded || 'revision' == $post->post_type )
return;
delete_post_meta( $post_ID, 'syntaxhighlighter_encoded' ); // Previously used
add_post_meta( $post_ID, '_syntaxhighlighter_encoded', true, true );
}
// Transforms an attributes array into a 'key="value"' format (i.e. reverses the process)
function atts2string( $atts, $quotes = true ) {
if ( empty( $atts ) ) {
return '';
}
$atts = $this->attributefix( $atts );
// Re-map [code="php"] style tags
if ( isset( $atts[0] ) ) {
if ( empty( $atts['language'] ) ) {
$atts['language'] = $atts[0];
}
unset( $atts[0] );
}
$strings = array();
foreach ( $atts as $key => $value )
$strings[] = ( $quotes ) ? $key . '="' . esc_attr( $value ) . '"' : $key . '=' . esc_attr( $value );
return ' ' . implode( ' ', $strings );
}
// Simple function for escaping just single quotes (the original js_escape() escapes more than we need)
function js_escape_singlequotes( $string ) {
return str_replace( "'", "\'", $string );
}
// Output any needed scripts. This is meant for the footer.
function maybe_output_scripts() {
global $wp_styles;
if ( 1 == $this->settings['loadallbrushes'] ) {
$this->usedbrushes = array_flip( array_values( $this->brushes ) );
}
if ( empty( $this->usedbrushes ) ) {
return;
}
$scripts = array();
foreach ( $this->usedbrushes as $brush => $unused )
$scripts[] = 'syntaxhighlighter-brush-' . strtolower( $brush );
wp_print_scripts( $scripts );
// Stylesheets can't be in the footer, so inject them via Javascript
echo "
's
function shortcode_callback( $atts, $code = '', $tag = false ) {
global $post;
if ( false === $tag || empty( $code ) ) {
return $code;
}
// Avoid PHP notices
if ( ! isset( $post ) ) {
$post = null;
}
$code = apply_filters( 'syntaxhighlighter_precode', $code, $atts, $tag );
// Error fixing for [tag="language"]
if ( isset( $atts[0] ) ) {
$atts = $this->attributefix( $atts );
$atts['language'] = $atts[0];
unset( $atts[0] );
}
// Default out all of the available parameters to "false" (easy way to check if they're set or not)
// Note this isn't the same as if the user passes the string "false" to the shortcode
$atts = (array) apply_filters( 'syntaxhighlighter_shortcodeatts', shortcode_atts( array(
'language' => false,
'lang' => false,
'type' => false, // language alias
'autolinks' => false,
'classname' => false,
'collapse' => false,
'firstline' => false,
'fontsize' => false,
'gutter' => false,
'highlight' => false,
'htmlscript' => false,
'light' => false,
'padlinenumbers' => false,
'smarttabs' => false,
'tabsize' => false,
'title' => $this->settings['title'],
'toolbar' => false,
'wraplines' => false,
'quickcode' => false,
), $atts ) );
// Check for language shortcode tag such as [php]code[/php]
if ( isset( $this->brushes[ $tag ] ) ) {
$lang = $tag;
}
// If a valid tag is not used, it must be sourcecode/source/code
else {
$atts = $this->attributefix( $atts );
// Check for the "language" attribute
if ( false !== $atts['language'] )
$lang = $atts['language'];
// Check for the "lang" attribute
elseif ( false !== $atts['lang'] )
$lang = $atts['lang'];
// Default to plain text
else
$lang = 'text';
// All language aliases are lowercase
$lang = strtolower( $lang );
// Validate passed attribute
if ( ! isset( $this->brushes[ $lang ] ) ) {
return $code;
}
}
// Switch from the alias to the real brush name (so custom aliases can be used)
$lang = $this->brushes[ $lang ];
// Register this brush as used so it's script will be outputted
$this->usedbrushes[ $lang ] = true;
$params = array();
$params[] = "brush: $lang;";
// Fix bug that prevents collapse from working if the toolbar is off or light mode is on
if ( 'true' == $atts['collapse'] || '1' === $atts['collapse'] || 1 == $this->settings['collapse'] ) {
$atts['toolbar'] = 'true';
$atts['light'] = 'false';
}
// Parameter renaming (the shortcode API doesn't like parameter names with dashes)
$rename_map = array(
'autolinks' => 'auto-links',
'classname' => 'class-name',
'firstline' => 'first-line',
'fontsize' => 'font-size',
'htmlscript' => 'html-script',
'padlinenumbers' => 'pad-line-numbers',
'smarttabs' => 'smart-tabs',
'tabsize' => 'tab-size',
'wraplines' => 'wrap-lines',
'quickcode' => 'quick-code',
);
// Allowed configuration parameters and their type
// Use the proper names (see above)
$allowed_atts = (array) apply_filters( 'syntaxhighlighter_allowedatts', array(
'auto-links' => 'boolean',
'class-name' => 'other',
'collapse' => 'boolean',
'first-line' => 'integer',
'font-size' => 'integer',
'gutter' => 'boolean',
'highlight' => 'other',
'html-script' => 'boolean',
'light' => 'boolean',
'pad-line-numbers' => 'other',
'smart-tabs' => 'boolean',
'tab-size' => 'integer',
'title' => 'other',
'toolbar' => 'boolean',
'wrap-lines' => 'boolean',
'quick-code' => 'boolean',
) );
$title = '';
// Sanitize configuration parameters and such
foreach ( $atts as $key => $value ) {
$key = strtolower( $key );
// Put back parameter names that have been renamed for shortcode use
if ( ! empty( $rename_map[ $key ] ) ) {
$key = $rename_map[ $key ];
}
// This this parameter if it's unknown, not set, or the language which was already handled
if ( empty( $allowed_atts[ $key ] ) || false === $value || in_array( $key, array( 'language', 'lang' ) ) ) {
continue;
}
// Sanitize values
switch ( $allowed_atts[ $key ] ) {
case 'boolean':
$value = strtolower( $value );
if ( 'true' === $value || '1' === $value || 'on' == $value )
$value = 'true';
elseif ( 'false' === $value || '0' === $value || 'off' == $value )
$value = 'false';
else
continue 2; // Invalid value, ditch parameter
break;
// integer
case 'integer':
$value = (int) $value;
break;
}
// Sanitize the "classname" parameter
if ( 'class-name' == $key )
$value = "'" . trim( preg_replace( '/[^a-zA-Z0-9 _-]/i', '', $value ) ) . "'";
// Special sanitization for "pad-line-numbers"
if ( 'pad-line-numbers' == $key ) {
$value = strtolower( $value );
if ( 'true' === $value || '1' === $value )
$value = 'true';
elseif ( 'false' === $value || '0' === $value )
$value = 'false';
else
$value = (int) $value;
}
// Add % sign to "font-size"
if ( 'font-size' == $key )
$value = $value . '%';
// If "html-script", then include the XML brush as it's needed
if ( 'html-script' == $key && 'true' == $value )
$this->usedbrushes['xml'] = true;
// Sanitize row highlights
if ( 'highlight' == $key ) {
if ( false === strpos( $value, ',' ) && false === strpos( $value, '-' ) ) {
$value = (int) $value;
} else {
$lines = explode( ',', $value );
$highlights = array();
foreach ( $lines as $line ) {
// Line range
if ( false !== strpos( $line, '-' ) ) {
list( $range_start, $range_end ) = array_map( 'intval', explode( '-', $line ) );
if ( ! $range_start || ! $range_end || $range_end <= $range_start )
continue;
for ( $i = $range_start; $i <= $range_end; $i++ )
$highlights[] = $i;
} else {
$highlights[] = (int) $line;
}
}
natsort( $highlights );
$value = implode( ',', $highlights );
}
if ( empty( $value ) )
continue;
// Wrap highlight in [ ]
$params[] = "$key: [$value];";
continue;
}
// Don't allow HTML in the title parameter
if ( 'title' == $key ) {
$value = strip_tags( html_entity_decode( strip_tags( $value ) ) );
}
$params[] = "$key: $value;";
// Set the title variable if the title parameter is set (but not for feeds)
if ( 'title' == $key && ! is_feed() )
$title = ' title="' . esc_attr( $value ) . '"';
}
$code = ( false === strpos( $code, '<' ) && false === strpos( $code, '>' ) && 2 == $this->get_code_format( $post ) ) ? strip_tags( $code ) : htmlspecialchars( $code );
// Escape shortcodes
$code = preg_replace( '/\[/', '[', $code );
$params[] = 'notranslate'; // For Google, see http://otto42.com/9k
$params = apply_filters( 'syntaxhighlighter_cssclasses', $params ); // Use this to add additional CSS classes / SH parameters
return apply_filters( 'syntaxhighlighter_htmlresult', '' . $code . '
' );
}
// Settings page
function settings_page() { ?>
';
$title = ( empty( $this->settings['title'] ) && 1 != $this->settings['collapse'] ) ? ' title="Code example: (this example was added using the title parameter)"' : '';
// Site owners may opt to disable the short tags, i.e. [php]
$democode = apply_filters( 'syntaxhighlighter_democode', '[sourcecode language="php" htmlscript="true" highlight="12"' . $title . ']
PHP Code Example
' . __( 'PHP Code Example', 'syntaxhighlighter' ) . '
' . __( 'This line is highlighted.', 'syntaxhighlighter' ) . '
[/sourcecode]' );
$this->codeformat = 1;
echo $this->parse_shortcodes( $democode );
$this->codeformat = false;
echo '';
?>
true', '1', 'false', '0' ); ?>
lang',
'language',
implode( ', ', array_keys( $this->brushes ) )
),
array(
'a' => array(
'href' => array(),
),
'code' => array(),
)
);
?>
2,5-10,12'
);
?>
true (automatic padding), or an integer (forced padding).', 'padlinenumbers parameter', 'syntaxhighlighter' ),
'padlinenumbers'
);
?>
collapse'
);
?>
[php][/php][css autolinks="false" classname="myclass" collapse="false" firstline="1" gutter="true" highlight="1-3,6,9" htmlscript="false" light="false" padlinenumbers="false" smarttabs="true" tabsize="4" toolbar="true" title=""][/css][code lang="js"][/code][sourcecode language="plain"][/sourcecode]