It occurs to me that wpautop()
is the register_globals
of WordPress—a feature that was instrumental for its growth and popularity, but really needs to DIAF. They should rename the function wppeepee()
because it finds a way to pee pee on your content at the most inopportune moments, causing unending headaches in your code.
For those of you who don’t know, wpautop
can be seen as nl2br()
on steriods, or (as I prefer to call it) a poor man’s Markdown. It’s been in WordPress for almost forever, and it’s hard to imagine writing a blog post without it, even if it’s a Really Bad Idea™.
Here’s an example I ran into today:
Because WordPress’s shortcode interpreter is poorly-coded,1 you cannot mix self-closing ([shortcode]
or [shortcode /]
) and content-based ([shortcode]…[/shortcode]
shortcodes for a given shortcode in the same page. (FYWP #257) If it sounds confusing, it’s because it is. The takeaway here is that you just have to be very consistent in how you write shortcodes.
I noticed that the plugin I forked was using the the oEmbed handler to inject the shortcode that does the processing. This is taking advantage of a side effect in the ordering of the_content
actions:
- oEmbeds trigger via
WP_Embed::autoembed()
wpautop()
runs- regular shortcode processing is triggered
(Just being able to take advantage of this side-effect is reminiscent of the Perl-like mentality inherent in WordPress development. (FYWP #258))
The reason for this ordering is specifically designed to work around wpautop
as noted in the core codebase.
<?php | |
/** | |
* API for easily embedding rich media such as videos and images into content. | |
* | |
* @package WordPress | |
* @subpackage Embed | |
* @since 2.9.0 | |
*/ | |
class WP_Embed { | |
public $handlers = array(); | |
public $post_ID; | |
public $usecache = true; | |
public $linkifunknown = true; | |
/** | |
* When an URL cannot be embedded, return false instead of returning a link | |
* or the URL. Bypasses the 'embed_maybe_make_link' filter. | |
*/ | |
public $return_false_on_fail = false; | |
/** | |
* Constructor | |
*/ | |
public function __construct() { | |
// Hack to get the [embed] shortcode to run before wpautop() | |
add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 ); | |
// Shortcode placeholder for strip_shortcodes() | |
add_shortcode( 'embed', '__return_false' ); | |
// Attempts to embed all URLs in a post | |
add_filter( 'the_content', array( $this, 'autoembed' ), 8 ); | |
// After a post is saved, cache oEmbed items via AJAX | |
add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) ); | |
} | |
/** | |
* Process the [embed] shortcode. | |
* | |
* Since the [embed] shortcode needs to be run earlier than other shortcodes, | |
* this function removes all existing shortcodes, registers the [embed] shortcode, | |
* calls {@link do_shortcode()}, and then re-registers the old shortcodes. | |
* | |
* @uses $shortcode_tags | |
* | |
* @param string $content Content to parse | |
* @return string Content with shortcode parsed | |
*/ | |
public function run_shortcode( $content ) { | |
global $shortcode_tags; | |
// Back up current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
add_shortcode( 'embed', array( $this, 'shortcode' ) ); | |
// Do the shortcode (only the [embed] one is registered) | |
$content = do_shortcode( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
/** | |
* If a post/page was saved, then output JavaScript to make | |
* an AJAX request that will call WP_Embed::cache_oembed(). | |
*/ | |
public function maybe_run_ajax_cache() { | |
$post = get_post(); | |
if ( ! $post || empty( $_GET['message'] ) ) | |
return; | |
?> | |
<script type="text/javascript"> | |
/* <![CDATA[ */ | |
jQuery(document).ready(function($){ | |
$.get("<?php echo admin_url( 'admin-ajax.php?action=oembed-cache&post=' . $post->ID, 'relative' ); ?>"); | |
}); | |
/* ]]> */ | |
</script> | |
<?php | |
} | |
/** | |
* Register an embed handler. Do not use this function directly, use {@link wp_embed_register_handler()} instead. | |
* This function should probably also only be used for sites that do not support oEmbed. | |
* | |
* @param string $id An internal ID/name for the handler. Needs to be unique. | |
* @param string $regex The regex that will be used to see if this handler should be used for a URL. | |
* @param callback $callback The callback function that will be called if the regex is matched. | |
* @param int $priority Optional. Used to specify the order in which the registered handlers will be tested (default: 10). Lower numbers correspond with earlier testing, and handlers with the same priority are tested in the order in which they were added to the action. | |
*/ | |
public function register_handler( $id, $regex, $callback, $priority = 10 ) { | |
$this->handlers[$priority][$id] = array( | |
'regex' => $regex, | |
'callback' => $callback, | |
); | |
} | |
/** | |
* Unregister a previously registered embed handler. Do not use this function directly, use {@link wp_embed_unregister_handler()} instead. | |
* | |
* @param string $id The handler ID that should be removed. | |
* @param int $priority Optional. The priority of the handler to be removed (default: 10). | |
*/ | |
public function unregister_handler( $id, $priority = 10 ) { | |
if ( isset($this->handlers[$priority][$id]) ) | |
unset($this->handlers[$priority][$id]); | |
} | |
/** | |
* The {@link do_shortcode()} callback function. | |
* | |
* Attempts to convert a URL into embed HTML. Starts by checking the URL against the regex of the registered embed handlers. | |
* If none of the regex matches and it's enabled, then the URL will be given to the {@link WP_oEmbed} class. | |
* | |
* @param array $attr { | |
* Shortcode attributes. Optional. | |
* | |
* @type int $width Width of the embed in pixels. | |
* @type int $height Height of the embed in pixels. | |
* } | |
* @param string $url The URL attempting to be embedded. | |
* @return string The embed HTML on success, otherwise the original URL. | |
*/ | |
public function shortcode( $attr, $url = '' ) { | |
$post = get_post(); | |
if ( empty( $url ) && ! empty( $attr['src'] ) ) { | |
$url = $attr['src']; | |
} | |
if ( empty( $url ) ) | |
return ''; | |
$rawattr = $attr; | |
$attr = wp_parse_args( $attr, wp_embed_defaults( $url ) ); | |
// kses converts & into & and we need to undo this | |
// See https://core.trac.wordpress.org/ticket/11311 | |
$url = str_replace( '&', '&', $url ); | |
// Look for known internal handlers | |
ksort( $this->handlers ); | |
foreach ( $this->handlers as $priority => $handlers ) { | |
foreach ( $handlers as $id => $handler ) { | |
if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) { | |
if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) ) | |
/** | |
* Filter the returned embed handler. | |
* | |
* @since 2.9.0 | |
* | |
* @see WP_Embed::shortcode() | |
* | |
* @param mixed $return The shortcode callback function to call. | |
* @param string $url The attempted embed URL. | |
* @param array $attr An array of shortcode attributes. | |
*/ | |
return apply_filters( 'embed_handler_html', $return, $url, $attr ); | |
} | |
} | |
} | |
$post_ID = ( ! empty( $post->ID ) ) ? $post->ID : null; | |
if ( ! empty( $this->post_ID ) ) // Potentially set by WP_Embed::cache_oembed() | |
$post_ID = $this->post_ID; | |
// Unknown URL format. Let oEmbed have a go. | |
if ( $post_ID ) { | |
// Check for a cached result (stored in the post meta) | |
$key_suffix = md5( $url . serialize( $attr ) ); | |
$cachekey = '_oembed_' . $key_suffix; | |
$cachekey_time = '_oembed_time_' . $key_suffix; | |
/** | |
* Filter the oEmbed TTL value (time to live). | |
* | |
* @since 4.0.0 | |
* | |
* @param int $time Time to live (in seconds). | |
* @param string $url The attempted embed URL. | |
* @param array $attr An array of shortcode attributes. | |
* @param int $post_ID Post ID. | |
*/ | |
$ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID ); | |
$cache = get_post_meta( $post_ID, $cachekey, true ); | |
$cache_time = get_post_meta( $post_ID, $cachekey_time, true ); | |
if ( ! $cache_time ) { | |
$cache_time = 0; | |
} | |
$cached_recently = ( time() - $cache_time ) < $ttl; | |
if ( $this->usecache || $cached_recently ) { | |
// Failures are cached. Serve one if we're using the cache. | |
if ( '{{unknown}}' === $cache ) | |
return $this->maybe_make_link( $url ); | |
if ( ! empty( $cache ) ) { | |
/** | |
* Filter the cached oEmbed HTML. | |
* | |
* @since 2.9.0 | |
* | |
* @see WP_Embed::shortcode() | |
* | |
* @param mixed $cache The cached HTML result, stored in post meta. | |
* @param string $url The attempted embed URL. | |
* @param array $attr An array of shortcode attributes. | |
* @param int $post_ID Post ID. | |
*/ | |
return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID ); | |
} | |
} | |
/** | |
* Filter whether to inspect the given URL for discoverable link tags. | |
* | |
* @since 2.9.0 | |
* | |
* @see WP_oEmbed::discover() | |
* | |
* @param bool $enable Whether to enable `<link>` tag discovery. Default false. | |
*/ | |
$attr['discover'] = ( apply_filters( 'embed_oembed_discover', false ) && author_can( $post_ID, 'unfiltered_html' ) ); | |
// Use oEmbed to get the HTML | |
$html = wp_oembed_get( $url, $attr ); | |
// Maybe cache the result | |
if ( $html ) { | |
update_post_meta( $post_ID, $cachekey, $html ); | |
update_post_meta( $post_ID, $cachekey_time, time() ); | |
} elseif ( ! $cache ) { | |
update_post_meta( $post_ID, $cachekey, '{{unknown}}' ); | |
} | |
// If there was a result, return it | |
if ( $html ) { | |
/** This filter is documented in wp-includes/class-wp-embed.php */ | |
return apply_filters( 'embed_oembed_html', $html, $url, $attr, $post_ID ); | |
} | |
} | |
// Still unknown | |
return $this->maybe_make_link( $url ); | |
} | |
/** | |
* Delete all oEmbed caches. Unused by core as of 4.0.0. | |
* | |
* @param int $post_ID Post ID to delete the caches for. | |
*/ | |
public function delete_oembed_caches( $post_ID ) { | |
$post_metas = get_post_custom_keys( $post_ID ); | |
if ( empty($post_metas) ) | |
return; | |
foreach( $post_metas as $post_meta_key ) { | |
if ( '_oembed_' == substr( $post_meta_key, 0, 8 ) ) | |
delete_post_meta( $post_ID, $post_meta_key ); | |
} | |
} | |
/** | |
* Triggers a caching of all oEmbed results. | |
* | |
* @param int $post_ID Post ID to do the caching for. | |
*/ | |
public function cache_oembed( $post_ID ) { | |
$post = get_post( $post_ID ); | |
$post_types = get_post_types( array( 'show_ui' => true ) ); | |
/** | |
* Filter the array of post types to cache oEmbed results for. | |
* | |
* @since 2.9.0 | |
* | |
* @param array $post_types Array of post types to cache oEmbed results for. Defaults to post types with `show_ui` set to true. | |
*/ | |
if ( empty( $post->ID ) || ! in_array( $post->post_type, apply_filters( 'embed_cache_oembed_types', $post_types ) ) ){ | |
return; | |
} | |
// Trigger a caching | |
if ( ! empty( $post->post_content ) ) { | |
$this->post_ID = $post->ID; | |
$this->usecache = false; | |
$content = $this->run_shortcode( $post->post_content ); | |
$this->autoembed( $content ); | |
$this->usecache = true; | |
} | |
} | |
/** | |
* Passes any unlinked URLs that are on their own line to {@link WP_Embed::shortcode()} for potential embedding. | |
* | |
* @uses WP_Embed::autoembed_callback() | |
* | |
* @param string $content The content to be searched. | |
* @return string Potentially modified $content. | |
*/ | |
public function autoembed( $content ) { | |
return preg_replace_callback( '|^\s*(https?://[^\s"]+)\s*$|im', array( $this, 'autoembed_callback' ), $content ); | |
} | |
/** | |
* Callback function for {@link WP_Embed::autoembed()}. | |
* | |
* @param array $match A regex match array. | |
* @return string The embed HTML on success, otherwise the original URL. | |
*/ | |
public function autoembed_callback( $match ) { | |
$oldval = $this->linkifunknown; | |
$this->linkifunknown = false; | |
$return = $this->shortcode( array(), $match[1] ); | |
$this->linkifunknown = $oldval; | |
return "\n$return\n"; | |
} | |
/** | |
* Conditionally makes a hyperlink based on an internal class variable. | |
* | |
* @param string $url URL to potentially be linked. | |
* @return false|string Linked URL or the original URL. False if 'return_false_on_fail' is true. | |
*/ | |
public function maybe_make_link( $url ) { | |
if ( $this->return_false_on_fail ) { | |
return false; | |
} | |
$output = ( $this->linkifunknown ) ? '<a href="' . esc_url($url) . '">' . esc_html($url) . '</a>' : $url; | |
/** | |
* Filter the returned, maybe-linked embed URL. | |
* | |
* @since 2.9.0 | |
* | |
* @param string $output The linked or original URL. | |
* @param string $url The original URL. | |
*/ | |
return apply_filters( 'embed_maybe_make_link', $output, $url ); | |
} | |
} | |
$GLOBALS['wp_embed'] = new WP_Embed(); |
(Here 8
means, “Get it done before getting wpautop
eed on.”)
It took me an hour of workaround/hacking/discovery to understand this side-effect. Divide this by a lack-of-curiosity factor of normal people (10), but multiply this by the millions of WordPress installs, content creators, and developers out there and by every post that they may have hit this. You get many millions of hours lost. A average human lifespan is only around half a million hours. Without a doubt, wpautop
is killing people! (FYWP #259) Stop the madness!
This side-effect is particularly nasty for my fork because it sets the shortcode standard for the page—an unfortunate thing I ran into when I made a test page for it. To understand this, imagine including a bare link like so:
https://gist.github.com/padolsey/527683
This gets turned into the following shortcode via side effect:
[gist id=527683] |
Which now means that all my content-based gist shortcodes on the same page will now be improperly processed!
The fix is to not depend on the side-effect and call code directly. So instead of code returning a shortcode to generate a side-effect:
<?php | |
/** | |
* GistPress | |
* | |
* @package GistPress | |
* @author Brady Vercher <brady@blazersix.com> | |
* @author Gary Jones <gary@garyjones.co.uk> | |
* @copyright Copyright (c) 2012, Blazer Six, Inc. | |
* @license GPL-2.0+ | |
* | |
* @todo Cache the style sheet locally. #22 | |
*/ | |
/** | |
* The main plugin class. | |
* | |
* @package GistPress | |
* @author Brady Vercher <brady@blazersix.com> | |
* @author Gary Jones <gary@garyjones.co.uk> | |
*/ | |
class GistPress { | |
/** @var object Logger object. */ | |
protected $logger = null; | |
/** | |
* Toggle to short-circuit shortcode output and delete its corresponding | |
* transient so output can be regenerated the next time it is run. | |
* | |
* @var bool | |
*/ | |
protected $delete_shortcode_transients = false; | |
/** | |
* Sets a logger instance on the object. | |
* | |
* Since logging is optional, the dependency injection is done via this | |
* method, instead of being required through a constructor. | |
* | |
* Under PSR-1, this method would be called setLogger(). | |
* | |
* @see https://github.com/php-fig/log/blob/master/Psr/Log/LoggerAwareInterface.php | |
* | |
* @since 1.1.0 | |
* | |
* @param object $logger | |
*/ | |
public function set_logger( $logger ) { | |
$this->logger = $logger; | |
} | |
/** | |
* Return logger instance. | |
* | |
* Under PSR-1, this method would be called getLogger(). | |
* | |
* @since 1.1.0 | |
* | |
* @return object | |
*/ | |
public function get_logger() { | |
return $this->$logger; | |
} | |
/** | |
* Set up the plugin. | |
* | |
* Adds a [gist] shortcode to do the bulk of the heavy lifting. An embed | |
* handler is registered to mimic oEmbed functionality, but it relies on | |
* the shortcode for processing. | |
* | |
* Supported formats: | |
* | |
* * Old link: https://gist.github.com/{{id}}#file_{{filename}} | |
* * Old link with username: https://gist.github.com/{{user}}/{{id}}#file_{{filename}} | |
* * New bookmark: https://gist.github.com/{{id}}#file-{{file_slug}} | |
* * New bookmark with username: https://gist.github.com/{{user}}/{{id}}#file-{{sanitized-filename}} | |
* | |
* @since 1.1.0 | |
*/ | |
public function run() { | |
$oembed_pattern = '#https://gist\.github\.com/(?:.*/)?([a-z0-9]+)(?:\#file([_-])(.*))?#i'; | |
wp_embed_register_handler( 'gist', $oembed_pattern, array( $this, 'wp_embed_handler' ) ); | |
add_shortcode( 'gist', array( $this, 'shortcode' ) ); | |
add_action( 'init', array( $this, 'style' ), 15 ); | |
add_action( 'post_updated', array( $this, 'delete_gist_transients' ), 10, 3 ); | |
} | |
/** | |
* Register the Gist style sheet so it can be embedded once. | |
* | |
* @since 1.0.0 | |
*/ | |
public function style() { | |
wp_register_style( 'gistpress', get_option( 'gistpress_stylesheet' ) ); | |
} | |
/** | |
* WP embed handler to generate a shortcode string from a Gist URL. | |
* | |
* Parses Gist URLs for oEmbed support. Returns the value as a shortcode | |
* string to let the shortcode method handle processing. The value | |
* returned also doesn't have wpautop() applied, which is a must for | |
* source code. | |
* | |
* @since 1.0.0 | |
* | |
* @param array $matches Search results against the regex pattern listed in | |
* run(). | |
* @param array $attr Associative array of shortcode attributes, merged | |
* with embed handler default attributes. | |
* @param string $url The URL attempting to be embedded. | |
* @param array $rawattr Associative array of raw shortcode attributes. | |
* | |
* @return string Shortcode | |
*/ | |
public function wp_embed_handler( array $matches, array $attr, $url, array $rawattr ) { | |
$shortcode = '[gist'; | |
if ( isset( $matches[1] ) && ! empty( $matches[1] ) ) { | |
$shortcode .= ' id="' . esc_attr( $matches[1] ) . '"'; | |
} | |
// Make specific to a single file. | |
if ( isset( $matches[3] ) && ! empty( $matches[3] ) ) { | |
$real_file_name = $this->get_file_name( $matches[3], $matches[2], $matches[1] ); | |
if ( $real_file_name ) { | |
$shortcode .= ' file="' . esc_attr( $real_file_name ) . '"'; | |
} | |
} | |
// This attribute added so we can identify if a oembed URL or direct shortcode was used. | |
$shortcode .= ' oembed="1"]'; | |
return $shortcode; | |
} | |
/** | |
* Gist shortcode. | |
* | |
* Works with secret Gists, too. | |
* | |
* Shortcode attributes: | |
* | |
* - id - The Gist id (found in the URL). The only required attribute. | |
* - embed_stylesheet - Whether the external style sheet should be enqueued for output in the footer. | |
* * If the footer is too late, set to false and enqueue the 'github-gist' style before 'wp_head'. | |
* * Any custom styles should be added to the theme's style sheet. | |
* - file - Name of a specific file in a Gist. | |
* - highlight - Comma-separated list of line numbers to highlight. | |
* * Ranges can be specified. Ex: 2,4,6-10,12 | |
* - highlight_color - Background color of highlighted lines. | |
* * To change it globally, hook into the filter and supply a different color. | |
* - lines - A range of lines to limit the Gist to. | |
* * Suited for single file Gists or shortcodes using the 'file' attribute. | |
* - lines_start - A number to start counting from for line numbers. | |
* - show_line_numbers - Whether line numbers should be displayed. | |
* - show_meta - Whether the trailing meta information in default Gist embeds should be displayed. | |
* | |
* @since 1.0.0 | |
* | |
* @uses GistPress::rebuild_shortcode() Rebuild shortcode string. | |
* @uses GistPress::standardize_attributes() Set defaults and sanitize. | |
* @uses GistPress::shortcode_hash() Get hash of attributes. | |
* @uses GistPress::transient_key() Transient key name. | |
* @uses GistPress::debug_log() Potentially log a debug message. | |
* @uses GistPress::debug_log() Gist retrieval failure string. | |
* | |
* @param array $rawattr Raw attributes of the shortcode. | |
* | |
* @return string HTML content to display the Gist. | |
*/ | |
public function shortcode( array $rawattr ) { | |
$shortcode = $this->rebuild_shortcode( $rawattr ); | |
$attr = $this->standardize_attributes( $rawattr ); | |
$shortcode_hash = $this->shortcode_hash( 'gist', $attr ); | |
// Short-circuit the shortcode output and just delete the transient. | |
// This is set to true when posts are updated. | |
if ( $this->delete_shortcode_transients ) { | |
delete_transient( $this->transient_key( $shortcode_hash ) ); | |
delete_transient( $this->gist_files_transient_key( $attr['id'] ) ); | |
return; | |
} | |
// Log what we're dealing with - title uses original attributes, but hashed against processed attributes. | |
$this->debug_log( '<h2>' . $shortcode . '</h2>', $shortcode_hash ); | |
// Bail if the ID is not set. | |
if ( empty( $attr['id'] ) ) { | |
$this->debug_log( __( 'Shortcode did not have a required id attribute.', 'gistpress' ), $shortcode_hash ); | |
return ''; | |
} | |
$url = 'https://gist.github.com/' . $attr['id']; | |
$json_url = $url . '.json'; | |
if ( is_feed() ) { | |
$html = sprintf( '<a href="%s" target="_blank"><em>%s</em></a>', esc_url( $url ), __( 'View this code snippet on GitHub.', 'gistpress' ) ); | |
/** | |
* Filter what is shown in feeds. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $html Markup to show in feeds. | |
*/ | |
return apply_filters( 'gistpress_feed_html', $html ); | |
} | |
$html = $this->get_gist_html( $json_url, $attr ); | |
if ( $this->unknown() === $html ) { | |
return make_clickable( $url ); | |
} | |
// If there was a result, return it. | |
if ( $html ) { | |
if ( $attr['embed_stylesheet'] ) { | |
wp_enqueue_style( 'gistpress' ); | |
} | |
/** | |
* Filter the output HTML. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $html The output HTML. | |
* @param string $url The URL to the Gist. | |
* @param array $attr Shortcode attributes, standardized. | |
* @param int $id Post ID. | |
*/ | |
$html = apply_filters( 'gistpress_html', $html, $url, $attr, get_the_ID() ); | |
foreach ( $attr as $key => $value ) { | |
$message = '<strong>' . $key . __(' (shortcode attribute)', 'gistpress') . ':</strong> '; | |
$message .= is_scalar( $value ) ? $value : print_r( $value, true ); | |
$this->debug_log( $message, $shortcode_hash ); | |
} | |
$this->debug_log( '<strong>Gist:</strong><br />' . $html, $shortcode_hash ); | |
return $html; | |
} | |
return ''; | |
} | |
/** | |
* Helper method to determine if a shortcode attribute is true or false. | |
* | |
* @since 1.1.0 | |
* | |
* @param string|int|bool $var Attribute value. | |
* | |
* @return bool | |
*/ | |
public function shortcode_bool( $var ) { | |
$falsey = array( 'false', '0', 'no', 'n' ); | |
return ( ! $var || in_array( strtolower( $var ), $falsey ) ) ? false : true; | |
} | |
/** | |
* Parses and expands the shortcode 'highlight' attribute and returns it | |
* in a usable format. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $line_numbers Comma-separated list of line numbers and ranges. | |
* | |
* @return array|null List of line numbers, or null if no line numbers given | |
*/ | |
public function parse_highlight_arg( $line_numbers ) { | |
if ( empty( $line_numbers ) ) { | |
return null; | |
} | |
// Determine which lines should be highlighted. | |
$highlight = array_map( 'trim', explode( ',', $line_numbers ) ); | |
// Convert any ranges. | |
foreach ( $highlight as $index => $num ) { | |
if ( false !== strpos( $num, '-' ) ) { | |
unset( $highlight[ $index ] ); | |
$range = array_map( 'trim', explode( '-', $num ) ); | |
foreach ( range( $range[0], $range[1] ) as $line ) { | |
array_push( $highlight, $line ); | |
} | |
} | |
} | |
return array_unique( $highlight ); | |
} | |
/** | |
* Parses the shortcode 'lines' attribute into min and max values. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $line_numbers Range of line numbers separated by a dash. | |
* | |
* @return array Associative array with min and max line numbers. | |
*/ | |
public function parse_line_number_arg( $line_numbers ) { | |
if ( empty( $line_numbers ) ) { | |
return array( 'min' => 0, 'max' => 0, ); | |
} | |
if ( false === strpos( $line_numbers, '-' ) ) { | |
$range = array_fill_keys( array( 'min', 'max', ), absint( trim( $line_numbers ) ) ); | |
} else { | |
$numbers = array_map( 'absint', array_map( 'trim', explode( '-', $line_numbers ) ) ); | |
$range = array( | |
'min' => $numbers[0], | |
'max' => $numbers[1], | |
); | |
} | |
return $range; | |
} | |
/** | |
* Retrieve Gist HTML. | |
* | |
* Gist HTML can come from one of three different sources: | |
* Remote JSON endpoint, | |
* Transient, | |
* Post meta cache. | |
* | |
* When a Gist is intially requested, the HTML is fetched from the JSON | |
* endpoint and cached in a post meta field. It is then processed to limit | |
* line numbers, highlight specific lines, and add a few extra classes as | |
* style hooks. The processed HTML is then stored in a transient using a | |
* hash of the shortcodes attributes for the key. | |
* | |
* On subsequent requests, the HTML is fetched from the transient until it | |
* expires, then it is requested from the remote URL again. | |
* | |
* In the event the HTML can't be fetched from the remote endpoint and the | |
* transient has expired, the HTML is retrieved from the post meta backup. | |
* | |
* This algorithm allows Gist HTML to stay in sync with any changes GitHub | |
* may make to their markup, while providing a local cache for faster | |
* retrieval and a backup in case GitHub can't be reached. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $url The JSON endpoint for the Gist. | |
* @param array $args List of shortcode attributes. | |
* | |
* @return string Gist HTML or {{unknown}} if it couldn't be determined. | |
*/ | |
public function get_gist_html( $url, array $args ) { | |
// Add a specific file from a Gist to the URL. | |
if ( ! empty( $args['file'] ) ) { | |
$url = add_query_arg( 'file', urlencode( $args['file'] ), $url ); | |
} | |
$shortcode_hash = $this->shortcode_hash( 'gist', $args ); | |
$raw_key = '_gist_raw_' . md5( $url ); | |
$transient_key = $this->transient_key( $shortcode_hash ); | |
$html = get_transient( $transient_key ); | |
if ( empty( $html ) ) { | |
$html = get_transient( $raw_key ); | |
$transient_expire = DAY_IN_SECONDS; | |
if ( $html && $this->unknown() != $html ) { | |
$html = $this->process_gist_html( $html, $args ); | |
$this->debug_log( __( '<strong>Raw Source:</strong> Transient Cache', 'gistpress' ), $shortcode_hash ); | |
} else { | |
// Retrieve raw html from Gist JSON endpoint. | |
$json = $this->fetch_gist( $url ); | |
if ( ! empty( $json->div ) ) { | |
set_transient( $raw_key, $json->div, $transient_expire ); | |
// Update the post meta fallback. | |
// @link http://core.trac.wordpress.org/ticket/21767 | |
update_post_meta( get_the_ID(), $raw_key, addslashes( $json->div ) ); | |
$html = $this->process_gist_html( $json->div, $args ); | |
$this->debug_log( __( '<strong>Raw Source:</strong> Remote JSON Endpoint - ', 'gistpress' ) . $url, $shortcode_hash ); | |
$this->debug_log( __( '<strong>Output Source:</strong> Processed the raw source.', 'gistpress' ), $shortcode_hash ); | |
} | |
// Update the style sheet reference. | |
if ( ! empty( $json->stylesheet ) ) { | |
update_option( 'gistpress_stylesheet', $json->stylesheet ); | |
} | |
} | |
// Failures are cached, too. Update the post to attempt to fetch again. | |
$html = ( $html ) ? $html : $this->unknown(); | |
if ( $this->unknown() == $html && ( $fallback = get_post_meta( get_the_ID(), $raw_key, true ) ) ) { | |
// Return the fallback instead of the string representing unknown. | |
$html = $this->process_gist_html( $fallback, $args ); | |
// Cache the fallback for an hour. | |
$transient_expire = HOUR_IN_SECONDS; | |
$this->debug_log( __( '<strong>Raw Source:</strong> Post Meta Fallback', 'gistpress' ), $shortcode_hash ); | |
$this->debug_log( __( '<strong>Output Source:</strong> Processed Raw Source', 'gistpress' ), $shortcode_hash ); | |
} elseif ( $this->unknown() == $html ) { | |
$this->debug_log( '<strong style="color: #e00">' . __( 'Remote call and transient failed and fallback was empty.', 'gistpress' ) . '</strong>', $shortcode_hash ); | |
} | |
// Cache the processed HTML. | |
set_transient( $transient_key, $html, $transient_expire ); | |
} else { | |
$this->debug_log( __( '<strong>Output Source:</strong> Transient Cache', 'gistpress' ), $shortcode_hash ); | |
} | |
$this->debug_log( '<strong>' . __( 'JSON Endpoint:', 'gistpress' ) . '</strong> ' . $url, $shortcode_hash ); | |
$this->debug_log( '<strong>' . __( 'Raw Key (Transient & Post Meta):', 'gistpress' ) . '</strong> ' . $raw_key, $shortcode_hash ); | |
$this->debug_log( '<strong>' . __( 'Processed Output Key (Transient):', 'gistpress' ) . '</strong> ' . $transient_key, $shortcode_hash ); | |
return $html; | |
} | |
/** | |
* Fetch Gist data from its JSON endpoint. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $url Gist JSON endpoint. | |
* | |
* @return object|bool Gist JSON object, or false if anything except a HTTP | |
* Status code of 200 was received. | |
*/ | |
public function fetch_gist( $url ) { | |
$response = wp_remote_get( $url, array( 'sslverify' => false ) ); | |
if ( '200' == wp_remote_retrieve_response_code( $response ) ) { | |
return json_decode( wp_remote_retrieve_body( $response ) ); | |
} | |
return false; | |
} | |
/** | |
* Process the HTML returned from a Gist's JSON endpoint based on settings | |
* passed through the shortcode. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $html HTML from the Gist's JSON endpoint. | |
* @param array $args List of shortcode attributes. | |
* | |
* @return string Modified HTML. | |
*/ | |
public function process_gist_html( $html, array $args ) { | |
// Remove the line number cell if it has been disabled. | |
if ( ! $args['show_line_numbers'] ) { | |
$html = preg_replace( '#<td class="line-numbers">.*?</td>#s', '', $html ); | |
} | |
// Remove the meta section if it has been disabled. | |
if ( ! $args['show_meta'] ) { | |
$html = preg_replace( '#<div class="gist-meta">.*?</div>#s', '', $html ); | |
} | |
$lines_pattern = '#(<pre class="line-pre"[^>]*>)(.+?)</pre>#s'; | |
preg_match( $lines_pattern, $html, $lines_matches ); | |
if ( ! empty( $lines_matches[2] ) ) { | |
// Restrict the line number display if a range has been specified. | |
if ( $args['show_line_numbers'] && ( ( $args['lines']['min'] && $args['lines']['max'] ) || ! empty( $args['lines_start'] ) ) ) { | |
$html = $this->process_gist_line_numbers( $html, $args['lines'], $args['lines_start'] ); | |
} | |
if ( ! empty( $args['highlight'] ) ) { | |
// Flip to use isset() when looping through the lines. | |
$highlight = array_flip( $args['highlight'] ); | |
} | |
// Extract and cleanup the individual lines from the Gist HTML into an array for processing. | |
$lines = preg_replace( '#<div[^>]+>#', '', trim( $lines_matches[2] ), 1 ); | |
$lines = preg_split( '#</div>[\s]*<div[^>]+>#', substr( $lines, 0, strlen( $lines ) - 6 ) ); | |
foreach ( $lines as $key => $line ) { | |
// Remove lines if they're not in the specified range and continue. | |
if ( ( $args['lines']['min'] && $key < $args['lines']['min'] - 1 ) || ( $args['lines']['max'] && $key > $args['lines']['max'] - 1 ) ) { | |
unset( $lines[ $key ] ); | |
continue; | |
} | |
// Add classes for styling. | |
$classes = array( 'line' ); | |
//$classes[] = ( $key % 2 ) ? 'line-odd' : 'line-even'; | |
$style = ''; | |
if ( isset( $highlight[ $key + 1 ] ) ) { | |
$classes[] = 'line-highlight'; | |
if ( ! empty( $args['highlight_color'] ) ) { | |
$style = ' style="background-color: ' . $args['highlight_color'] . ' !important"'; | |
} | |
} | |
/** | |
* Filter the classes applied to a line of the Gist. | |
* | |
* @since 2.0.0 | |
* | |
* @param array $classes List of HTML class values. | |
*/ | |
$classes = apply_filters( 'gistpress_line_classes', $classes ); | |
$class = ( ! empty( $classes ) && is_array( $classes ) ) ? ' class="' . implode ( ' ', $classes ) . '"' : ''; | |
$lines[ $key ] = '<div' . $class . $style . '>' . $line . '</div>'; | |
} | |
$replacement = $lines_matches[1] . join( '', $lines ) . '</pre>'; | |
$replacement = $this->preg_replace_quote( $replacement ); | |
$html = preg_replace( $lines_pattern, $replacement, $html, 1 ); | |
} | |
return $html; | |
} | |
/** | |
* Removes line numbers from the Gist's HTML that fall outside the | |
* supplied range and modifies the starting number if specified. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $html HTML from the Gist's JSON endpoint. | |
* @param array $range Array of min and max values. | |
* @param int $start Optional. Line number to start counting at. | |
* | |
* @return string Modified HTML. | |
*/ | |
public function process_gist_line_numbers( $html, array $range, $start = null ) { | |
$line_num_pattern = '#(<td class="line-numbers">)(.*?)</td>#s'; | |
preg_match( $line_num_pattern, $html, $line_num_matches ); | |
if ( ! empty( $line_num_matches[2] ) ) { | |
$start = absint( $start ); | |
$lines = array_map( 'trim', explode( "\n", trim( $line_num_matches[2] ) ) ); | |
if( ! $start && $range['min'] && $range['max'] ) { | |
$line_numbers = array_slice( $lines, $range['min'] - 1, $range['max'] - $range['min'] + 1 ); | |
} else { | |
// Determine how many lines should be shown. | |
$range_length = count( $lines ); | |
if ( $range['min'] && $range['max'] ) { | |
$range_length = $range['max'] - $range['min'] + 1; | |
$start = ( $start ) ? $start : $range['min']; | |
} | |
// Create a template with a placeholder for the line number. | |
preg_match( '#<span rel="([^"]+)[0-9]+?"#', $lines[0], $attr_matches ); | |
if ( ! empty( $attr_matches[1] ) ) { | |
$template = sprintf( '<span rel="%1$s%2$s" id="%1$s%2$s">%2$s</span>', esc_attr( $attr_matches[1] ), '{{num}}' ); | |
} else { | |
$template = '<span>{{num}}</span>'; | |
} | |
// Generate HTML for the line numbers. | |
$line_numbers = array(); | |
for ( $i = $start; $i <= $start + $range_length - 1; $i ++ ) { | |
$line_numbers[] = str_replace( '{{num}}', $i, $template ); | |
} | |
} | |
$replacement = $line_num_matches[1] . join( "\n", $line_numbers ) . '</td>'; | |
$html = preg_replace( $line_num_pattern, $replacement, $html, 1 ); | |
} | |
return $html; | |
} | |
/** | |
* Removes transients associated with Gists embedded in a post. | |
* | |
* Retrieves the keys of meta data associated with a post and deletes any | |
* transients with a matching embed key. | |
* | |
* @since 1.1.0 | |
* | |
* @param int $post_id Post ID. | |
* @param WP_Post $post_after Post object after update. | |
* @param WP_Post $post_before Post object before update. | |
*/ | |
public function delete_gist_transients( $post_id, WP_Post $post_after, WP_Post $post_before ) { | |
$this->delete_shortcode_transients = true; | |
// Run the shortcodes to clear associated transients. | |
do_shortcode( $GLOBALS['wp_embed']->autoembed( $post_after->post_content ) ); | |
do_shortcode( $GLOBALS['wp_embed']->autoembed( $post_before->post_content ) ); | |
// Delete raw transients whose keys match a post meta fallback. | |
$keys = get_post_custom_keys( $post_id ); | |
if ( $keys ) { | |
foreach( $keys as $key ) { | |
if ( 0 === strpos( $key, '_gist_raw_' ) ) { | |
delete_transient( $key ); | |
} | |
} | |
} | |
} | |
/** | |
* Rebuild the original shortcode as a string with raw attributes. | |
* | |
* @since 1.1.1 | |
* | |
* @param array $rawattr Raw attributes => values. | |
* | |
* @return string Gist shortcode. | |
*/ | |
protected function rebuild_shortcode( array $rawattr ) { | |
$attrs = array(); | |
foreach ( $rawattr as $key => $value ) { | |
if ( 'oembed' != $key ) { | |
$attrs[] = $key . '="' . $value . '"'; | |
} | |
} | |
return '[gist ' . implode( ' ', $attrs ) . ']'; | |
} | |
/** | |
* Set defaults and sanitize shortcode attributes and attribute values. | |
* | |
* @since 1.1.1 | |
* | |
* @param array $rawattr Associative array of raw attributes => values. | |
* | |
* @return array Standardized and sanitized shortcode attributes. | |
*/ | |
protected function standardize_attributes( array $rawattr ) { | |
/** | |
* Filter the shortcode attributes defaults. | |
* | |
* @since 2.0.0 | |
* | |
* @see standardize_attributes() | |
* | |
* @param array $gistpress_shortcode_defaults { | |
* Shortcode attributes defaults. | |
* | |
* @type bool $embed_stylesheet Filterable value to include style sheet or not. Default is true | |
* to include it. | |
* @type string $file File name within gist. Default is an empty string, indicating | |
* all files. | |
* @type array $highlight Lines to highlight. Default is empty array, to highlight | |
* no lines. | |
* @type string $highlight_color Filterable hex color code. Default is #ffc. | |
* @type string $id Gist ID. Non-optional. | |
* @type string $lines Number of lines to show. Default is empty string, indicating | |
* all lines in the gist. | |
* @type string $lines_start Which line number to start from. Default is empty string, | |
* indicating line number 1. | |
* @type bool $show_line_numbers Show line numbers or not, default is true, to show line numbers. | |
* @type bool $show_meta Show meta information or not, default is true, to show | |
* meta information. | |
* } | |
*/ | |
$defaults = apply_filters( | |
'gistpress_shortcode_defaults', | |
array( | |
/** | |
* Filter to include the style sheet or not. | |
* | |
* @since 2.0.0 | |
* | |
* @param bool $gistpress_stylesheet_default Include default style sheet or not. | |
* Default is true, to include it. | |
*/ | |
'embed_stylesheet' => apply_filters( 'gistpress_stylesheet_default', true ), | |
'file' => '', | |
'highlight' => array(), | |
/** | |
* Filter highlight color. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $gistpress_highlight_color Hex color code for highlighting lines. | |
* Default is `#ffc`. | |
*/ | |
'highlight_color' => apply_filters( 'gistpress_highlight_color', '#ffc' ), | |
'id' => '', | |
'lines' => '', | |
'lines_start' => '', | |
'show_line_numbers' => true, | |
'show_meta' => true, | |
'oembed' => 0, // Private use only | |
) | |
); | |
// Sanitize attributes. | |
$attr = shortcode_atts( $defaults, $rawattr ); | |
$attr['embed_stylesheet'] = $this->shortcode_bool( $attr['embed_stylesheet'] ); | |
$attr['show_line_numbers'] = $this->shortcode_bool( $attr['show_line_numbers'] ); | |
$attr['show_meta'] = $this->shortcode_bool( $attr['show_meta'] ); | |
$attr['highlight'] = $this->parse_highlight_arg( $attr['highlight'] ); | |
$attr['lines'] = $this->parse_line_number_arg( $attr['lines'] ); | |
return $attr; | |
} | |
/** | |
* Try to determine the real file name from a sanitized file name. | |
* | |
* The new Gist "bookmark" URLs point to sanitized file names so that both | |
* hyphen and period in a file name show up as a hyphen e.g. a filename of | |
* foo.bar and foo-bar both appear in the bookmark URL as foo-bar. The | |
* correct original filenames are listed in the JSON data for the overall | |
* Gist, so this method does a call to that, and loops through the listed | |
* file names to see if it can determine which file was meant. | |
* | |
* If a Gist has two files that both resolve to the same sanitized filename, | |
* then we don't have any way to determine which one the other determined, | |
* so we just return the first one we find. If that's incorrect, the author | |
* can use the shortcode approach, which allows a specific file name to be | |
* used. | |
* | |
* @since 2.1.0 | |
* | |
* @param string $sanitized_filename Sanitized filename, such as foo-bar-php. | |
* @param string $delimiter Either underscore or hyphen. | |
* @param string $id Gist ID. | |
* | |
* @return string Filename, or empty string if it couldn't be determined. | |
*/ | |
protected function get_file_name( $sanitized_filename, $delimiter, $id ) { | |
// Old style link - filename wasn't actually changed. | |
if ( '_' === $delimiter ) { | |
return $sanitized_filename; | |
} | |
// New style bookmark - filename had . replaced with - | |
// Means we have to go and look up what the filename could have been. | |
$transient_key = $this->gist_files_transient_key( $id ); | |
$gist_files = get_transient( $transient_key ); | |
if ( ! $gist_files ) { | |
$url = 'https://gist.github.com/' . $id . '.json'; | |
$json = $this->fetch_gist( $url ); | |
if ( $json && ! empty( $json->files ) ) { | |
$gist_files = $json->files; | |
set_transient( $transient_key, $gist_files, WEEK_IN_SECONDS ); | |
} else { | |
set_transient( $transient_key, array(), MINUTE_IN_SECONDS * 15 ); | |
} | |
} | |
// If a gist has foo.bar.php and foo-bar.php, then we can't yet | |
// determine which was actually wanted, since both give the same | |
// bookmark URL. Here, we just return the first one we find. | |
if ( ! empty( $gist_files ) ) { | |
foreach ( $gist_files as $file ) { | |
if ( str_replace( '.', '-', $file ) === $sanitized_filename ) { | |
return $file; | |
} | |
} | |
} | |
return ''; | |
} | |
/** | |
* Wrapper for a PSR-3 compatible logger. | |
* | |
* If no logger has been set via the set_logger() method on an instance of | |
* this class, or WP_DEBUG is not enabled, then log messages quietly die | |
* here. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $message A message to log for the current shortcode. | |
* @param mixed $id Optional. An ID under which the message should be grouped. | |
* | |
* @todo Handle missing $id any better? | |
*/ | |
protected function debug_log( $message, $id = null ) { | |
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && isset( $this->logger ) ) { | |
$this->logger->debug( $message, array( 'key' => $id ) ); | |
} | |
} | |
/** | |
* Sort a shortcode's attributes by name and hash it for use as a cache | |
* key and logger message grouping. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $tag Shortcode tag, used as hash prefix. | |
* @param array $args Associative array of shortcode attributes. | |
* | |
* @return string md5 hash as a 32-character hexadecimal number. | |
*/ | |
protected function shortcode_hash( $tag, array $args ) { | |
ksort( $args ); | |
return md5( $tag . '_' . serialize( $args ) ); | |
} | |
/** | |
* Get the transient key. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $identifier The identifier part of the key. | |
* | |
* @return string Transient key name. | |
*/ | |
protected function transient_key( $identifier ) { | |
return 'gist_html_' . $identifier; | |
} | |
/** | |
* Get the transient key for a list of a Gist's files. | |
* | |
* @since 2.1.0 | |
* | |
* @param string $id The Gist id. | |
* | |
* @return string Transient key name. | |
*/ | |
protected function gist_files_transient_key( $gist_id ) { | |
return 'gist_files_' . md5( $gist_id ); | |
} | |
/** | |
* String to identify a failure when retrieving a Gist's HTML. | |
* | |
* @since 1.1.0 | |
* | |
* @return string | |
*/ | |
protected function unknown() { | |
return '{{unknown}}'; | |
} | |
/** | |
* Escape a regular expression replacement string. | |
* | |
* @since 2.0.2 | |
* @link http://www.procata.com/blog/archives/2005/11/13/two-preg_replace-escaping-gotchas/ | |
* | |
* @param string $str String to escape. | |
* @return string | |
*/ | |
public function preg_replace_quote( $str ) { | |
return preg_replace( '/(\$|\\\\)(?=\d)/', '\\\\$1', $str ); | |
} | |
} |
we can simply call the shortcode code directly:
<?php | |
/** | |
* GistPress | |
* | |
* @package GistPress | |
* @author Brady Vercher <brady@blazersix.com> | |
* @author Gary Jones <gary@garyjones.co.uk> | |
* @author terry chay <tychay@php.net> | |
* @copyright Copyright (c) 2012, Blazer Six, Inc. (c) 2015, terry chay | |
* @license GPL-2.0+ | |
* | |
* @todo Cache the style sheet locally. #22 <https://github.com/bradyvercher/gistpress/issues/22> | |
*/ | |
/** | |
* The main plugin class. | |
* | |
* @package GistPress | |
* @author Brady Vercher <brady@blazersix.com> | |
* @author Gary Jones <gary@garyjones.co.uk> | |
*/ | |
class GistPress { | |
/** @var object Logger object. */ | |
protected $logger = null; | |
/** @const OEMBED_REGEX regular extpression to extract from URL */ | |
// Username may only contain alphanumeric characters or single hyphens, and cannot begin or end with a hyphen | |
const OEMBED_REGEX = '#https://gist\.github\.com/(?:[0-9a-z\-]*/)?([0-9a-f]+)(?:/([0-9a-f]{40}))?(?:\#file([_-])(.*))?#i'; | |
/** | |
* Toggle to short-circuit shortcode output and delete its corresponding | |
* transient so output can be regenerated the next time it is run. | |
* | |
* @var bool | |
*/ | |
protected $delete_shortcode_transients = false; | |
/** | |
* Sets a logger instance on the object. | |
* | |
* Since logging is optional, the dependency injection is done via this | |
* method, instead of being required through a constructor. | |
* | |
* Under PSR-1, this method would be called setLogger(). | |
* | |
* @see https://github.com/php-fig/log/blob/master/Psr/Log/LoggerAwareInterface.php | |
* | |
* @since 1.1.0 | |
* | |
* @param object $logger | |
*/ | |
public function set_logger( $logger ) { | |
$this->logger = $logger; | |
} | |
/** | |
* Return logger instance. | |
* | |
* Under PSR-1, this method would be called getLogger(). | |
* | |
* @since 1.1.0 | |
* | |
* @return object | |
*/ | |
public function get_logger() { | |
return $this->$logger; | |
} | |
/** | |
* Set up the plugin. | |
* | |
* Adds a [gist] shortcode to do the bulk of the heavy lifting. An embed | |
* handler is registered to mimic oEmbed functionality, but it relies on | |
* the shortcode for processing. | |
* | |
* In the original code, this runs on init, but it should run on | |
* plugins_loaded which occurs before init. In addition, this needs to | |
* beat other shortcode plugins to work. | |
* | |
* Supported formats: | |
* | |
* * Old link: https://gist.github.com/{{id}}#file_{{filename}} | |
* * Old link with username: https://gist.github.com/{{user}}/{{id}}#file_{{filename}} | |
* * New bookmark: https://gist.github.com/{{id}}#file-{{file_slug}} | |
* * New bookmark with username: https://gist.github.com/{{user}}/{{id}}#file-{{sanitized-filename}} | |
* * new bookmark with username + revisionid: https://gist.github.com/{{user}}/{{id}}//{{revisiontag}}#file-{{sanitized-filename}} | |
* | |
* @since 1.1.0 | |
*/ | |
public function run() { | |
add_action( 'init', array($this, 'register_handlers'), 15); //TODO make position configurable | |
// TODO: make option | |
// Let's be sure to get JetPack specifically | |
add_filter( 'jetpack_shortcodes_to_include', array( $this, 'filter_remove_from_jetpack' ) ); | |
add_action( 'init', array( $this, 'style' ), 15 ); | |
add_action( 'post_updated', array( $this, 'delete_gist_transients' ), 10, 3 ); | |
} | |
/** | |
* Register the oEmbed and shortcode handler. | |
* | |
* This is in a separate method in order to be enqueed later by default. | |
* In this way, it will manipulate itself to override any existing gist | |
* handler. If it is set later, the existing gist handler will override | |
* this one. If it is set earlier, then it will be overridden. | |
*/ | |
public function register_handlers() { | |
// Unregister handlers before registering (in case if run last / default) | |
wp_embed_unregister_handler( 'gist' ); //the assumption is the priority is default (e.g. Jetpack) | |
///TODO: make option to disable these unregisters | |
wp_embed_unregister_handler( 'github-gist' ); // jetpack's oembed (backup) | |
wp_embed_unregister_handler( 'oe-gist' ); //oembed-gist | |
wp_embed_register_handler( 'gistpress', self::OEMBED_REGEX, array( $this, 'wp_embed_handler' ) ); | |
// the way add_shortcode() works, the last shortcode is the one that | |
// runs due to overwriting others | |
add_shortcode( 'gist', array( $this, 'shortcode' ) ); | |
} | |
/** | |
* Unregister gist from Jetpack | |
* | |
* {@see http://jetpack.me/tag/shortcodes/} (though it is not quite correct | |
* in the unlikely event that the first shortcode is gist) | |
* | |
* @param array $shortcodes the current list of php files to include | |
*/ | |
public function filter_remove_from_jetpack( $shortcodes ) { | |
$jetpack_shortcodes_dir = WP_CONTENT_DIR . '/plugins/jetpack/modules/shortcodes/'; | |
$key = array_search( $jetpack_shortcodes_dir . 'gist.php', $shortcodes); | |
if ( $key !== false ) { | |
unset($shortcodes[$key]); | |
} | |
return $shortcodes; | |
} | |
/** | |
* Register the Gist style sheet so it can be embedded once. | |
* | |
* @since 1.0.0 | |
*/ | |
public function style() { | |
wp_register_style( 'gistpress', get_option( 'gistpress_stylesheet' ) ); | |
} | |
/** | |
* WP embed handler to generate a shortcode string from a Gist URL. | |
* | |
* Parses Gist URLs for oEmbed support. Returns the value as a shortcode | |
* string to let the shortcode method handle processing. The value | |
* returned also doesn't have wpautop() applied, which is a must for | |
* source code. | |
* | |
* @since 1.0.0 | |
* | |
* @param array $matches Search results against the regex pattern listed in | |
* run(). | |
* @param array $attr Associative array of shortcode attributes, merged | |
* with embed handler default attributes. | |
* @param string $url The URL attempting to be embedded. | |
* @param array $rawattr Associative array of raw shortcode attributes. | |
* | |
* @return string Shortcode | |
*/ | |
public function wp_embed_handler( array $matches, array $attr, $url, array $rawattr ) { | |
// refactoring the url matching | |
$raw_attr = $this->_extract_url_matches( $matches ); | |
// Previously used reserved attribute "oembed" to identify that it | |
// is an oembed URL, this is no longer needed because call directly. | |
//$raw_attr['oembed'] = 1; | |
unset($raw_attr['url']); | |
if ( empty($raw_attr['file']) ) { unset($raw_attr['file']); } | |
// Injecting a shortcode for later handling is bad mojo for if we want | |
// to support dual self closing and content based shortcodes (only one | |
// or other per document). Therefore we should process it directly. | |
return $this->shortcode($raw_attr, '', $matches[0]); | |
} | |
/** | |
* Gist shortcode. | |
* | |
* Works with secret Gists, too. | |
* | |
* Shortcode attributes: | |
* | |
* - id - The Gist id (found in the URL). The only required attribute. | |
* - embed_stylesheet - Whether the external style sheet should be enqueued for output in the footer. | |
* * If the footer is too late, set to false and enqueue the 'github-gist' style before 'wp_head'. | |
* * Any custom styles should be added to the theme's style sheet. | |
* - file - Name of a specific file in a Gist. | |
* - highlight - Comma-separated list of line numbers to highlight. | |
* * Ranges can be specified. Ex: 2,4,6-10,12 | |
* - highlight_color - Background color of highlighted lines. | |
* * To change it globally, hook into the filter and supply a different color. | |
* - lines - A range of lines to limit the Gist to. | |
* * Suited for single file Gists or shortcodes using the 'file' attribute. | |
* - lines_start - A number to start counting from for line numbers. | |
* - show_line_numbers - Whether line numbers should be displayed. | |
* - show_meta - Whether the trailing meta information in default Gist embeds should be displayed. | |
* | |
* @since 1.0.0 | |
* | |
* @uses GistPress::rebuild_shortcode() Rebuild shortcode string. | |
* @uses GistPress::standardize_attributes() Set defaults and sanitize. | |
* @uses GistPress::shortcode_hash() Get hash of attributes. | |
* @uses GistPress::transient_key() Transient key name. | |
* @uses GistPress::debug_log() Potentially log a debug message. | |
* @uses GistPress::debug_log() Gist retrieval failure string. | |
* | |
* @param mixed $rawattr Raw attributes of the shortcode (usually array) | |
* @param string $content enclosing | |
* @param string $oembed_content should be url if coming from oembed, if | |
* shortcode, then it is empty | |
* | |
* @return string HTML content to display the Gist. If gist is invalid | |
* return a compatible version of shortcode with the error condition | |
* in the comment field. | |
*/ | |
public function shortcode( $rawattr , $content='', $oembed_content='') { | |
// used for rendering in error reporting | |
$shortcode = $this->rebuild_shortcode( $rawattr, $content ); | |
// handle jetpack style embed content | |
if ( $this->_attrs_in_content( $rawattr, $content ) ) { | |
// clear content string if we've extracted stuff in it | |
$content = ''; | |
} | |
if ( empty($rawattr) ) { $rawattr = array(); } //prevent fatals | |
$attr = $this->standardize_attributes( $rawattr ); | |
$shortcode_hash = $this->shortcode_hash( 'gist', $attr ); | |
// Short-circuit the shortcode output and just delete the transient. | |
// This is set to true when posts are updated. | |
if ( $this->delete_shortcode_transients ) { | |
delete_transient( $this->transient_key( $shortcode_hash ) ); | |
delete_transient( $this->gist_files_transient_key( $attr['id'] ) ); | |
return; | |
} | |
// Log what we're dealing with - title uses original attributes, but hashed against processed attributes. | |
if ( !empty($oembed_content) ) { | |
$this->debug_log( '<h2>'. $oembed_content . ' → ' . $shortcode . '</h2>', $shortcode_hash ); | |
} else { | |
$this->debug_log( '<h2>' . $shortcode . '</h2>', $shortcode_hash ); | |
} | |
// see if ID is in url parameter before bailing | |
if ( empty( $attr['id'] ) && !empty( $attr['url'] ) ) { | |
$this->_attrs_in_content( $attr, $attr['url'] ); | |
unset($attr['url']); | |
} | |
// Bail if the ID is not set. | |
if ( empty( $attr['id'] ) ) { | |
$this->debug_log( __( 'Shortcode did not have a required id attribute.', 'gistpress' ), $shortcode_hash ); | |
return '<!-- GistPress: Missing ID attribute -->' . $shortcode . '<!-- /GistPress -->'; | |
} | |
if ( is_feed() ) { | |
$html = sprintf( '<a href="%s" target="_blank"><em>%s</em></a>', esc_url( $this->_url_from_attrs( $attr ) ), __( 'View this code snippet on GitHub.', 'gistpress' ) ); | |
/** | |
* Filter what is shown in feeds. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $html Markup to show in feeds. | |
*/ | |
return apply_filters( 'gistpress_feed_html', $html ); | |
} | |
$json_url = $this->_url_from_attrs( $attr, 'json' ); | |
$html = $this->get_gist_html( $json_url, $attr ); | |
if ( $this->unknown() === $html ) { | |
return make_clickable( $url ); | |
} | |
// If there was a result, return it. | |
if ( $html ) { | |
if ( $attr['embed_stylesheet'] ) { | |
wp_enqueue_style( 'gistpress' ); | |
} | |
/** | |
* Filter the output HTML. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $html The output HTML. | |
* @param string $url The URL to the Gist. | |
* @param array $attr Shortcode attributes, standardized. | |
* @param int $id Post ID. | |
*/ | |
$html = apply_filters( 'gistpress_html', $html, $this->_url_from_attrs( $attr, 'root' ), $attr, get_the_ID() ); | |
foreach ( $attr as $key => $value ) { | |
$message = '<strong>' . $key . __(' (shortcode attribute)', 'gistpress') . ':</strong> '; | |
$message .= is_scalar( $value ) ? $value : print_r( $value, true ); | |
$this->debug_log( $message, $shortcode_hash ); | |
} | |
$this->debug_log( '<strong>Gist:</strong><br />' . $html, $shortcode_hash ); | |
return $html; | |
} | |
return ''; | |
} | |
/** | |
* Helper method to determine if a shortcode attribute is true or false. | |
* | |
* @since 1.1.0 | |
* | |
* @param string|int|bool $var Attribute value. | |
* | |
* @return bool | |
*/ | |
public function shortcode_bool( $var ) { | |
$falsey = array( 'false', '0', 'no', 'n' ); | |
return ( ! $var || in_array( strtolower( $var ), $falsey ) ) ? false : true; | |
} | |
/** | |
* Parses and expands the shortcode 'highlight' attribute and returns it | |
* in a usable format. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $line_numbers Comma-separated list of line numbers and ranges. | |
* | |
* @return array|null List of line numbers, or null if no line numbers given | |
*/ | |
public function parse_highlight_arg( $line_numbers ) { | |
if ( empty( $line_numbers ) ) { | |
return null; | |
} | |
// Determine which lines should be highlighted. | |
$highlight = array_map( 'trim', explode( ',', $line_numbers ) ); | |
// Convert any ranges. | |
foreach ( $highlight as $index => $num ) { | |
if ( false !== strpos( $num, '-' ) ) { | |
unset( $highlight[ $index ] ); | |
$range = array_map( 'trim', explode( '-', $num ) ); | |
foreach ( range( $range[0], $range[1] ) as $line ) { | |
array_push( $highlight, $line ); | |
} | |
} | |
} | |
return array_unique( $highlight ); | |
} | |
/** | |
* Parses the shortcode 'lines' attribute into min and max values. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $line_numbers Range of line numbers separated by a dash. | |
* | |
* @return array Associative array with min and max line numbers. | |
*/ | |
public function parse_line_number_arg( $line_numbers ) { | |
if ( empty( $line_numbers ) ) { | |
return array( 'min' => 0, 'max' => 0, ); | |
} | |
if ( false === strpos( $line_numbers, '-' ) ) { | |
$range = array_fill_keys( array( 'min', 'max', ), absint( trim( $line_numbers ) ) ); | |
} else { | |
$numbers = array_map( 'absint', array_map( 'trim', explode( '-', $line_numbers ) ) ); | |
$range = array( | |
'min' => $numbers[0], | |
'max' => $numbers[1], | |
); | |
} | |
return $range; | |
} | |
/** | |
* Retrieve Gist HTML. | |
* | |
* Gist HTML can come from one of three different sources: | |
* Remote JSON endpoint, | |
* Transient, | |
* Post meta cache. | |
* | |
* When a Gist is intially requested, the HTML is fetched from the JSON | |
* endpoint and cached in a post meta field. It is then processed to limit | |
* line numbers, highlight specific lines, and add a few extra classes as | |
* style hooks. The processed HTML is then stored in a transient using a | |
* hash of the shortcodes attributes for the key. | |
* | |
* On subsequent requests, the HTML is fetched from the transient until it | |
* expires, then it is requested from the remote URL again. | |
* | |
* In the event the HTML can't be fetched from the remote endpoint and the | |
* transient has expired, the HTML is retrieved from the post meta backup. | |
* | |
* This algorithm allows Gist HTML to stay in sync with any changes GitHub | |
* may make to their markup, while providing a local cache for faster | |
* retrieval and a backup in case GitHub can't be reached. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $url The JSON endpoint for the Gist( + filename) | |
* @param array $args List of shortcode attributes. | |
* | |
* @return string Gist HTML or {{unknown}} if it couldn't be determined. | |
*/ | |
public function get_gist_html( $url, array $args ) { | |
// Add a specific file from a Gist to the URL. (already added to $url) | |
$shortcode_hash = $this->shortcode_hash( 'gist', $args ); | |
$raw_key = '_gist_raw_' . md5( $url ); | |
$transient_key = $this->transient_key( $shortcode_hash ); | |
$html = get_transient( $transient_key ); | |
if ( empty( $html ) ) { | |
$html = get_transient( $raw_key ); | |
$transient_expire = DAY_IN_SECONDS; | |
if ( $html && $this->unknown() != $html ) { | |
$html = $this->process_gist_html( $html, $args ); | |
$this->debug_log( __( '<strong>Raw Source:</strong> Transient Cache', 'gistpress' ), $shortcode_hash ); | |
} else { | |
// Retrieve raw html from Gist JSON endpoint. | |
$json = $this->fetch_gist( $url ); | |
if ( ! empty( $json->div ) ) { | |
set_transient( $raw_key, $json->div, $transient_expire ); | |
// Update the post meta fallback. | |
// @link http://core.trac.wordpress.org/ticket/21767 | |
update_post_meta( get_the_ID(), $raw_key, addslashes( $json->div ) ); | |
$html = $this->process_gist_html( $json->div, $args ); | |
$this->debug_log( __( '<strong>Raw Source:</strong> Remote JSON Endpoint - ', 'gistpress' ) . $url, $shortcode_hash ); | |
$this->debug_log( __( '<strong>Output Source:</strong> Processed the raw source.', 'gistpress' ), $shortcode_hash ); | |
} | |
// Update the style sheet reference. | |
if ( ! empty( $json->stylesheet ) ) { | |
update_option( 'gistpress_stylesheet', $json->stylesheet ); | |
} | |
} | |
// Failures are cached, too. Update the post to attempt to fetch again. | |
$html = ( $html ) ? $html : $this->unknown(); | |
if ( $this->unknown() == $html && ( $fallback = get_post_meta( get_the_ID(), $raw_key, true ) ) ) { | |
// Return the fallback instead of the string representing unknown. | |
$html = $this->process_gist_html( $fallback, $args ); | |
// Cache the fallback for an hour. | |
$transient_expire = HOUR_IN_SECONDS; | |
$this->debug_log( __( '<strong>Raw Source:</strong> Post Meta Fallback', 'gistpress' ), $shortcode_hash ); | |
$this->debug_log( __( '<strong>Output Source:</strong> Processed Raw Source', 'gistpress' ), $shortcode_hash ); | |
} elseif ( $this->unknown() == $html ) { | |
$this->debug_log( '<strong style="color: #e00">' . __( 'Remote call and transient failed and fallback was empty.', 'gistpress' ) . '</strong>', $shortcode_hash ); | |
} | |
// Cache the processed HTML. | |
set_transient( $transient_key, $html, $transient_expire ); | |
} else { | |
$this->debug_log( __( '<strong>Output Source:</strong> Transient Cache', 'gistpress' ), $shortcode_hash ); | |
} | |
$this->debug_log( '<strong>' . __( 'JSON Endpoint:', 'gistpress' ) . '</strong> ' . $url, $shortcode_hash ); | |
$this->debug_log( '<strong>' . __( 'Raw Key (Transient & Post Meta):', 'gistpress' ) . '</strong> ' . $raw_key, $shortcode_hash ); | |
$this->debug_log( '<strong>' . __( 'Processed Output Key (Transient):', 'gistpress' ) . '</strong> ' . $transient_key, $shortcode_hash ); | |
return $html; | |
} | |
/** | |
* Fetch Gist data from its JSON endpoint. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $url Gist JSON endpoint. | |
* | |
* @return object|bool Gist JSON object, or false if anything except a HTTP | |
* Status code of 200 was received. | |
*/ | |
public function fetch_gist( $url ) { | |
$response = wp_remote_get( $url, array( 'sslverify' => false ) ); | |
if ( '200' == wp_remote_retrieve_response_code( $response ) ) { | |
return json_decode( wp_remote_retrieve_body( $response ) ); | |
} | |
return false; | |
} | |
/** | |
* Process the HTML returned from a Gist's JSON endpoint based on settings | |
* passed through the shortcode. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $html HTML from the Gist's JSON endpoint. | |
* @param array $args List of shortcode attributes. | |
* | |
* @return string Modified HTML. | |
*/ | |
public function process_gist_html( $html, array $args ) { | |
// Remove the line number cell if it has been disabled. | |
if ( ! $args['show_line_numbers'] ) { | |
$html = preg_replace( '#<td class="line-numbers">.*?</td>#s', '', $html ); | |
} | |
// Remove the meta section if it has been disabled. | |
if ( ! $args['show_meta'] ) { | |
$html = preg_replace( '#<div class="gist-meta">.*?</div>#s', '', $html ); | |
} | |
$lines_pattern = '#(<pre class="line-pre"[^>]*>)(.+?)</pre>#s'; | |
preg_match( $lines_pattern, $html, $lines_matches ); | |
if ( ! empty( $lines_matches[2] ) ) { | |
// Restrict the line number display if a range has been specified. | |
if ( $args['show_line_numbers'] && ( ( $args['lines']['min'] && $args['lines']['max'] ) || ! empty( $args['lines_start'] ) ) ) { | |
$html = $this->process_gist_line_numbers( $html, $args['lines'], $args['lines_start'] ); | |
} | |
if ( ! empty( $args['highlight'] ) ) { | |
// Flip to use isset() when looping through the lines. | |
$highlight = array_flip( $args['highlight'] ); | |
} | |
// Extract and cleanup the individual lines from the Gist HTML into an array for processing. | |
$lines = preg_replace( '#<div[^>]+>#', '', trim( $lines_matches[2] ), 1 ); | |
$lines = preg_split( '#</div>[\s]*<div[^>]+>#', substr( $lines, 0, strlen( $lines ) - 6 ) ); | |
foreach ( $lines as $key => $line ) { | |
// Remove lines if they're not in the specified range and continue. | |
if ( ( $args['lines']['min'] && $key < $args['lines']['min'] - 1 ) || ( $args['lines']['max'] && $key > $args['lines']['max'] - 1 ) ) { | |
unset( $lines[ $key ] ); | |
continue; | |
} | |
// Add classes for styling. | |
$classes = array( 'line' ); | |
//$classes[] = ( $key % 2 ) ? 'line-odd' : 'line-even'; | |
$style = ''; | |
if ( isset( $highlight[ $key + 1 ] ) ) { | |
$classes[] = 'line-highlight'; | |
if ( ! empty( $args['highlight_color'] ) ) { | |
$style = ' style="background-color: ' . $args['highlight_color'] . ' !important"'; | |
} | |
} | |
/** | |
* Filter the classes applied to a line of the Gist. | |
* | |
* @since 2.0.0 | |
* | |
* @param array $classes List of HTML class values. | |
*/ | |
$classes = apply_filters( 'gistpress_line_classes', $classes ); | |
$class = ( ! empty( $classes ) && is_array( $classes ) ) ? ' class="' . implode ( ' ', $classes ) . '"' : ''; | |
$lines[ $key ] = '<div' . $class . $style . '>' . $line . '</div>'; | |
} | |
$replacement = $lines_matches[1] . join( '', $lines ) . '</pre>'; | |
$replacement = $this->preg_replace_quote( $replacement ); | |
$html = preg_replace( $lines_pattern, $replacement, $html, 1 ); | |
} | |
return $html; | |
} | |
/** | |
* Removes line numbers from the Gist's HTML that fall outside the | |
* supplied range and modifies the starting number if specified. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $html HTML from the Gist's JSON endpoint. | |
* @param array $range Array of min and max values. | |
* @param int $start Optional. Line number to start counting at. | |
* | |
* @return string Modified HTML. | |
*/ | |
public function process_gist_line_numbers( $html, array $range, $start = null ) { | |
$line_num_pattern = '#(<td class="line-numbers">)(.*?)</td>#s'; | |
preg_match( $line_num_pattern, $html, $line_num_matches ); | |
if ( ! empty( $line_num_matches[2] ) ) { | |
$start = absint( $start ); | |
$lines = array_map( 'trim', explode( "\n", trim( $line_num_matches[2] ) ) ); | |
if( ! $start && $range['min'] && $range['max'] ) { | |
$line_numbers = array_slice( $lines, $range['min'] - 1, $range['max'] - $range['min'] + 1 ); | |
} else { | |
// Determine how many lines should be shown. | |
$range_length = count( $lines ); | |
if ( $range['min'] && $range['max'] ) { | |
$range_length = $range['max'] - $range['min'] + 1; | |
$start = ( $start ) ? $start : $range['min']; | |
} | |
// Create a template with a placeholder for the line number. | |
preg_match( '#<span rel="([^"]+)[0-9]+?"#', $lines[0], $attr_matches ); | |
if ( ! empty( $attr_matches[1] ) ) { | |
$template = sprintf( '<span rel="%1$s%2$s" id="%1$s%2$s">%2$s</span>', esc_attr( $attr_matches[1] ), '{{num}}' ); | |
} else { | |
$template = '<span>{{num}}</span>'; | |
} | |
// Generate HTML for the line numbers. | |
$line_numbers = array(); | |
for ( $i = $start; $i <= $start + $range_length - 1; $i ++ ) { | |
$line_numbers[] = str_replace( '{{num}}', $i, $template ); | |
} | |
} | |
$replacement = $line_num_matches[1] . join( "\n", $line_numbers ) . '</td>'; | |
$html = preg_replace( $line_num_pattern, $replacement, $html, 1 ); | |
} | |
return $html; | |
} | |
/** | |
* Removes transients associated with Gists embedded in a post. | |
* | |
* Retrieves the keys of meta data associated with a post and deletes any | |
* transients with a matching embed key. | |
* | |
* @since 1.1.0 | |
* | |
* @param int $post_id Post ID. | |
* @param WP_Post $post_after Post object after update. | |
* @param WP_Post $post_before Post object before update. | |
*/ | |
public function delete_gist_transients( $post_id, WP_Post $post_after, WP_Post $post_before ) { | |
$this->delete_shortcode_transients = true; | |
// Run the shortcodes to clear associated transients. | |
do_shortcode( $GLOBALS['wp_embed']->autoembed( $post_after->post_content ) ); | |
do_shortcode( $GLOBALS['wp_embed']->autoembed( $post_before->post_content ) ); | |
// Delete raw transients whose keys match a post meta fallback. | |
$keys = get_post_custom_keys( $post_id ); | |
if ( $keys ) { | |
foreach( $keys as $key ) { | |
if ( 0 === strpos( $key, '_gist_raw_' ) ) { | |
delete_transient( $key ); | |
} | |
} | |
} | |
} | |
/** | |
* Rebuild the original shortcode as a string with raw attributes. | |
* | |
* Now handles cases where $rawattr is missing first attribute name | |
* (jetpack style), and where there is a content | |
* | |
* @since 1.1.1 | |
* | |
* @param mixed $rawattr usually an array… Raw attributes => values. | |
* @param string $content the stuff in the middle | |
* | |
* @return string Gist shortcode. | |
*/ | |
protected function rebuild_shortcode( $rawattr, $content ) { | |
if ( is_array($rawattr) ) { | |
$attrs = array(); | |
foreach ( $rawattr as $key => $value ) { | |
// handle non-attribute codes | |
if ( is_int($key) ) { | |
$attrs[] = $value; | |
} else { | |
$attrs[] = $key . '="' . $value . '"'; | |
} | |
} | |
$return = '[gist '. implode( ' ', $attrs ); | |
} else { | |
$return = '[gist'; | |
} | |
if ($content) { | |
$return .= ']' . esc_html($content) . '[/gist]'; | |
} else { | |
$return .= ']'; | |
} | |
return $return; | |
} | |
/** | |
* Set defaults and sanitize shortcode attributes and attribute values. | |
* | |
* @since 1.1.1 | |
* | |
* @param array $rawattr Associative array of raw attributes => values. | |
* | |
* @return array Standardized and sanitized shortcode attributes. | |
*/ | |
protected function standardize_attributes( array $rawattr ) { | |
// Translate Jetpack style shortcodes into attributes | |
if ( !empty($rawattr[0]) ) { | |
$rawattr = array_merge( $this->_extract_url_string($rawattr[0]), $rawattr ); | |
unset($rawattr[0]); // not really needed, but let's be safe | |
} | |
/** | |
* Filter the shortcode attributes defaults. | |
* | |
* @since 2.0.0 | |
* | |
* @see standardize_attributes() | |
* | |
* @param array $gistpress_shortcode_defaults { | |
* Shortcode attributes defaults. | |
* | |
* @type bool $embed_stylesheet Filterable value to include style sheet or not. Default is true | |
* to include it. | |
* @type string $file File name within gist. Default is an empty string, indicating | |
* all files. | |
* @type array $highlight Lines to highlight. Default is empty array, to highlight | |
* no lines. | |
* @type string $highlight_color Filterable hex color code. Default is #ffc. | |
* @type string $id Gist ID. Non-optional. | |
* @type string $lines Number of lines to show. Default is empty string, indicating | |
* all lines in the gist. | |
* @type string $lines_start Which line number to start from. Default is empty string, | |
* indicating line number 1. | |
* @type bool $show_line_numbers Show line numbers or not, default is true, to show line numbers. | |
* @type bool $show_meta Show meta information or not, default is true, to show | |
* meta information. | |
* @type string $url Allow URL style shortcode embedding | |
* @type string $revision Link a specific revision in a gist | |
* } | |
*/ | |
$defaults = apply_filters( | |
'gistpress_shortcode_defaults', | |
array( | |
/** | |
* Filter to include the style sheet or not. | |
* | |
* @since 2.0.0 | |
* | |
* @param bool $gistpress_stylesheet_default Include default style sheet or not. | |
* Default is true, to include it. | |
*/ | |
'embed_stylesheet' => apply_filters( 'gistpress_stylesheet_default', true ), | |
'file' => '', | |
'highlight' => array(), | |
/** | |
* Filter highlight color. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $gistpress_highlight_color Hex color code for highlighting lines. | |
* Default is `#ffc`. | |
*/ | |
'highlight_color' => apply_filters( 'gistpress_highlight_color', '#ffc' ), | |
'id' => '', | |
'lines' => '', | |
'lines_start' => '', | |
'show_line_numbers' => true, | |
'show_meta' => true, | |
'url' => '', | |
'revision' => '', | |
) | |
); | |
// Sanitize attributes. | |
$attr = shortcode_atts( $defaults, $rawattr ); | |
$attr['embed_stylesheet'] = $this->shortcode_bool( $attr['embed_stylesheet'] ); | |
$attr['show_line_numbers'] = $this->shortcode_bool( $attr['show_line_numbers'] ); | |
$attr['show_meta'] = $this->shortcode_bool( $attr['show_meta'] ); | |
$attr['highlight'] = $this->parse_highlight_arg( $attr['highlight'] ); | |
$attr['lines'] = $this->parse_line_number_arg( $attr['lines'] ); | |
return $attr; | |
} | |
/** | |
* Try to determine the real file name from a sanitized file name. | |
* | |
* The new Gist "bookmark" URLs point to sanitized file names so that both | |
* hyphen and period in a file name show up as a hyphen e.g. a filename of | |
* foo.bar and foo-bar both appear in the bookmark URL as foo-bar. The | |
* correct original filenames are listed in the JSON data for the overall | |
* Gist, so this method does a call to that, and loops through the listed | |
* file names to see if it can determine which file was meant. | |
* | |
* If a Gist has two files that both resolve to the same sanitized filename, | |
* then we don't have any way to determine which one the other determined, | |
* so we just return the first one we find. If that's incorrect, the author | |
* can use the shortcode approach, which allows a specific file name to be | |
* used. | |
* | |
* @since 2.1.0 | |
* | |
* @param string $sanitized_filename Sanitized filename, such as foo-bar-php. | |
* @param string $delimiter Either underscore or hyphen. | |
* @param string $id Gist ID. | |
* | |
* @return string Filename, or empty string if it couldn't be determined. | |
*/ | |
protected function get_file_name( $sanitized_filename, $delimiter, $id ) { | |
// Old style link - filename wasn't actually changed. | |
if ( '_' === $delimiter ) { | |
return $sanitized_filename; | |
} | |
// New style bookmark - filename had . replaced with - | |
// Means we have to go and look up what the filename could have been. | |
$transient_key = $this->gist_files_transient_key( $id ); | |
$gist_files = get_transient( $transient_key ); | |
if ( ! $gist_files ) { | |
$url = 'https://gist.github.com/' . $id . '.json'; | |
$json = $this->fetch_gist( $url ); | |
if ( $json && ! empty( $json->files ) ) { | |
$gist_files = $json->files; | |
set_transient( $transient_key, $gist_files, WEEK_IN_SECONDS ); | |
} else { | |
set_transient( $transient_key, array(), MINUTE_IN_SECONDS * 15 ); | |
} | |
} | |
// If a gist has foo.bar.php and foo-bar.php, then we can't yet | |
// determine which was actually wanted, since both give the same | |
// bookmark URL. Here, we just return the first one we find. | |
if ( ! empty( $gist_files ) ) { | |
foreach ( $gist_files as $file ) { | |
if ( str_replace( '.', '-', $file ) === $sanitized_filename ) { | |
return $file; | |
} | |
} | |
} | |
return ''; | |
} | |
/** | |
* Sanitize filename in the style of github | |
* | |
* Note that this isn't perfect if it is an old style but the . is injected | |
* this might not link properly, not a big deal given where this will be. | |
* used (only in hashlinks). Plus, I don't think GitHub uses the old | |
* hashlinks anymore. | |
* {@see https://gist.github.com/anonymous/13338#file-getjoy}. | |
* | |
* @param string $filename the filename to hashlink | |
* @return string the hashlink id | |
*/ | |
protected function set_file_name( $filename ) { | |
return 'file-'.str_replace( '.', '-', $filename); | |
} | |
/** | |
* Wrapper for a PSR-3 compatible logger. | |
* | |
* If no logger has been set via the set_logger() method on an instance of | |
* this class, or WP_DEBUG is not enabled, then log messages quietly die | |
* here. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $message A message to log for the current shortcode. | |
* @param mixed $id Optional. An ID under which the message should be grouped. | |
* | |
* @todo Handle missing $id any better? | |
*/ | |
protected function debug_log( $message, $id = null ) { | |
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && isset( $this->logger ) ) { | |
$this->logger->debug( $message, array( 'key' => $id ) ); | |
} | |
} | |
/** | |
* Sort a shortcode's attributes by name and hash it for use as a cache | |
* key and logger message grouping. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $tag Shortcode tag, used as hash prefix. | |
* @param array $args Associative array of shortcode attributes. | |
* | |
* @return string md5 hash as a 32-character hexadecimal number. | |
*/ | |
protected function shortcode_hash( $tag, array $args ) { | |
ksort( $args ); | |
return md5( $tag . '_' . serialize( $args ) ); | |
} | |
/** | |
* Get the transient key. | |
* | |
* @since 1.1.0 | |
* | |
* @param string $identifier The identifier part of the key. | |
* | |
* @return string Transient key name. | |
*/ | |
protected function transient_key( $identifier ) { | |
return 'gist_html_' . $identifier; | |
} | |
/** | |
* Get the transient key for a list of a Gist's files. | |
* | |
* @since 2.1.0 | |
* | |
* @param string $id The Gist id. | |
* | |
* @return string Transient key name. | |
*/ | |
protected function gist_files_transient_key( $gist_id ) { | |
return 'gist_files_' . md5( $gist_id ); | |
} | |
/** | |
* String to identify a failure when retrieving a Gist's HTML. | |
* | |
* @since 1.1.0 | |
* | |
* @return string | |
*/ | |
protected function unknown() { | |
return '{{unknown}}'; | |
} | |
// | |
// PRIVATE UTILITY FUNCTIONS | |
// | |
/** | |
* Extracts content from the regex matches of a gist oembed URL. | |
* | |
* @param array $matches preg_match output | |
* @return array has of data extracted. They are: url, id, (file) | |
*/ | |
private function _extract_url_matches( $matches ) { | |
$return = array( | |
'url' => $matches[0], | |
'id' => ( isset( $matches[1] ) && ! empty( $matches[1] ) ) | |
? $matches[1] | |
: '', | |
); | |
if ( isset( $matches[4] ) && ! empty( $matches[4] ) ) { | |
$return['file'] = $this->get_file_name( $matches[4], $matches[3], $matches[1] ); | |
} | |
if ( isset( $matches[2] ) && ! empty( $matches[2] ) ) { | |
$return['revision'] = $matches[2]; | |
} | |
return $return; | |
} | |
/** | |
* Extracts the content from a url | |
* | |
* @param string $url the string to match to a URL | |
* @return array either empty array or data extracted: url, id, file | |
*/ | |
private function _extract_url_string( $url ) { | |
if ( preg_match( self::OEMBED_REGEX, $url, $matches ) ) { | |
return $this->_extract_url_matches( $matches ); | |
} | |
return array(); | |
} | |
/** | |
* Extracts Jetpack style content into $attrs | |
* | |
* Note that there are two styles: URL or a bare ID. Unlike Jetpack, this | |
* also understands modern-style URLs, but it does not support files in the | |
* case of bare IDs. | |
* | |
* @param array $attrs the current state of attributes | |
* @param string $content the content to search for URL or ID | |
* @return boolean true if match happenned | |
*/ | |
private function _attrs_in_content( &$attrs, $content ) { | |
//see if content is a URL | |
if ( preg_match( self::OEMBED_REGEX, $content, $matches ) ) { | |
$attrs = array_merge( $attrs, $this->_extract_url_matches( $matches ) ); | |
return true; | |
} | |
// see if content is an ID | |
// Note that Jetpack is wrong because is_numeric returns false on hexadecimal | |
// numbers | |
if ( preg_match( '!(^[0-9a-f]+)$!', $content, $matches ) ) { | |
$attrs['id'] = $matches[1]; | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Generate a gist URL from the attributes array | |
* @param array $attrs The shortcode attributes. Relevant params are id, | |
* file, revision (username will be mapped on | |
* server-side) | |
* @param string $type The type of URL to return. Values are 'full', 'root' | |
* 'latest', json', and 'js' | |
* @return string The URL | |
*/ | |
private function _url_from_attrs( $attrs, $type='full' ) { | |
$url = 'https://gist.github.com/' . $attrs['id']; | |
if ( $type == 'latest' ) { return $url; } | |
if ( !empty($attrs['revision']) ) { | |
$url .= '/' . $attrs['revision']; | |
} | |
if ( $type == 'root' ) { return $url; } | |
if ( $type == 'json' ) { | |
$url .= '.json'; | |
} elseif ( $type == 'js' ) { | |
$url .= '.js'; | |
} | |
if ( $type == 'full' ) { | |
if ( empty($attrs['file']) ) { | |
return $url; | |
} | |
return $url . '#' . $this->set_file_name( $attrs['file'] ); | |
} | |
if ( !empty($attrs['file']) ) { | |
//wordpress built-in add_query_arg() is expensive | |
$url .= '?file='. urlencode( $attrs['file'] ); | |
} | |
return $url; | |
} | |
// | |
// PUBLIC UTILITY FUNCTIONS | |
// | |
/** | |
* Escape a regular expression replacement string. | |
* | |
* @since 2.0.2 | |
* @link http://www.procata.com/blog/archives/2005/11/13/two-preg_replace-escaping-gotchas/ | |
* | |
* @param string $str String to escape. | |
* @return string | |
*/ | |
public function preg_replace_quote( $str ) { | |
return preg_replace( '/(\$|\\\\)(?=\d)/', '\\\\$1', $str ); | |
} | |
} |
No shortcode gets injected and all is safe. (This is how JetPack’s plugin works, btw.)
Ahh, but then the output of your embed will get wp_autop
eed on. In this case all the line numbers will get an extra <br \>
tag thrown in, and the look is destroyed.
Jetpack shortcodes are totally cool with being peed on
But wait? That doesn’t happen in JetPack, you counter.
First of all, JetPack doesn’t cache the gist highlighting, instead it depends on a remote script (security hole) being opened on your site with GitHub’s oEmbed service, defeating the main purpose of using GistPress in the first place.
Second of all. Are you sure? Here is what JetPack embed put out:
<?php | |
/** | |
* GitHub's Gist site supports oEmbed but their oembed provider only | |
* returns raw HTML (no styling) and the first little bit of the code. | |
* | |
* Their Javascript-based embed method is a lot better, so that's what we're using. | |
*/ | |
wp_embed_register_handler( 'github-gist', '#https?://gist\.github\.com/([a-zA-Z0-9]+)#', 'github_gist_embed_handler' ); | |
add_shortcode( 'gist', 'github_gist_shortcode' ); | |
function github_gist_embed_handler( $matches, $attr, $url, $rawattr ) { | |
// Let the shortcode callback do all the work | |
return github_gist_shortcode( $attr, $url ); | |
} | |
function github_gist_shortcode( $atts, $content = '' ) { | |
if ( empty( $atts[0] ) && empty( $content ) ) | |
return '<!-- Missing Gist ID -->'; | |
$id = ( ! empty( $content ) ) ? $content : $atts[0]; | |
// Parse a URL | |
if ( ! is_numeric( $id ) ) | |
$id = preg_replace( '#https?://gist.github.com/([a-zA-Z0-9]+)#', '$1', $id ); | |
if ( ! $id ) | |
return '<!-- Invalid Gist ID -->'; | |
$embed_url = "https://gist.github.com/{$id}.js"; | |
if ( ! empty( $atts['file'] ) ) | |
$embed_url = add_query_arg( 'file', urlencode( $atts['file'] ), $embed_url ); | |
// inline style to prevent the bottom margin to the embed that themes like TwentyTen, et al., add to tables | |
return '<style>.gist table { margin-bottom: 0; }</style>' . '<script src="' . esc_url( $embed_url ) . '"></script>'; | |
} |
And here is an HTML run on a link embed
<style>.gist table { margin-bottom: 0; }</style> | |
<p><script src="https://gist.github.com/padolsey/527683.js"></script></p> |
Remind me where did that extra <p>
come from again?
WordPress Core is not okay with it, but is cool with embed output being peed on
Then how does the embed
code in WordPress core work? I’ll let the core explain:
<?php | |
/** | |
* API for easily embedding rich media such as videos and images into content. | |
* | |
* @package WordPress | |
* @subpackage Embed | |
* @since 2.9.0 | |
*/ | |
class WP_Embed { | |
public $handlers = array(); | |
public $post_ID; | |
public $usecache = true; | |
public $linkifunknown = true; | |
/** | |
* When an URL cannot be embedded, return false instead of returning a link | |
* or the URL. Bypasses the 'embed_maybe_make_link' filter. | |
*/ | |
public $return_false_on_fail = false; | |
/** | |
* Constructor | |
*/ | |
public function __construct() { | |
// Hack to get the [embed] shortcode to run before wpautop() | |
add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 ); | |
// Shortcode placeholder for strip_shortcodes() | |
add_shortcode( 'embed', '__return_false' ); | |
// Attempts to embed all URLs in a post | |
add_filter( 'the_content', array( $this, 'autoembed' ), 8 ); | |
// After a post is saved, cache oEmbed items via AJAX | |
add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) ); | |
} | |
/** | |
* Process the [embed] shortcode. | |
* | |
* Since the [embed] shortcode needs to be run earlier than other shortcodes, | |
* this function removes all existing shortcodes, registers the [embed] shortcode, | |
* calls {@link do_shortcode()}, and then re-registers the old shortcodes. | |
* | |
* @uses $shortcode_tags | |
* | |
* @param string $content Content to parse | |
* @return string Content with shortcode parsed | |
*/ | |
public function run_shortcode( $content ) { | |
global $shortcode_tags; | |
// Back up current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
add_shortcode( 'embed', array( $this, 'shortcode' ) ); | |
// Do the shortcode (only the [embed] one is registered) | |
$content = do_shortcode( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
/** | |
* If a post/page was saved, then output JavaScript to make | |
* an AJAX request that will call WP_Embed::cache_oembed(). | |
*/ | |
public function maybe_run_ajax_cache() { | |
$post = get_post(); | |
if ( ! $post || empty( $_GET['message'] ) ) | |
return; | |
?> | |
<script type="text/javascript"> | |
/* <![CDATA[ */ | |
jQuery(document).ready(function($){ | |
$.get("<?php echo admin_url( 'admin-ajax.php?action=oembed-cache&post=' . $post->ID, 'relative' ); ?>"); | |
}); | |
/* ]]> */ | |
</script> | |
<?php | |
} | |
/** | |
* Register an embed handler. Do not use this function directly, use {@link wp_embed_register_handler()} instead. | |
* This function should probably also only be used for sites that do not support oEmbed. | |
* | |
* @param string $id An internal ID/name for the handler. Needs to be unique. | |
* @param string $regex The regex that will be used to see if this handler should be used for a URL. | |
* @param callback $callback The callback function that will be called if the regex is matched. | |
* @param int $priority Optional. Used to specify the order in which the registered handlers will be tested (default: 10). Lower numbers correspond with earlier testing, and handlers with the same priority are tested in the order in which they were added to the action. | |
*/ | |
public function register_handler( $id, $regex, $callback, $priority = 10 ) { | |
$this->handlers[$priority][$id] = array( | |
'regex' => $regex, | |
'callback' => $callback, | |
); | |
} | |
/** | |
* Unregister a previously registered embed handler. Do not use this function directly, use {@link wp_embed_unregister_handler()} instead. | |
* | |
* @param string $id The handler ID that should be removed. | |
* @param int $priority Optional. The priority of the handler to be removed (default: 10). | |
*/ | |
public function unregister_handler( $id, $priority = 10 ) { | |
if ( isset($this->handlers[$priority][$id]) ) | |
unset($this->handlers[$priority][$id]); | |
} | |
/** | |
* The {@link do_shortcode()} callback function. | |
* | |
* Attempts to convert a URL into embed HTML. Starts by checking the URL against the regex of the registered embed handlers. | |
* If none of the regex matches and it's enabled, then the URL will be given to the {@link WP_oEmbed} class. | |
* | |
* @param array $attr { | |
* Shortcode attributes. Optional. | |
* | |
* @type int $width Width of the embed in pixels. | |
* @type int $height Height of the embed in pixels. | |
* } | |
* @param string $url The URL attempting to be embedded. | |
* @return string The embed HTML on success, otherwise the original URL. | |
*/ | |
public function shortcode( $attr, $url = '' ) { | |
$post = get_post(); | |
if ( empty( $url ) && ! empty( $attr['src'] ) ) { | |
$url = $attr['src']; | |
} | |
if ( empty( $url ) ) | |
return ''; | |
$rawattr = $attr; | |
$attr = wp_parse_args( $attr, wp_embed_defaults( $url ) ); | |
// kses converts & into & and we need to undo this | |
// See https://core.trac.wordpress.org/ticket/11311 | |
$url = str_replace( '&', '&', $url ); | |
// Look for known internal handlers | |
ksort( $this->handlers ); | |
foreach ( $this->handlers as $priority => $handlers ) { | |
foreach ( $handlers as $id => $handler ) { | |
if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) { | |
if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) ) | |
/** | |
* Filter the returned embed handler. | |
* | |
* @since 2.9.0 | |
* | |
* @see WP_Embed::shortcode() | |
* | |
* @param mixed $return The shortcode callback function to call. | |
* @param string $url The attempted embed URL. | |
* @param array $attr An array of shortcode attributes. | |
*/ | |
return apply_filters( 'embed_handler_html', $return, $url, $attr ); | |
} | |
} | |
} | |
$post_ID = ( ! empty( $post->ID ) ) ? $post->ID : null; | |
if ( ! empty( $this->post_ID ) ) // Potentially set by WP_Embed::cache_oembed() | |
$post_ID = $this->post_ID; | |
// Unknown URL format. Let oEmbed have a go. | |
if ( $post_ID ) { | |
// Check for a cached result (stored in the post meta) | |
$key_suffix = md5( $url . serialize( $attr ) ); | |
$cachekey = '_oembed_' . $key_suffix; | |
$cachekey_time = '_oembed_time_' . $key_suffix; | |
/** | |
* Filter the oEmbed TTL value (time to live). | |
* | |
* @since 4.0.0 | |
* | |
* @param int $time Time to live (in seconds). | |
* @param string $url The attempted embed URL. | |
* @param array $attr An array of shortcode attributes. | |
* @param int $post_ID Post ID. | |
*/ | |
$ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID ); | |
$cache = get_post_meta( $post_ID, $cachekey, true ); | |
$cache_time = get_post_meta( $post_ID, $cachekey_time, true ); | |
if ( ! $cache_time ) { | |
$cache_time = 0; | |
} | |
$cached_recently = ( time() - $cache_time ) < $ttl; | |
if ( $this->usecache || $cached_recently ) { | |
// Failures are cached. Serve one if we're using the cache. | |
if ( '{{unknown}}' === $cache ) | |
return $this->maybe_make_link( $url ); | |
if ( ! empty( $cache ) ) { | |
/** | |
* Filter the cached oEmbed HTML. | |
* | |
* @since 2.9.0 | |
* | |
* @see WP_Embed::shortcode() | |
* | |
* @param mixed $cache The cached HTML result, stored in post meta. | |
* @param string $url The attempted embed URL. | |
* @param array $attr An array of shortcode attributes. | |
* @param int $post_ID Post ID. | |
*/ | |
return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID ); | |
} | |
} | |
/** | |
* Filter whether to inspect the given URL for discoverable link tags. | |
* | |
* @since 2.9.0 | |
* | |
* @see WP_oEmbed::discover() | |
* | |
* @param bool $enable Whether to enable `<link>` tag discovery. Default false. | |
*/ | |
$attr['discover'] = ( apply_filters( 'embed_oembed_discover', false ) && author_can( $post_ID, 'unfiltered_html' ) ); | |
// Use oEmbed to get the HTML | |
$html = wp_oembed_get( $url, $attr ); | |
// Maybe cache the result | |
if ( $html ) { | |
update_post_meta( $post_ID, $cachekey, $html ); | |
update_post_meta( $post_ID, $cachekey_time, time() ); | |
} elseif ( ! $cache ) { | |
update_post_meta( $post_ID, $cachekey, '{{unknown}}' ); | |
} | |
// If there was a result, return it | |
if ( $html ) { | |
/** This filter is documented in wp-includes/class-wp-embed.php */ | |
return apply_filters( 'embed_oembed_html', $html, $url, $attr, $post_ID ); | |
} | |
} | |
// Still unknown | |
return $this->maybe_make_link( $url ); | |
} | |
/** | |
* Delete all oEmbed caches. Unused by core as of 4.0.0. | |
* | |
* @param int $post_ID Post ID to delete the caches for. | |
*/ | |
public function delete_oembed_caches( $post_ID ) { | |
$post_metas = get_post_custom_keys( $post_ID ); | |
if ( empty($post_metas) ) | |
return; | |
foreach( $post_metas as $post_meta_key ) { | |
if ( '_oembed_' == substr( $post_meta_key, 0, 8 ) ) | |
delete_post_meta( $post_ID, $post_meta_key ); | |
} | |
} | |
/** | |
* Triggers a caching of all oEmbed results. | |
* | |
* @param int $post_ID Post ID to do the caching for. | |
*/ | |
public function cache_oembed( $post_ID ) { | |
$post = get_post( $post_ID ); | |
$post_types = get_post_types( array( 'show_ui' => true ) ); | |
/** | |
* Filter the array of post types to cache oEmbed results for. | |
* | |
* @since 2.9.0 | |
* | |
* @param array $post_types Array of post types to cache oEmbed results for. Defaults to post types with `show_ui` set to true. | |
*/ | |
if ( empty( $post->ID ) || ! in_array( $post->post_type, apply_filters( 'embed_cache_oembed_types', $post_types ) ) ){ | |
return; | |
} | |
// Trigger a caching | |
if ( ! empty( $post->post_content ) ) { | |
$this->post_ID = $post->ID; | |
$this->usecache = false; | |
$content = $this->run_shortcode( $post->post_content ); | |
$this->autoembed( $content ); | |
$this->usecache = true; | |
} | |
} | |
/** | |
* Passes any unlinked URLs that are on their own line to {@link WP_Embed::shortcode()} for potential embedding. | |
* | |
* @uses WP_Embed::autoembed_callback() | |
* | |
* @param string $content The content to be searched. | |
* @return string Potentially modified $content. | |
*/ | |
public function autoembed( $content ) { | |
return preg_replace_callback( '|^\s*(https?://[^\s"]+)\s*$|im', array( $this, 'autoembed_callback' ), $content ); | |
} | |
/** | |
* Callback function for {@link WP_Embed::autoembed()}. | |
* | |
* @param array $match A regex match array. | |
* @return string The embed HTML on success, otherwise the original URL. | |
*/ | |
public function autoembed_callback( $match ) { | |
$oldval = $this->linkifunknown; | |
$this->linkifunknown = false; | |
$return = $this->shortcode( array(), $match[1] ); | |
$this->linkifunknown = $oldval; | |
return "\n$return\n"; | |
} | |
/** | |
* Conditionally makes a hyperlink based on an internal class variable. | |
* | |
* @param string $url URL to potentially be linked. | |
* @return false|string Linked URL or the original URL. False if 'return_false_on_fail' is true. | |
*/ | |
public function maybe_make_link( $url ) { | |
if ( $this->return_false_on_fail ) { | |
return false; | |
} | |
$output = ( $this->linkifunknown ) ? '<a href="' . esc_url($url) . '">' . esc_html($url) . '</a>' : $url; | |
/** | |
* Filter the returned, maybe-linked embed URL. | |
* | |
* @since 2.9.0 | |
* | |
* @param string $output The linked or original URL. | |
* @param string $url The original URL. | |
*/ | |
return apply_filters( 'embed_maybe_make_link', $output, $url ); | |
} | |
} | |
$GLOBALS['wp_embed'] = new WP_Embed(); |
run_shortcode()
registers a placeholder shortcode function, and then before wpautop
runs, it disables all registered shortcodes, adds its real function, runs the shortcode processor with just that code enabled, and then re-enables all shortcodes including the dummy function.
Do you get an inkling that something is seriously wrong here?
<pre>
is the umbrella to wpautop
ee
Well other source code rendering tools don’t have this problem. For example, the code
shortcode doesn’t get wpautop
eed on. This code is in the SyntaxHighliter Evolved and is the same code that powers WordPress.com’s shortcode handling.
How does it work it’s magic?
<?php /* | |
************************************************************************** | |
Plugin Name: SyntaxHighlighter Evolved | |
Plugin URI: http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/ | |
Version: 3.1.11 | |
Description: Easily post syntax-highlighted code to your site without having to modify the code at all. Uses Alex Gorbatchev's <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter">SyntaxHighlighter</a>. <strong>TIP:</strong> Don't use the Visual editor if you don't want your code mangled. TinyMCE will "clean up" your HTML. | |
Author: Alex Mills (Viper007Bond) | |
Author URI: http://www.viper007bond.com/ | |
************************************************************************** | |
Thanks to: | |
* Alex Gorbatchev for writing the Javascript-powered synatax highlighter script | |
* Andrew Ozz for writing the TinyMCE plugin | |
**************************************************************************/ | |
class SyntaxHighlighter { | |
// All of these variables are private. Filters are provided for things that can be modified. | |
var $pluginver = '3.1.11'; // Plugin version | |
var $agshver = false; // Alex Gorbatchev's SyntaxHighlighter version (dynamically set below due to v2 vs v3) | |
var $shfolder = false; // Controls what subfolder to load SyntaxHighlighter from (v2 or v3) | |
var $settings = array(); // Contains the user's settings | |
var $defaultsettings = array(); // Contains the default settings | |
var $brushes = array(); // Array of aliases => brushes | |
var $shortcodes = array(); // Array of shortcodes to use | |
var $themes = array(); // Array of themes | |
var $usedbrushes = array(); // Stores used brushes so we know what to output | |
var $encoded = false; // Used to mark that a character encode took place | |
var $codeformat = false; // If set, SyntaxHighlighter::get_code_format() will return this value | |
var $content_save_pre_ran = false; // It's possible for the "content_save_pre" filter to run multiple times, so keep track | |
// Initalize the plugin by registering the hooks | |
function __construct() { | |
if ( ! function_exists( 'esc_html' ) ) | |
return; | |
// Load localization domain | |
load_plugin_textdomain( 'syntaxhighlighter', false, '/syntaxhighlighter/localization' ); | |
// 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_head', array( $this, 'output_header_placeholder' ), 15 ); | |
add_action( 'admin_head', array( $this, 'output_header_placeholder' ), 15 ); // For comments | |
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 ); | |
// Register widget hooks | |
// Requires change added in WordPress 2.9 | |
if ( class_exists('WP_Embed') ) { | |
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 | |
) ); | |
// 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'; | |
} | |
// 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-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-groovy', plugins_url( $this->shfolder . '/scripts/shBrushGroovy.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-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 ); | |
// 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 ); | |
// 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', | |
'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', | |
'groovy' => 'groovy', | |
'java' => 'java', | |
'jfx' => 'javafx', | |
'javafx' => 'javafx', | |
'js' => 'jscript', | |
'jscript' => 'jscript', | |
'javascript' => 'jscript', | |
'latex' => 'latex', // Not used as a shortcode | |
'tex' => 'latex', | |
'matlab' => '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', | |
'vb' => 'vb', | |
'vbnet' => 'vb', | |
'xml' => 'xml', | |
'xhtml' => 'xml', | |
'xslt' => 'xml', | |
'html' => 'xml', | |
) ); | |
// 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_true' ); | |
// 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' ) ); | |
} | |
// Add the custom TinyMCE plugin which wraps plugin shortcodes in <pre> 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[] = '<a href="' . admin_url( 'options-general.php?page=syntaxhighlighter' ) . '">' . __( 'Settings', 'syntaxhighlighter' ) . '</a>'; | |
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 "<script type='text/javascript'>\n"; | |
echo " var syntaxHLcodes = '" . implode( '|', $shortcodes ) . "';\n"; | |
echo "</script>\n"; | |
} | |
// A filter function that runs do_shortcode() but only with this plugin's shortcodes | |
function shortcode_hack( $content, $callback ) { | |
global $shortcode_tags; | |
// Backup current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
// Register all of this plugin's shortcodes | |
foreach ( $this->shortcodes as $shortcode ) | |
add_shortcode( $shortcode, $callback ); | |
// Do the shortcodes (only this plugins's are registered) | |
$content = $this->do_shortcode_keep_escaped_tags( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
// This is a clone of do_shortcode() that uses a different callback function | |
// The new callback function will keep escaped tags escaped, i.e. [[foo]] | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_keep_escaped_tags( $content ) { | |
global $shortcode_tags; | |
if (empty($shortcode_tags) || !is_array($shortcode_tags)) | |
return $content; | |
$pattern = get_shortcode_regex(); | |
return preg_replace_callback('/'.$pattern.'/s', array( $this, 'do_shortcode_tag_keep_escaped_tags' ), $content); | |
} | |
// Callback for above do_shortcode_keep_escaped_tags() function | |
// It's a clone of core's do_shortcode_tag() function with a modification to the escaped shortcode return | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_tag_keep_escaped_tags( $m ) { | |
global $shortcode_tags; | |
// allow [[foo]] syntax for escaping a tag | |
if ( $m[1] == '[' && $m[6] == ']' ) { | |
return $m[0]; // This line was modified for this plugin (no substr call) | |
} | |
$tag = $m[2]; | |
$attr = shortcode_parse_atts( $m[3] ); | |
if ( isset( $m[5] ) ) { | |
// enclosing tag - extra parameter | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; | |
} else { | |
// self-closing tag | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, NULL, $tag ) . $m[6]; | |
} | |
} | |
// 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' ) ); | |
} | |
// 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) | |
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' ) ); | |
} | |
// 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 an anchor in the header for the Javascript to use. | |
// In the <head>, we don't know if we'll need this plugin's CSS and JavaScript yet but we will in the footer. | |
function output_header_placeholder() { | |
echo '<style type="text/css" id="syntaxhighlighteranchor"></style>' . "\n"; | |
} | |
// 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 "<script type='text/javascript'>\n"; | |
echo " (function(){\n"; | |
echo " var corecss = document.createElement('link');\n"; | |
echo " var themecss = document.createElement('link');\n"; | |
if ( !is_a($wp_styles, 'WP_Styles') ) | |
$wp_styles = new WP_Styles(); | |
$needcore = false; | |
if ( 'none' == $this->settings['theme'] ) { | |
$needcore = true; | |
} else { | |
$theme = ( !empty($this->themes[$this->settings['theme']]) ) ? strtolower($this->settings['theme']) : $this->defaultsettings['theme']; | |
$theme = 'syntaxhighlighter-theme-' . $theme; | |
// See if the requested theme has been registered | |
if ( !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered[$theme]) && !empty($wp_styles->registered[$theme]->src) ) { | |
// Users can register their own stylesheet and may opt to not load the core stylesheet if they wish for some reason | |
if ( is_array($wp_styles->registered[$theme]->deps) && in_array( 'syntaxhighlighter-core', $wp_styles->registered[$theme]->deps ) ) | |
$needcore = true; | |
} | |
// Otherwise use the default theme | |
else { | |
$theme = 'syntaxhighlighter-theme-' . $this->defaultsettings['theme']; | |
$needcore = true; | |
} | |
} | |
if ( $needcore && !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered['syntaxhighlighter-core']) && !empty($wp_styles->registered['syntaxhighlighter-core']->src) ) : | |
$corecssurl = add_query_arg( 'ver', $this->agshver, $wp_styles->registered['syntaxhighlighter-core']->src ); | |
$corecssurl = apply_filters( 'syntaxhighlighter_csscoreurl', $corecssurl ); | |
?> | |
var corecssurl = "<?php echo esc_js( $corecssurl ); ?>"; | |
if ( corecss.setAttribute ) { | |
corecss.setAttribute( "rel", "stylesheet" ); | |
corecss.setAttribute( "type", "text/css" ); | |
corecss.setAttribute( "href", corecssurl ); | |
} else { | |
corecss.rel = "stylesheet"; | |
corecss.href = corecssurl; | |
} | |
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif $needcore | |
if ( 'none' != $this->settings['theme'] ) : ?> | |
var themecssurl = "<?php echo esc_js( apply_filters( 'syntaxhighlighter_cssthemeurl', add_query_arg( 'ver', $this->agshver, $wp_styles->registered[$theme]->src ) ) ); ?>"; | |
if ( themecss.setAttribute ) { | |
themecss.setAttribute( "rel", "stylesheet" ); | |
themecss.setAttribute( "type", "text/css" ); | |
themecss.setAttribute( "href", themecssurl ); | |
} else { | |
themecss.rel = "stylesheet"; | |
themecss.href = themecssurl; | |
} | |
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss); | |
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif none != theme | |
echo " })();\n"; | |
switch ( $this->settings['shversion'] ) { | |
case 2: | |
echo " SyntaxHighlighter.config.clipboardSwf = '" . esc_js( apply_filters( 'syntaxhighlighter_clipboardurl', plugins_url( 'syntaxhighlighter2/scripts/clipboard.swf', __FILE__ ) ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( 'show source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.viewSource = '" . $this->js_escape_singlequotes( __( 'view source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboard = '" . $this->js_escape_singlequotes( __( 'copy to clipboard', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboardConfirmation = '" . $this->js_escape_singlequotes( __( 'The code is in your clipboard now', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.print = '" . $this->js_escape_singlequotes( __( 'print', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
case 3: | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( '+ expand source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
} | |
if ( 1 != $this->settings['autolinks'] ) | |
echo " SyntaxHighlighter.defaults['auto-links'] = false;\n"; | |
if ( !empty($this->settings['classname']) ) | |
echo " SyntaxHighlighter.defaults['class-name'] = '" . $this->js_escape_singlequotes( $this->settings['classname'] ) . "';\n"; | |
if ( 1 == $this->settings['collapse'] ) | |
echo " SyntaxHighlighter.defaults['collapse'] = true;\n"; | |
if ( 1 != $this->settings['firstline'] ) | |
echo " SyntaxHighlighter.defaults['first-line'] = " . $this->settings['firstline'] . ";\n"; | |
if ( 1 != $this->settings['gutter'] ) | |
echo " SyntaxHighlighter.defaults['gutter'] = false;\n"; | |
/* | |
if ( 1 == $this->settings['htmlscript'] ) | |
echo " SyntaxHighlighter.defaults['html-script'] = true;\n"; | |
*/ | |
if ( 1 == $this->settings['light'] ) | |
echo " SyntaxHighlighter.defaults['light'] = true;\n"; | |
echo " SyntaxHighlighter.defaults['pad-line-numbers'] = "; | |
switch ( $this->settings['padlinenumbers'] ) { | |
case 'true': | |
echo 'true'; | |
break; | |
case 'false'; | |
echo 'false'; | |
break; | |
default; | |
echo (int) $this->settings['padlinenumbers']; | |
} | |
echo ";\n"; | |
if ( 1 != $this->settings['smarttabs'] ) | |
echo " SyntaxHighlighter.defaults['smart-tabs'] = false;\n"; | |
if ( 4 != $this->settings['tabsize'] ) | |
echo " SyntaxHighlighter.defaults['tab-size'] = " . $this->settings['tabsize'] . ";\n"; | |
if ( 1 != $this->settings['toolbar'] ) | |
echo " SyntaxHighlighter.defaults['toolbar'] = false;\n"; | |
// 2.x only for now | |
if ( 1 != $this->settings['wraplines'] ) | |
echo " SyntaxHighlighter.defaults['wrap-lines'] = false;\n"; | |
?> SyntaxHighlighter.all(); | |
</script> | |
<?php | |
} | |
// No-name attribute fixing | |
function attributefix( $atts = array() ) { | |
if ( empty($atts[0]) ) | |
return $atts; | |
// Quoted value | |
if ( 0 !== preg_match( '#=("|\')(.*?)\1#', $atts[0], $match ) ) | |
$atts[0] = $match[2]; | |
// Unquoted value | |
elseif ( '=' == substr( $atts[0], 0, 1 ) ) | |
$atts[0] = substr( $atts[0], 1 ); | |
return $atts; | |
} | |
// Shortcode handler for transforming the shortcodes to their final <pre>'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, | |
), $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', | |
); | |
// 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', | |
) ); | |
$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 ); | |
$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', '<pre class="' . esc_attr( implode( ' ', $params ) ) . '"' . $title . '>' . $code . '</pre>' );; | |
} | |
// Settings page | |
function settings_page() { ?> | |
<script type="text/javascript"> | |
// <![CDATA[ | |
jQuery(document).ready(function($) { | |
// Confirm pressing of the "Reset to Defaults" button | |
$("#syntaxhighlighter-defaults").click(function(){ | |
var areyousure = confirm("<?php echo esc_js( __( 'Are you sure you want to reset your settings to the defaults?', 'syntaxhighlighter' ) ); ?>"); | |
if ( true != areyousure ) return false; | |
}); | |
<?php if ( !empty( $_GET['defaults'] ) ) : ?> | |
$("#message p strong").text("<?php echo esc_js( __( 'Settings reset to defaults.', 'syntaxhighlighter' ) ); ?>"); | |
<?php endif; ?> | |
}); | |
// ]]> | |
</script> | |
<div class="wrap"> | |
<?php if ( function_exists('screen_icon') ) screen_icon(); ?> | |
<h2><?php _e( 'SyntaxHighlighter Settings', 'syntaxhighlighter' ); ?></h2> | |
<form method="post" action="options.php"> | |
<?php settings_fields('syntaxhighlighter_settings'); ?> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-shversion"><?php _e( 'Highlighter Version', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[shversion]" id="syntaxhighlighter-shversion" class="postform"> | |
<?php | |
$versions = array( | |
3 => __( 'Version 3.x', 'syntaxhighlighter' ), | |
2 => __( 'Version 2.x', 'syntaxhighlighter' ), | |
); | |
foreach ( $versions as $version => $name ) { | |
echo ' <option value="' . esc_attr( $version ) . '"' . selected( $this->settings['shversion'], $version, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select><br /> | |
<?php _e( 'Version 3 allows visitors to easily highlight portions of your code with their mouse (either by dragging or double-clicking) and copy it to their clipboard. No toolbar containing a Flash-based button is required.', 'syntaxhighlighter' ); ?><br /> | |
<?php _e( 'Version 2 allows for line wrapping, something that version 3 does not do at this time.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-theme"><?php _e( 'Color Theme', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[theme]" id="syntaxhighlighter-theme" class="postform"> | |
<?php | |
foreach ( $this->themes as $theme => $name ) { | |
echo ' <option value="' . esc_attr( $theme ) . '"' . selected( $this->settings['theme'], $theme, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-loadallbrushes"><input name="syntaxhighlighter_settings[loadallbrushes]" type="checkbox" id="syntaxhighlighter-loadallbrushes" value="1" <?php checked( $this->settings['loadallbrushes'], 1 ); ?> /> <?php _e( 'Always load all language files (for directly using <code><pre></code> tags rather than shortcodes)<br /> If left unchecked (default), then language files will only be loaded when needed<br /> If unsure, leave this box unchecked', 'syntaxhighlighter' ); ?></label> | |
</fieldset> | |
</td> | |
</tr> | |
</table> | |
<h3><?php _e( 'Defaults', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'All of the settings below can be configured on a per-code block basis, but you can control the defaults of all code blocks here.', 'syntaxhighlighter' ); ?></p> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-gutter"><input name="syntaxhighlighter_settings[gutter]" type="checkbox" id="syntaxhighlighter-gutter" value="1" <?php checked( $this->settings['gutter'], 1 ); ?> /> <?php _e( 'Display line numbers', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-toolbar"><input name="syntaxhighlighter_settings[toolbar]" type="checkbox" id="syntaxhighlighter-toolbar" value="1" <?php checked( $this->settings['toolbar'], 1 ); ?> /> <?php _e( 'Display the toolbar', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-autolinks"><input name="syntaxhighlighter_settings[autolinks]" type="checkbox" id="syntaxhighlighter-autolinks" value="1" <?php checked( $this->settings['autolinks'], 1 ); ?> /> <?php _e( 'Automatically make URLs clickable', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-collapse"><input name="syntaxhighlighter_settings[collapse]" type="checkbox" id="syntaxhighlighter-collapse" value="1" <?php checked( $this->settings['collapse'], 1 ); ?> /> <?php _e( 'Collapse code boxes', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-light"><input name="syntaxhighlighter_settings[light]" type="checkbox" id="syntaxhighlighter-light" value="1" <?php checked( $this->settings['light'], 1 ); ?> /> <?php _e( 'Use the light display mode, best for single lines of code', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-smarttabs"><input name="syntaxhighlighter_settings[smarttabs]" type="checkbox" id="syntaxhighlighter-smarttabs" value="1" <?php checked( $this->settings['smarttabs'], 1 ); ?> /> <?php _e( 'Use smart tabs allowing tabs being used for alignment', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-wraplines"><input name="syntaxhighlighter_settings[wraplines]" type="checkbox" id="syntaxhighlighter-wraplines" value="1" <?php checked( $this->settings['wraplines'], 1 ); ?> /> <?php _e( 'Wrap long lines (v2.x only, disabling this will make a scrollbar show instead)', 'syntaxhighlighter' ); ?></label><br /> | |
<!--<label for="syntaxhighlighter-htmlscript"><input name="syntaxhighlighter_settings[htmlscript]" type="checkbox" id="syntaxhighlighter-htmlscript" value="1" <?php checked( $this->settings['htmlscript'], 1 ); ?> /> <?php _e( 'Enable "HTML script" mode by default (see the bottom of this page for details). Checking this box is not recommended as this mode only works with certain languages.', 'syntaxhighlighter' ); ?></label>--> | |
</fieldset> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-classname"><?php _e( 'Additional CSS Class(es)', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[classname]" type="text" id="syntaxhighlighter-classname" value="<?php echo esc_attr( $this->settings['classname'] ); ?>" class="regular-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-firstline"><?php _e( 'Starting Line Number', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[firstline]" type="text" id="syntaxhighlighter-firstline" value="<?php echo esc_attr( $this->settings['firstline'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-padlinenumbers"><?php _e( 'Line Number Padding', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[padlinenumbers]" id="syntaxhighlighter-padlinenumbers" class="postform"> | |
<?php | |
$linepaddings = array( | |
'false' => __( 'Off', 'syntaxhighlighter' ), | |
'true' => __( 'Automatic', 'syntaxhighlighter' ), | |
1 => 1, | |
2 => 2, | |
3 => 3, | |
4 => 4, | |
5 => 5, | |
); | |
foreach ( $linepaddings as $value => $name ) { | |
echo ' <option value="' . esc_attr( $value ) . '"' . selected( $this->settings['padlinenumbers'], $value, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-tabsize"><?php _e( 'Tab Size', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[tabsize]" type="text" id="syntaxhighlighter-tabsize" value="<?php echo esc_attr( $this->settings['tabsize'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-title"><?php _e( 'Title', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<input name="syntaxhighlighter_settings[title]" type="text" id="syntaxhighlighter-title" value="<?php echo esc_attr( $this->settings['title'] ); ?>" class="regular-text" /><br /> | |
<?php _e( 'Some optional default text to display above each code block or as the clickable text for collapsed code blocks.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
</table> | |
<p class="submit"> | |
<?php | |
if ( function_exists( 'submit_button' ) ) { | |
submit_button( null, 'primary', 'syntaxhighlighter-submit', false ); | |
echo ' '; | |
submit_button( __( 'Reset to Defaults', 'syntaxhighlighter' ), 'primary', 'syntaxhighlighter-defaults', false ); | |
} else { | |
echo '<input type="submit" name="syntaxhighlighter-submit" class="button-primary" value="' . __( 'Save Changes') . '" />' . "\n"; | |
echo '<input type="submit" name="syntaxhighlighter-defaults" id="syntaxhighlighter-defaults" class="button-primary" value="' . __( 'Reset to Defaults', 'syntaxhighlighter' ) . '" />' . "\n"; | |
} | |
?> | |
</p> | |
</form> | |
<h3><?php _e( 'Preview', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'Click "Save Changes" to update this preview.', 'syntaxhighlighter' ); ?> | |
<?php | |
echo '<div'; | |
if ( ! empty( $GLOBALS['content_width'] ) ) | |
echo ' style="max-width:' . intval( $GLOBALS['content_width'] ) . 'px"'; | |
echo '>'; | |
$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 . ']<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>PHP Code Example</title> | |
</head> | |
<body> | |
<h1>' . __( 'PHP Code Example', 'syntaxhighlighter' ) . '</h1> | |
<p><?php echo \'' . __( 'Hello World!', 'syntaxhighlighter' ) . '\'; ?></p> | |
<p>' . __( 'This line is highlighted.', 'syntaxhighlighter' ) . '</p> | |
<div class="foobar"> | |
' . __( ' This is an | |
example of smart | |
tabs.', 'syntaxhighlighter' ) . ' | |
</div> | |
<p><a href="http://wordpress.org/">' . __( 'WordPress' ) . '</a></p> | |
</body> | |
</html>[/sourcecode]' ); | |
$this->codeformat = 1; | |
echo $this->parse_shortcodes( $democode ); | |
$this->codeformat = false; | |
echo '</div>'; | |
?> | |
<h3 style="margin-top:30px"><?php _e( 'Shortcode Parameters', 'syntaxhighlighter' ); ?></h3> | |
<p><?php printf( __( 'These are the parameters you can pass to the shortcode and what they do. For the booleans (i.e. on/off), pass %1$s/%2$s or %3$s/%4$s.', 'syntaxhighlighter' ), '<code>true</code>', '<code>1</code>', '<code>false</code>', '<code>0</code>' ); ?></p> | |
<ul class="ul-disc"> | |
<li><?php printf( _x( '%1$s or %2$s — The language syntax to highlight with. You can alternately just use that as the tag, such as <code>[php]code[/php]</code>. <a href="%3$s">Click here</a> for a list of valid tags (under "aliases").', 'language parameter', 'syntaxhighlighter' ), '<code>lang</code>', '<code>language</code>', 'http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle automatic URL linking.', 'autolinks parameter', 'syntaxhighlighter' ), '<code>autolinks</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Add an additional CSS class to the code box.', 'classname parameter', 'syntaxhighlighter' ), '<code>classname</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle collapsing the code box by default, requiring a click to expand it. Good for large code posts.', 'collapse parameter', 'syntaxhighlighter' ), '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — An interger specifying what number the first line should be (for the line numbering).', 'firstline parameter', 'syntaxhighlighter' ), '<code>firstline</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the left-side line numbering.', 'gutter parameter', 'syntaxhighlighter' ), '<code>gutter</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s — A comma-separated list of line numbers to highlight. You can also specify a range. Example: %2$s', 'highlight parameter', 'syntaxhighlighter' ), '<code>highlight</code>', '<code>2,5-10,12</code>' ); ?></li> | |
<li><?php printf( _x( "%s — Toggle highlighting any extra HTML/XML. Good for when you're mixing HTML/XML with another language, such as having PHP inside an HTML web page. The above preview has it enabled for example. This only works with certain languages.", 'htmlscript parameter', 'syntaxhighlighter' ), '<code>htmlscript</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle light mode which disables the gutter and toolbar all at once.', 'light parameter', 'syntaxhighlighter' ), '<code>light</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Controls line number padding. Valid values are <code>false</code> (no padding), <code>true</code> (automatic padding), or an integer (forced padding).', 'padlinenumbers parameter', 'syntaxhighlighter' ), '<code>padlinenumbers</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s (v3 only) — Sets some text to show up before the code. Very useful when combined with the %2$s parameter.', 'title parameter', 'syntaxhighlighter' ), '<code>title</code>', '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the toolbar (buttons in v2, the about question mark in v3)', 'toolbar parameter', 'syntaxhighlighter' ), '<code>toolbar</code>' ); ?></li> | |
<li><?php printf( _x( '%s (v2 only) — Toggle line wrapping.', 'wraplines parameter', 'syntaxhighlighter'), '<code>wraplines</code>' ); ?></li> | |
</ul> | |
<p><?php _e( 'Some example shortcodes:', 'syntaxhighlighter' ); ?></p> | |
<ul class="ul-disc"> | |
<li><code>[php]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/php]</code></li> | |
<li><code>[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="<?php _e( 'example-filename.php', 'syntaxhighlighter' ); ?>"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/css]</code></li> | |
<li><code>[code lang="js"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/code]</code></li> | |
<li><code>[sourcecode language="plain"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/sourcecode]</code></li> | |
</ul> | |
<?php $this->maybe_output_scripts(); ?> | |
</div> | |
<?php | |
} | |
// Validate the settings sent from the settings page | |
function validate_settings( $settings ) { | |
if ( !empty($_POST['syntaxhighlighter-defaults']) ) { | |
$settings = $this->defaultsettings; | |
$_REQUEST['_wp_http_referer'] = add_query_arg( 'defaults', 'true', $_REQUEST['_wp_http_referer'] ); | |
} else { | |
$settings['shversion'] = ( ! empty($settings['shversion']) && 2 == $settings['shversion'] ) ? 2 : 3; | |
$settings['theme'] = ( ! empty($settings['theme']) && isset($this->themes[$settings['theme']]) ) ? strtolower($settings['theme']) : $this->defaultsettings['theme']; | |
$settings['loadallbrushes'] = ( ! empty($settings['loadallbrushes']) ) ? 1 : 0; | |
$settings['autolinks'] = ( ! empty($settings['autolinks']) ) ? 1 : 0; | |
$settings['collapse'] = ( ! empty($settings['collapse']) ) ? 1 : 0; | |
$settings['gutter'] = ( ! empty($settings['gutter']) ) ? 1 : 0; | |
$settings['light'] = ( ! empty($settings['light']) ) ? 1 : 0; | |
$settings['smarttabs'] = ( ! empty($settings['smarttabs']) ) ? 1 : 0; | |
$settings['toolbar'] = ( ! empty($settings['toolbar']) ) ? 1 : 0; // May be overridden below | |
$settings['wraplines'] = ( ! empty($settings['wraplines']) ) ? 1 : 0; // 2.x only for now | |
// If the version changed, then force change the toolbar version setting | |
if ( $settings['shversion'] != $this->settings['shversion'] ) { | |
$settings['toolbar'] = ( 2 == $settings['shversion'] ) ? 1 : 0; | |
} | |
if ( 'true' != $settings['padlinenumbers'] && 'false' != $settings['padlinenumbers'] ) | |
$settings['padlinenumbers'] = (int) $settings['padlinenumbers']; | |
$settings['classname'] = ( !empty($settings['classname']) ) ? preg_replace( '/[^ A-Za-z0-9_-]*/', '', $settings['classname'] ) : ''; | |
$settings['firstline'] = (int) ( !empty($settings['firstline']) ) ? $settings['firstline'] : $this->defaultsettings['firstline']; | |
$settings['tabsize'] = (int) ( !empty($settings['tabsize']) ) ? $settings['tabsize'] : $this->defaultsettings['tabsize']; | |
} | |
return $settings; | |
} | |
// PHP4 compatibility | |
function SyntaxHighlighter() { | |
$this->__construct(); | |
} | |
} | |
// Start this plugin once all other plugins are fully loaded | |
add_action( 'init', 'SyntaxHighlighter', 5 ); | |
function SyntaxHighlighter() { | |
global $SyntaxHighlighter; | |
$SyntaxHighlighter = new SyntaxHighlighter(); | |
} | |
?> |
<?php /* | |
************************************************************************** | |
Plugin Name: SyntaxHighlighter Evolved | |
Plugin URI: http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/ | |
Version: 3.1.11 | |
Description: Easily post syntax-highlighted code to your site without having to modify the code at all. Uses Alex Gorbatchev's <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter">SyntaxHighlighter</a>. <strong>TIP:</strong> Don't use the Visual editor if you don't want your code mangled. TinyMCE will "clean up" your HTML. | |
Author: Alex Mills (Viper007Bond) | |
Author URI: http://www.viper007bond.com/ | |
************************************************************************** | |
Thanks to: | |
* Alex Gorbatchev for writing the Javascript-powered synatax highlighter script | |
* Andrew Ozz for writing the TinyMCE plugin | |
**************************************************************************/ | |
class SyntaxHighlighter { | |
// All of these variables are private. Filters are provided for things that can be modified. | |
var $pluginver = '3.1.11'; // Plugin version | |
var $agshver = false; // Alex Gorbatchev's SyntaxHighlighter version (dynamically set below due to v2 vs v3) | |
var $shfolder = false; // Controls what subfolder to load SyntaxHighlighter from (v2 or v3) | |
var $settings = array(); // Contains the user's settings | |
var $defaultsettings = array(); // Contains the default settings | |
var $brushes = array(); // Array of aliases => brushes | |
var $shortcodes = array(); // Array of shortcodes to use | |
var $themes = array(); // Array of themes | |
var $usedbrushes = array(); // Stores used brushes so we know what to output | |
var $encoded = false; // Used to mark that a character encode took place | |
var $codeformat = false; // If set, SyntaxHighlighter::get_code_format() will return this value | |
var $content_save_pre_ran = false; // It's possible for the "content_save_pre" filter to run multiple times, so keep track | |
// Initalize the plugin by registering the hooks | |
function __construct() { | |
if ( ! function_exists( 'esc_html' ) ) | |
return; | |
// Load localization domain | |
load_plugin_textdomain( 'syntaxhighlighter', false, '/syntaxhighlighter/localization' ); | |
// 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_head', array( $this, 'output_header_placeholder' ), 15 ); | |
add_action( 'admin_head', array( $this, 'output_header_placeholder' ), 15 ); // For comments | |
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 ); | |
// Register widget hooks | |
// Requires change added in WordPress 2.9 | |
if ( class_exists('WP_Embed') ) { | |
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 | |
) ); | |
// 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'; | |
} | |
// 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-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-groovy', plugins_url( $this->shfolder . '/scripts/shBrushGroovy.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-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 ); | |
// 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 ); | |
// 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', | |
'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', | |
'groovy' => 'groovy', | |
'java' => 'java', | |
'jfx' => 'javafx', | |
'javafx' => 'javafx', | |
'js' => 'jscript', | |
'jscript' => 'jscript', | |
'javascript' => 'jscript', | |
'latex' => 'latex', // Not used as a shortcode | |
'tex' => 'latex', | |
'matlab' => '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', | |
'vb' => 'vb', | |
'vbnet' => 'vb', | |
'xml' => 'xml', | |
'xhtml' => 'xml', | |
'xslt' => 'xml', | |
'html' => 'xml', | |
) ); | |
// 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_true' ); | |
// 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' ) ); | |
} | |
// Add the custom TinyMCE plugin which wraps plugin shortcodes in <pre> 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[] = '<a href="' . admin_url( 'options-general.php?page=syntaxhighlighter' ) . '">' . __( 'Settings', 'syntaxhighlighter' ) . '</a>'; | |
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 "<script type='text/javascript'>\n"; | |
echo " var syntaxHLcodes = '" . implode( '|', $shortcodes ) . "';\n"; | |
echo "</script>\n"; | |
} | |
// A filter function that runs do_shortcode() but only with this plugin's shortcodes | |
function shortcode_hack( $content, $callback ) { | |
global $shortcode_tags; | |
// Backup current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
// Register all of this plugin's shortcodes | |
foreach ( $this->shortcodes as $shortcode ) | |
add_shortcode( $shortcode, $callback ); | |
// Do the shortcodes (only this plugins's are registered) | |
$content = $this->do_shortcode_keep_escaped_tags( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
// This is a clone of do_shortcode() that uses a different callback function | |
// The new callback function will keep escaped tags escaped, i.e. [[foo]] | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_keep_escaped_tags( $content ) { | |
global $shortcode_tags; | |
if (empty($shortcode_tags) || !is_array($shortcode_tags)) | |
return $content; | |
$pattern = get_shortcode_regex(); | |
return preg_replace_callback('/'.$pattern.'/s', array( $this, 'do_shortcode_tag_keep_escaped_tags' ), $content); | |
} | |
// Callback for above do_shortcode_keep_escaped_tags() function | |
// It's a clone of core's do_shortcode_tag() function with a modification to the escaped shortcode return | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_tag_keep_escaped_tags( $m ) { | |
global $shortcode_tags; | |
// allow [[foo]] syntax for escaping a tag | |
if ( $m[1] == '[' && $m[6] == ']' ) { | |
return $m[0]; // This line was modified for this plugin (no substr call) | |
} | |
$tag = $m[2]; | |
$attr = shortcode_parse_atts( $m[3] ); | |
if ( isset( $m[5] ) ) { | |
// enclosing tag - extra parameter | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; | |
} else { | |
// self-closing tag | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, NULL, $tag ) . $m[6]; | |
} | |
} | |
// 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' ) ); | |
} | |
// 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) | |
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' ) ); | |
} | |
// 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 an anchor in the header for the Javascript to use. | |
// In the <head>, we don't know if we'll need this plugin's CSS and JavaScript yet but we will in the footer. | |
function output_header_placeholder() { | |
echo '<style type="text/css" id="syntaxhighlighteranchor"></style>' . "\n"; | |
} | |
// 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 "<script type='text/javascript'>\n"; | |
echo " (function(){\n"; | |
echo " var corecss = document.createElement('link');\n"; | |
echo " var themecss = document.createElement('link');\n"; | |
if ( !is_a($wp_styles, 'WP_Styles') ) | |
$wp_styles = new WP_Styles(); | |
$needcore = false; | |
if ( 'none' == $this->settings['theme'] ) { | |
$needcore = true; | |
} else { | |
$theme = ( !empty($this->themes[$this->settings['theme']]) ) ? strtolower($this->settings['theme']) : $this->defaultsettings['theme']; | |
$theme = 'syntaxhighlighter-theme-' . $theme; | |
// See if the requested theme has been registered | |
if ( !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered[$theme]) && !empty($wp_styles->registered[$theme]->src) ) { | |
// Users can register their own stylesheet and may opt to not load the core stylesheet if they wish for some reason | |
if ( is_array($wp_styles->registered[$theme]->deps) && in_array( 'syntaxhighlighter-core', $wp_styles->registered[$theme]->deps ) ) | |
$needcore = true; | |
} | |
// Otherwise use the default theme | |
else { | |
$theme = 'syntaxhighlighter-theme-' . $this->defaultsettings['theme']; | |
$needcore = true; | |
} | |
} | |
if ( $needcore && !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered['syntaxhighlighter-core']) && !empty($wp_styles->registered['syntaxhighlighter-core']->src) ) : | |
$corecssurl = add_query_arg( 'ver', $this->agshver, $wp_styles->registered['syntaxhighlighter-core']->src ); | |
$corecssurl = apply_filters( 'syntaxhighlighter_csscoreurl', $corecssurl ); | |
?> | |
var corecssurl = "<?php echo esc_js( $corecssurl ); ?>"; | |
if ( corecss.setAttribute ) { | |
corecss.setAttribute( "rel", "stylesheet" ); | |
corecss.setAttribute( "type", "text/css" ); | |
corecss.setAttribute( "href", corecssurl ); | |
} else { | |
corecss.rel = "stylesheet"; | |
corecss.href = corecssurl; | |
} | |
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif $needcore | |
if ( 'none' != $this->settings['theme'] ) : ?> | |
var themecssurl = "<?php echo esc_js( apply_filters( 'syntaxhighlighter_cssthemeurl', add_query_arg( 'ver', $this->agshver, $wp_styles->registered[$theme]->src ) ) ); ?>"; | |
if ( themecss.setAttribute ) { | |
themecss.setAttribute( "rel", "stylesheet" ); | |
themecss.setAttribute( "type", "text/css" ); | |
themecss.setAttribute( "href", themecssurl ); | |
} else { | |
themecss.rel = "stylesheet"; | |
themecss.href = themecssurl; | |
} | |
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss); | |
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif none != theme | |
echo " })();\n"; | |
switch ( $this->settings['shversion'] ) { | |
case 2: | |
echo " SyntaxHighlighter.config.clipboardSwf = '" . esc_js( apply_filters( 'syntaxhighlighter_clipboardurl', plugins_url( 'syntaxhighlighter2/scripts/clipboard.swf', __FILE__ ) ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( 'show source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.viewSource = '" . $this->js_escape_singlequotes( __( 'view source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboard = '" . $this->js_escape_singlequotes( __( 'copy to clipboard', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboardConfirmation = '" . $this->js_escape_singlequotes( __( 'The code is in your clipboard now', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.print = '" . $this->js_escape_singlequotes( __( 'print', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
case 3: | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( '+ expand source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
} | |
if ( 1 != $this->settings['autolinks'] ) | |
echo " SyntaxHighlighter.defaults['auto-links'] = false;\n"; | |
if ( !empty($this->settings['classname']) ) | |
echo " SyntaxHighlighter.defaults['class-name'] = '" . $this->js_escape_singlequotes( $this->settings['classname'] ) . "';\n"; | |
if ( 1 == $this->settings['collapse'] ) | |
echo " SyntaxHighlighter.defaults['collapse'] = true;\n"; | |
if ( 1 != $this->settings['firstline'] ) | |
echo " SyntaxHighlighter.defaults['first-line'] = " . $this->settings['firstline'] . ";\n"; | |
if ( 1 != $this->settings['gutter'] ) | |
echo " SyntaxHighlighter.defaults['gutter'] = false;\n"; | |
/* | |
if ( 1 == $this->settings['htmlscript'] ) | |
echo " SyntaxHighlighter.defaults['html-script'] = true;\n"; | |
*/ | |
if ( 1 == $this->settings['light'] ) | |
echo " SyntaxHighlighter.defaults['light'] = true;\n"; | |
echo " SyntaxHighlighter.defaults['pad-line-numbers'] = "; | |
switch ( $this->settings['padlinenumbers'] ) { | |
case 'true': | |
echo 'true'; | |
break; | |
case 'false'; | |
echo 'false'; | |
break; | |
default; | |
echo (int) $this->settings['padlinenumbers']; | |
} | |
echo ";\n"; | |
if ( 1 != $this->settings['smarttabs'] ) | |
echo " SyntaxHighlighter.defaults['smart-tabs'] = false;\n"; | |
if ( 4 != $this->settings['tabsize'] ) | |
echo " SyntaxHighlighter.defaults['tab-size'] = " . $this->settings['tabsize'] . ";\n"; | |
if ( 1 != $this->settings['toolbar'] ) | |
echo " SyntaxHighlighter.defaults['toolbar'] = false;\n"; | |
// 2.x only for now | |
if ( 1 != $this->settings['wraplines'] ) | |
echo " SyntaxHighlighter.defaults['wrap-lines'] = false;\n"; | |
?> SyntaxHighlighter.all(); | |
</script> | |
<?php | |
} | |
// No-name attribute fixing | |
function attributefix( $atts = array() ) { | |
if ( empty($atts[0]) ) | |
return $atts; | |
// Quoted value | |
if ( 0 !== preg_match( '#=("|\')(.*?)\1#', $atts[0], $match ) ) | |
$atts[0] = $match[2]; | |
// Unquoted value | |
elseif ( '=' == substr( $atts[0], 0, 1 ) ) | |
$atts[0] = substr( $atts[0], 1 ); | |
return $atts; | |
} | |
// Shortcode handler for transforming the shortcodes to their final <pre>'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, | |
), $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', | |
); | |
// 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', | |
) ); | |
$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 ); | |
$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', '<pre class="' . esc_attr( implode( ' ', $params ) ) . '"' . $title . '>' . $code . '</pre>' );; | |
} | |
// Settings page | |
function settings_page() { ?> | |
<script type="text/javascript"> | |
// <![CDATA[ | |
jQuery(document).ready(function($) { | |
// Confirm pressing of the "Reset to Defaults" button | |
$("#syntaxhighlighter-defaults").click(function(){ | |
var areyousure = confirm("<?php echo esc_js( __( 'Are you sure you want to reset your settings to the defaults?', 'syntaxhighlighter' ) ); ?>"); | |
if ( true != areyousure ) return false; | |
}); | |
<?php if ( !empty( $_GET['defaults'] ) ) : ?> | |
$("#message p strong").text("<?php echo esc_js( __( 'Settings reset to defaults.', 'syntaxhighlighter' ) ); ?>"); | |
<?php endif; ?> | |
}); | |
// ]]> | |
</script> | |
<div class="wrap"> | |
<?php if ( function_exists('screen_icon') ) screen_icon(); ?> | |
<h2><?php _e( 'SyntaxHighlighter Settings', 'syntaxhighlighter' ); ?></h2> | |
<form method="post" action="options.php"> | |
<?php settings_fields('syntaxhighlighter_settings'); ?> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-shversion"><?php _e( 'Highlighter Version', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[shversion]" id="syntaxhighlighter-shversion" class="postform"> | |
<?php | |
$versions = array( | |
3 => __( 'Version 3.x', 'syntaxhighlighter' ), | |
2 => __( 'Version 2.x', 'syntaxhighlighter' ), | |
); | |
foreach ( $versions as $version => $name ) { | |
echo ' <option value="' . esc_attr( $version ) . '"' . selected( $this->settings['shversion'], $version, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select><br /> | |
<?php _e( 'Version 3 allows visitors to easily highlight portions of your code with their mouse (either by dragging or double-clicking) and copy it to their clipboard. No toolbar containing a Flash-based button is required.', 'syntaxhighlighter' ); ?><br /> | |
<?php _e( 'Version 2 allows for line wrapping, something that version 3 does not do at this time.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-theme"><?php _e( 'Color Theme', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[theme]" id="syntaxhighlighter-theme" class="postform"> | |
<?php | |
foreach ( $this->themes as $theme => $name ) { | |
echo ' <option value="' . esc_attr( $theme ) . '"' . selected( $this->settings['theme'], $theme, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-loadallbrushes"><input name="syntaxhighlighter_settings[loadallbrushes]" type="checkbox" id="syntaxhighlighter-loadallbrushes" value="1" <?php checked( $this->settings['loadallbrushes'], 1 ); ?> /> <?php _e( 'Always load all language files (for directly using <code><pre></code> tags rather than shortcodes)<br /> If left unchecked (default), then language files will only be loaded when needed<br /> If unsure, leave this box unchecked', 'syntaxhighlighter' ); ?></label> | |
</fieldset> | |
</td> | |
</tr> | |
</table> | |
<h3><?php _e( 'Defaults', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'All of the settings below can be configured on a per-code block basis, but you can control the defaults of all code blocks here.', 'syntaxhighlighter' ); ?></p> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-gutter"><input name="syntaxhighlighter_settings[gutter]" type="checkbox" id="syntaxhighlighter-gutter" value="1" <?php checked( $this->settings['gutter'], 1 ); ?> /> <?php _e( 'Display line numbers', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-toolbar"><input name="syntaxhighlighter_settings[toolbar]" type="checkbox" id="syntaxhighlighter-toolbar" value="1" <?php checked( $this->settings['toolbar'], 1 ); ?> /> <?php _e( 'Display the toolbar', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-autolinks"><input name="syntaxhighlighter_settings[autolinks]" type="checkbox" id="syntaxhighlighter-autolinks" value="1" <?php checked( $this->settings['autolinks'], 1 ); ?> /> <?php _e( 'Automatically make URLs clickable', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-collapse"><input name="syntaxhighlighter_settings[collapse]" type="checkbox" id="syntaxhighlighter-collapse" value="1" <?php checked( $this->settings['collapse'], 1 ); ?> /> <?php _e( 'Collapse code boxes', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-light"><input name="syntaxhighlighter_settings[light]" type="checkbox" id="syntaxhighlighter-light" value="1" <?php checked( $this->settings['light'], 1 ); ?> /> <?php _e( 'Use the light display mode, best for single lines of code', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-smarttabs"><input name="syntaxhighlighter_settings[smarttabs]" type="checkbox" id="syntaxhighlighter-smarttabs" value="1" <?php checked( $this->settings['smarttabs'], 1 ); ?> /> <?php _e( 'Use smart tabs allowing tabs being used for alignment', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-wraplines"><input name="syntaxhighlighter_settings[wraplines]" type="checkbox" id="syntaxhighlighter-wraplines" value="1" <?php checked( $this->settings['wraplines'], 1 ); ?> /> <?php _e( 'Wrap long lines (v2.x only, disabling this will make a scrollbar show instead)', 'syntaxhighlighter' ); ?></label><br /> | |
<!--<label for="syntaxhighlighter-htmlscript"><input name="syntaxhighlighter_settings[htmlscript]" type="checkbox" id="syntaxhighlighter-htmlscript" value="1" <?php checked( $this->settings['htmlscript'], 1 ); ?> /> <?php _e( 'Enable "HTML script" mode by default (see the bottom of this page for details). Checking this box is not recommended as this mode only works with certain languages.', 'syntaxhighlighter' ); ?></label>--> | |
</fieldset> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-classname"><?php _e( 'Additional CSS Class(es)', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[classname]" type="text" id="syntaxhighlighter-classname" value="<?php echo esc_attr( $this->settings['classname'] ); ?>" class="regular-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-firstline"><?php _e( 'Starting Line Number', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[firstline]" type="text" id="syntaxhighlighter-firstline" value="<?php echo esc_attr( $this->settings['firstline'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-padlinenumbers"><?php _e( 'Line Number Padding', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[padlinenumbers]" id="syntaxhighlighter-padlinenumbers" class="postform"> | |
<?php | |
$linepaddings = array( | |
'false' => __( 'Off', 'syntaxhighlighter' ), | |
'true' => __( 'Automatic', 'syntaxhighlighter' ), | |
1 => 1, | |
2 => 2, | |
3 => 3, | |
4 => 4, | |
5 => 5, | |
); | |
foreach ( $linepaddings as $value => $name ) { | |
echo ' <option value="' . esc_attr( $value ) . '"' . selected( $this->settings['padlinenumbers'], $value, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-tabsize"><?php _e( 'Tab Size', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[tabsize]" type="text" id="syntaxhighlighter-tabsize" value="<?php echo esc_attr( $this->settings['tabsize'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-title"><?php _e( 'Title', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<input name="syntaxhighlighter_settings[title]" type="text" id="syntaxhighlighter-title" value="<?php echo esc_attr( $this->settings['title'] ); ?>" class="regular-text" /><br /> | |
<?php _e( 'Some optional default text to display above each code block or as the clickable text for collapsed code blocks.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
</table> | |
<p class="submit"> | |
<?php | |
if ( function_exists( 'submit_button' ) ) { | |
submit_button( null, 'primary', 'syntaxhighlighter-submit', false ); | |
echo ' '; | |
submit_button( __( 'Reset to Defaults', 'syntaxhighlighter' ), 'primary', 'syntaxhighlighter-defaults', false ); | |
} else { | |
echo '<input type="submit" name="syntaxhighlighter-submit" class="button-primary" value="' . __( 'Save Changes') . '" />' . "\n"; | |
echo '<input type="submit" name="syntaxhighlighter-defaults" id="syntaxhighlighter-defaults" class="button-primary" value="' . __( 'Reset to Defaults', 'syntaxhighlighter' ) . '" />' . "\n"; | |
} | |
?> | |
</p> | |
</form> | |
<h3><?php _e( 'Preview', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'Click "Save Changes" to update this preview.', 'syntaxhighlighter' ); ?> | |
<?php | |
echo '<div'; | |
if ( ! empty( $GLOBALS['content_width'] ) ) | |
echo ' style="max-width:' . intval( $GLOBALS['content_width'] ) . 'px"'; | |
echo '>'; | |
$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 . ']<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>PHP Code Example</title> | |
</head> | |
<body> | |
<h1>' . __( 'PHP Code Example', 'syntaxhighlighter' ) . '</h1> | |
<p><?php echo \'' . __( 'Hello World!', 'syntaxhighlighter' ) . '\'; ?></p> | |
<p>' . __( 'This line is highlighted.', 'syntaxhighlighter' ) . '</p> | |
<div class="foobar"> | |
' . __( ' This is an | |
example of smart | |
tabs.', 'syntaxhighlighter' ) . ' | |
</div> | |
<p><a href="http://wordpress.org/">' . __( 'WordPress' ) . '</a></p> | |
</body> | |
</html>[/sourcecode]' ); | |
$this->codeformat = 1; | |
echo $this->parse_shortcodes( $democode ); | |
$this->codeformat = false; | |
echo '</div>'; | |
?> | |
<h3 style="margin-top:30px"><?php _e( 'Shortcode Parameters', 'syntaxhighlighter' ); ?></h3> | |
<p><?php printf( __( 'These are the parameters you can pass to the shortcode and what they do. For the booleans (i.e. on/off), pass %1$s/%2$s or %3$s/%4$s.', 'syntaxhighlighter' ), '<code>true</code>', '<code>1</code>', '<code>false</code>', '<code>0</code>' ); ?></p> | |
<ul class="ul-disc"> | |
<li><?php printf( _x( '%1$s or %2$s — The language syntax to highlight with. You can alternately just use that as the tag, such as <code>[php]code[/php]</code>. <a href="%3$s">Click here</a> for a list of valid tags (under "aliases").', 'language parameter', 'syntaxhighlighter' ), '<code>lang</code>', '<code>language</code>', 'http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle automatic URL linking.', 'autolinks parameter', 'syntaxhighlighter' ), '<code>autolinks</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Add an additional CSS class to the code box.', 'classname parameter', 'syntaxhighlighter' ), '<code>classname</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle collapsing the code box by default, requiring a click to expand it. Good for large code posts.', 'collapse parameter', 'syntaxhighlighter' ), '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — An interger specifying what number the first line should be (for the line numbering).', 'firstline parameter', 'syntaxhighlighter' ), '<code>firstline</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the left-side line numbering.', 'gutter parameter', 'syntaxhighlighter' ), '<code>gutter</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s — A comma-separated list of line numbers to highlight. You can also specify a range. Example: %2$s', 'highlight parameter', 'syntaxhighlighter' ), '<code>highlight</code>', '<code>2,5-10,12</code>' ); ?></li> | |
<li><?php printf( _x( "%s — Toggle highlighting any extra HTML/XML. Good for when you're mixing HTML/XML with another language, such as having PHP inside an HTML web page. The above preview has it enabled for example. This only works with certain languages.", 'htmlscript parameter', 'syntaxhighlighter' ), '<code>htmlscript</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle light mode which disables the gutter and toolbar all at once.', 'light parameter', 'syntaxhighlighter' ), '<code>light</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Controls line number padding. Valid values are <code>false</code> (no padding), <code>true</code> (automatic padding), or an integer (forced padding).', 'padlinenumbers parameter', 'syntaxhighlighter' ), '<code>padlinenumbers</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s (v3 only) — Sets some text to show up before the code. Very useful when combined with the %2$s parameter.', 'title parameter', 'syntaxhighlighter' ), '<code>title</code>', '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the toolbar (buttons in v2, the about question mark in v3)', 'toolbar parameter', 'syntaxhighlighter' ), '<code>toolbar</code>' ); ?></li> | |
<li><?php printf( _x( '%s (v2 only) — Toggle line wrapping.', 'wraplines parameter', 'syntaxhighlighter'), '<code>wraplines</code>' ); ?></li> | |
</ul> | |
<p><?php _e( 'Some example shortcodes:', 'syntaxhighlighter' ); ?></p> | |
<ul class="ul-disc"> | |
<li><code>[php]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/php]</code></li> | |
<li><code>[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="<?php _e( 'example-filename.php', 'syntaxhighlighter' ); ?>"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/css]</code></li> | |
<li><code>[code lang="js"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/code]</code></li> | |
<li><code>[sourcecode language="plain"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/sourcecode]</code></li> | |
</ul> | |
<?php $this->maybe_output_scripts(); ?> | |
</div> | |
<?php | |
} | |
// Validate the settings sent from the settings page | |
function validate_settings( $settings ) { | |
if ( !empty($_POST['syntaxhighlighter-defaults']) ) { | |
$settings = $this->defaultsettings; | |
$_REQUEST['_wp_http_referer'] = add_query_arg( 'defaults', 'true', $_REQUEST['_wp_http_referer'] ); | |
} else { | |
$settings['shversion'] = ( ! empty($settings['shversion']) && 2 == $settings['shversion'] ) ? 2 : 3; | |
$settings['theme'] = ( ! empty($settings['theme']) && isset($this->themes[$settings['theme']]) ) ? strtolower($settings['theme']) : $this->defaultsettings['theme']; | |
$settings['loadallbrushes'] = ( ! empty($settings['loadallbrushes']) ) ? 1 : 0; | |
$settings['autolinks'] = ( ! empty($settings['autolinks']) ) ? 1 : 0; | |
$settings['collapse'] = ( ! empty($settings['collapse']) ) ? 1 : 0; | |
$settings['gutter'] = ( ! empty($settings['gutter']) ) ? 1 : 0; | |
$settings['light'] = ( ! empty($settings['light']) ) ? 1 : 0; | |
$settings['smarttabs'] = ( ! empty($settings['smarttabs']) ) ? 1 : 0; | |
$settings['toolbar'] = ( ! empty($settings['toolbar']) ) ? 1 : 0; // May be overridden below | |
$settings['wraplines'] = ( ! empty($settings['wraplines']) ) ? 1 : 0; // 2.x only for now | |
// If the version changed, then force change the toolbar version setting | |
if ( $settings['shversion'] != $this->settings['shversion'] ) { | |
$settings['toolbar'] = ( 2 == $settings['shversion'] ) ? 1 : 0; | |
} | |
if ( 'true' != $settings['padlinenumbers'] && 'false' != $settings['padlinenumbers'] ) | |
$settings['padlinenumbers'] = (int) $settings['padlinenumbers']; | |
$settings['classname'] = ( !empty($settings['classname']) ) ? preg_replace( '/[^ A-Za-z0-9_-]*/', '', $settings['classname'] ) : ''; | |
$settings['firstline'] = (int) ( !empty($settings['firstline']) ) ? $settings['firstline'] : $this->defaultsettings['firstline']; | |
$settings['tabsize'] = (int) ( !empty($settings['tabsize']) ) ? $settings['tabsize'] : $this->defaultsettings['tabsize']; | |
} | |
return $settings; | |
} | |
// PHP4 compatibility | |
function SyntaxHighlighter() { | |
$this->__construct(); | |
} | |
} | |
// Start this plugin once all other plugins are fully loaded | |
add_action( 'init', 'SyntaxHighlighter', 5 ); | |
function SyntaxHighlighter() { | |
global $SyntaxHighlighter; | |
$SyntaxHighlighter = new SyntaxHighlighter(); | |
} | |
?> |
<?php /* | |
************************************************************************** | |
Plugin Name: SyntaxHighlighter Evolved | |
Plugin URI: http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/ | |
Version: 3.1.11 | |
Description: Easily post syntax-highlighted code to your site without having to modify the code at all. Uses Alex Gorbatchev's <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter">SyntaxHighlighter</a>. <strong>TIP:</strong> Don't use the Visual editor if you don't want your code mangled. TinyMCE will "clean up" your HTML. | |
Author: Alex Mills (Viper007Bond) | |
Author URI: http://www.viper007bond.com/ | |
************************************************************************** | |
Thanks to: | |
* Alex Gorbatchev for writing the Javascript-powered synatax highlighter script | |
* Andrew Ozz for writing the TinyMCE plugin | |
**************************************************************************/ | |
class SyntaxHighlighter { | |
// All of these variables are private. Filters are provided for things that can be modified. | |
var $pluginver = '3.1.11'; // Plugin version | |
var $agshver = false; // Alex Gorbatchev's SyntaxHighlighter version (dynamically set below due to v2 vs v3) | |
var $shfolder = false; // Controls what subfolder to load SyntaxHighlighter from (v2 or v3) | |
var $settings = array(); // Contains the user's settings | |
var $defaultsettings = array(); // Contains the default settings | |
var $brushes = array(); // Array of aliases => brushes | |
var $shortcodes = array(); // Array of shortcodes to use | |
var $themes = array(); // Array of themes | |
var $usedbrushes = array(); // Stores used brushes so we know what to output | |
var $encoded = false; // Used to mark that a character encode took place | |
var $codeformat = false; // If set, SyntaxHighlighter::get_code_format() will return this value | |
var $content_save_pre_ran = false; // It's possible for the "content_save_pre" filter to run multiple times, so keep track | |
// Initalize the plugin by registering the hooks | |
function __construct() { | |
if ( ! function_exists( 'esc_html' ) ) | |
return; | |
// Load localization domain | |
load_plugin_textdomain( 'syntaxhighlighter', false, '/syntaxhighlighter/localization' ); | |
// 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_head', array( $this, 'output_header_placeholder' ), 15 ); | |
add_action( 'admin_head', array( $this, 'output_header_placeholder' ), 15 ); // For comments | |
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 ); | |
// Register widget hooks | |
// Requires change added in WordPress 2.9 | |
if ( class_exists('WP_Embed') ) { | |
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 | |
) ); | |
// 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'; | |
} | |
// 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-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-groovy', plugins_url( $this->shfolder . '/scripts/shBrushGroovy.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-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 ); | |
// 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 ); | |
// 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', | |
'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', | |
'groovy' => 'groovy', | |
'java' => 'java', | |
'jfx' => 'javafx', | |
'javafx' => 'javafx', | |
'js' => 'jscript', | |
'jscript' => 'jscript', | |
'javascript' => 'jscript', | |
'latex' => 'latex', // Not used as a shortcode | |
'tex' => 'latex', | |
'matlab' => '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', | |
'vb' => 'vb', | |
'vbnet' => 'vb', | |
'xml' => 'xml', | |
'xhtml' => 'xml', | |
'xslt' => 'xml', | |
'html' => 'xml', | |
) ); | |
// 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_true' ); | |
// 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' ) ); | |
} | |
// Add the custom TinyMCE plugin which wraps plugin shortcodes in <pre> 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[] = '<a href="' . admin_url( 'options-general.php?page=syntaxhighlighter' ) . '">' . __( 'Settings', 'syntaxhighlighter' ) . '</a>'; | |
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 "<script type='text/javascript'>\n"; | |
echo " var syntaxHLcodes = '" . implode( '|', $shortcodes ) . "';\n"; | |
echo "</script>\n"; | |
} | |
// A filter function that runs do_shortcode() but only with this plugin's shortcodes | |
function shortcode_hack( $content, $callback ) { | |
global $shortcode_tags; | |
// Backup current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
// Register all of this plugin's shortcodes | |
foreach ( $this->shortcodes as $shortcode ) | |
add_shortcode( $shortcode, $callback ); | |
// Do the shortcodes (only this plugins's are registered) | |
$content = $this->do_shortcode_keep_escaped_tags( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
// This is a clone of do_shortcode() that uses a different callback function | |
// The new callback function will keep escaped tags escaped, i.e. [[foo]] | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_keep_escaped_tags( $content ) { | |
global $shortcode_tags; | |
if (empty($shortcode_tags) || !is_array($shortcode_tags)) | |
return $content; | |
$pattern = get_shortcode_regex(); | |
return preg_replace_callback('/'.$pattern.'/s', array( $this, 'do_shortcode_tag_keep_escaped_tags' ), $content); | |
} | |
// Callback for above do_shortcode_keep_escaped_tags() function | |
// It's a clone of core's do_shortcode_tag() function with a modification to the escaped shortcode return | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_tag_keep_escaped_tags( $m ) { | |
global $shortcode_tags; | |
// allow [[foo]] syntax for escaping a tag | |
if ( $m[1] == '[' && $m[6] == ']' ) { | |
return $m[0]; // This line was modified for this plugin (no substr call) | |
} | |
$tag = $m[2]; | |
$attr = shortcode_parse_atts( $m[3] ); | |
if ( isset( $m[5] ) ) { | |
// enclosing tag - extra parameter | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; | |
} else { | |
// self-closing tag | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, NULL, $tag ) . $m[6]; | |
} | |
} | |
// 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' ) ); | |
} | |
// 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) | |
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' ) ); | |
} | |
// 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 an anchor in the header for the Javascript to use. | |
// In the <head>, we don't know if we'll need this plugin's CSS and JavaScript yet but we will in the footer. | |
function output_header_placeholder() { | |
echo '<style type="text/css" id="syntaxhighlighteranchor"></style>' . "\n"; | |
} | |
// 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 "<script type='text/javascript'>\n"; | |
echo " (function(){\n"; | |
echo " var corecss = document.createElement('link');\n"; | |
echo " var themecss = document.createElement('link');\n"; | |
if ( !is_a($wp_styles, 'WP_Styles') ) | |
$wp_styles = new WP_Styles(); | |
$needcore = false; | |
if ( 'none' == $this->settings['theme'] ) { | |
$needcore = true; | |
} else { | |
$theme = ( !empty($this->themes[$this->settings['theme']]) ) ? strtolower($this->settings['theme']) : $this->defaultsettings['theme']; | |
$theme = 'syntaxhighlighter-theme-' . $theme; | |
// See if the requested theme has been registered | |
if ( !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered[$theme]) && !empty($wp_styles->registered[$theme]->src) ) { | |
// Users can register their own stylesheet and may opt to not load the core stylesheet if they wish for some reason | |
if ( is_array($wp_styles->registered[$theme]->deps) && in_array( 'syntaxhighlighter-core', $wp_styles->registered[$theme]->deps ) ) | |
$needcore = true; | |
} | |
// Otherwise use the default theme | |
else { | |
$theme = 'syntaxhighlighter-theme-' . $this->defaultsettings['theme']; | |
$needcore = true; | |
} | |
} | |
if ( $needcore && !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered['syntaxhighlighter-core']) && !empty($wp_styles->registered['syntaxhighlighter-core']->src) ) : | |
$corecssurl = add_query_arg( 'ver', $this->agshver, $wp_styles->registered['syntaxhighlighter-core']->src ); | |
$corecssurl = apply_filters( 'syntaxhighlighter_csscoreurl', $corecssurl ); | |
?> | |
var corecssurl = "<?php echo esc_js( $corecssurl ); ?>"; | |
if ( corecss.setAttribute ) { | |
corecss.setAttribute( "rel", "stylesheet" ); | |
corecss.setAttribute( "type", "text/css" ); | |
corecss.setAttribute( "href", corecssurl ); | |
} else { | |
corecss.rel = "stylesheet"; | |
corecss.href = corecssurl; | |
} | |
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif $needcore | |
if ( 'none' != $this->settings['theme'] ) : ?> | |
var themecssurl = "<?php echo esc_js( apply_filters( 'syntaxhighlighter_cssthemeurl', add_query_arg( 'ver', $this->agshver, $wp_styles->registered[$theme]->src ) ) ); ?>"; | |
if ( themecss.setAttribute ) { | |
themecss.setAttribute( "rel", "stylesheet" ); | |
themecss.setAttribute( "type", "text/css" ); | |
themecss.setAttribute( "href", themecssurl ); | |
} else { | |
themecss.rel = "stylesheet"; | |
themecss.href = themecssurl; | |
} | |
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss); | |
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif none != theme | |
echo " })();\n"; | |
switch ( $this->settings['shversion'] ) { | |
case 2: | |
echo " SyntaxHighlighter.config.clipboardSwf = '" . esc_js( apply_filters( 'syntaxhighlighter_clipboardurl', plugins_url( 'syntaxhighlighter2/scripts/clipboard.swf', __FILE__ ) ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( 'show source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.viewSource = '" . $this->js_escape_singlequotes( __( 'view source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboard = '" . $this->js_escape_singlequotes( __( 'copy to clipboard', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboardConfirmation = '" . $this->js_escape_singlequotes( __( 'The code is in your clipboard now', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.print = '" . $this->js_escape_singlequotes( __( 'print', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
case 3: | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( '+ expand source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
} | |
if ( 1 != $this->settings['autolinks'] ) | |
echo " SyntaxHighlighter.defaults['auto-links'] = false;\n"; | |
if ( !empty($this->settings['classname']) ) | |
echo " SyntaxHighlighter.defaults['class-name'] = '" . $this->js_escape_singlequotes( $this->settings['classname'] ) . "';\n"; | |
if ( 1 == $this->settings['collapse'] ) | |
echo " SyntaxHighlighter.defaults['collapse'] = true;\n"; | |
if ( 1 != $this->settings['firstline'] ) | |
echo " SyntaxHighlighter.defaults['first-line'] = " . $this->settings['firstline'] . ";\n"; | |
if ( 1 != $this->settings['gutter'] ) | |
echo " SyntaxHighlighter.defaults['gutter'] = false;\n"; | |
/* | |
if ( 1 == $this->settings['htmlscript'] ) | |
echo " SyntaxHighlighter.defaults['html-script'] = true;\n"; | |
*/ | |
if ( 1 == $this->settings['light'] ) | |
echo " SyntaxHighlighter.defaults['light'] = true;\n"; | |
echo " SyntaxHighlighter.defaults['pad-line-numbers'] = "; | |
switch ( $this->settings['padlinenumbers'] ) { | |
case 'true': | |
echo 'true'; | |
break; | |
case 'false'; | |
echo 'false'; | |
break; | |
default; | |
echo (int) $this->settings['padlinenumbers']; | |
} | |
echo ";\n"; | |
if ( 1 != $this->settings['smarttabs'] ) | |
echo " SyntaxHighlighter.defaults['smart-tabs'] = false;\n"; | |
if ( 4 != $this->settings['tabsize'] ) | |
echo " SyntaxHighlighter.defaults['tab-size'] = " . $this->settings['tabsize'] . ";\n"; | |
if ( 1 != $this->settings['toolbar'] ) | |
echo " SyntaxHighlighter.defaults['toolbar'] = false;\n"; | |
// 2.x only for now | |
if ( 1 != $this->settings['wraplines'] ) | |
echo " SyntaxHighlighter.defaults['wrap-lines'] = false;\n"; | |
?> SyntaxHighlighter.all(); | |
</script> | |
<?php | |
} | |
// No-name attribute fixing | |
function attributefix( $atts = array() ) { | |
if ( empty($atts[0]) ) | |
return $atts; | |
// Quoted value | |
if ( 0 !== preg_match( '#=("|\')(.*?)\1#', $atts[0], $match ) ) | |
$atts[0] = $match[2]; | |
// Unquoted value | |
elseif ( '=' == substr( $atts[0], 0, 1 ) ) | |
$atts[0] = substr( $atts[0], 1 ); | |
return $atts; | |
} | |
// Shortcode handler for transforming the shortcodes to their final <pre>'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, | |
), $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', | |
); | |
// 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', | |
) ); | |
$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 ); | |
$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', '<pre class="' . esc_attr( implode( ' ', $params ) ) . '"' . $title . '>' . $code . '</pre>' );; | |
} | |
// Settings page | |
function settings_page() { ?> | |
<script type="text/javascript"> | |
// <![CDATA[ | |
jQuery(document).ready(function($) { | |
// Confirm pressing of the "Reset to Defaults" button | |
$("#syntaxhighlighter-defaults").click(function(){ | |
var areyousure = confirm("<?php echo esc_js( __( 'Are you sure you want to reset your settings to the defaults?', 'syntaxhighlighter' ) ); ?>"); | |
if ( true != areyousure ) return false; | |
}); | |
<?php if ( !empty( $_GET['defaults'] ) ) : ?> | |
$("#message p strong").text("<?php echo esc_js( __( 'Settings reset to defaults.', 'syntaxhighlighter' ) ); ?>"); | |
<?php endif; ?> | |
}); | |
// ]]> | |
</script> | |
<div class="wrap"> | |
<?php if ( function_exists('screen_icon') ) screen_icon(); ?> | |
<h2><?php _e( 'SyntaxHighlighter Settings', 'syntaxhighlighter' ); ?></h2> | |
<form method="post" action="options.php"> | |
<?php settings_fields('syntaxhighlighter_settings'); ?> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-shversion"><?php _e( 'Highlighter Version', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[shversion]" id="syntaxhighlighter-shversion" class="postform"> | |
<?php | |
$versions = array( | |
3 => __( 'Version 3.x', 'syntaxhighlighter' ), | |
2 => __( 'Version 2.x', 'syntaxhighlighter' ), | |
); | |
foreach ( $versions as $version => $name ) { | |
echo ' <option value="' . esc_attr( $version ) . '"' . selected( $this->settings['shversion'], $version, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select><br /> | |
<?php _e( 'Version 3 allows visitors to easily highlight portions of your code with their mouse (either by dragging or double-clicking) and copy it to their clipboard. No toolbar containing a Flash-based button is required.', 'syntaxhighlighter' ); ?><br /> | |
<?php _e( 'Version 2 allows for line wrapping, something that version 3 does not do at this time.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-theme"><?php _e( 'Color Theme', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[theme]" id="syntaxhighlighter-theme" class="postform"> | |
<?php | |
foreach ( $this->themes as $theme => $name ) { | |
echo ' <option value="' . esc_attr( $theme ) . '"' . selected( $this->settings['theme'], $theme, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-loadallbrushes"><input name="syntaxhighlighter_settings[loadallbrushes]" type="checkbox" id="syntaxhighlighter-loadallbrushes" value="1" <?php checked( $this->settings['loadallbrushes'], 1 ); ?> /> <?php _e( 'Always load all language files (for directly using <code><pre></code> tags rather than shortcodes)<br /> If left unchecked (default), then language files will only be loaded when needed<br /> If unsure, leave this box unchecked', 'syntaxhighlighter' ); ?></label> | |
</fieldset> | |
</td> | |
</tr> | |
</table> | |
<h3><?php _e( 'Defaults', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'All of the settings below can be configured on a per-code block basis, but you can control the defaults of all code blocks here.', 'syntaxhighlighter' ); ?></p> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-gutter"><input name="syntaxhighlighter_settings[gutter]" type="checkbox" id="syntaxhighlighter-gutter" value="1" <?php checked( $this->settings['gutter'], 1 ); ?> /> <?php _e( 'Display line numbers', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-toolbar"><input name="syntaxhighlighter_settings[toolbar]" type="checkbox" id="syntaxhighlighter-toolbar" value="1" <?php checked( $this->settings['toolbar'], 1 ); ?> /> <?php _e( 'Display the toolbar', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-autolinks"><input name="syntaxhighlighter_settings[autolinks]" type="checkbox" id="syntaxhighlighter-autolinks" value="1" <?php checked( $this->settings['autolinks'], 1 ); ?> /> <?php _e( 'Automatically make URLs clickable', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-collapse"><input name="syntaxhighlighter_settings[collapse]" type="checkbox" id="syntaxhighlighter-collapse" value="1" <?php checked( $this->settings['collapse'], 1 ); ?> /> <?php _e( 'Collapse code boxes', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-light"><input name="syntaxhighlighter_settings[light]" type="checkbox" id="syntaxhighlighter-light" value="1" <?php checked( $this->settings['light'], 1 ); ?> /> <?php _e( 'Use the light display mode, best for single lines of code', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-smarttabs"><input name="syntaxhighlighter_settings[smarttabs]" type="checkbox" id="syntaxhighlighter-smarttabs" value="1" <?php checked( $this->settings['smarttabs'], 1 ); ?> /> <?php _e( 'Use smart tabs allowing tabs being used for alignment', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-wraplines"><input name="syntaxhighlighter_settings[wraplines]" type="checkbox" id="syntaxhighlighter-wraplines" value="1" <?php checked( $this->settings['wraplines'], 1 ); ?> /> <?php _e( 'Wrap long lines (v2.x only, disabling this will make a scrollbar show instead)', 'syntaxhighlighter' ); ?></label><br /> | |
<!--<label for="syntaxhighlighter-htmlscript"><input name="syntaxhighlighter_settings[htmlscript]" type="checkbox" id="syntaxhighlighter-htmlscript" value="1" <?php checked( $this->settings['htmlscript'], 1 ); ?> /> <?php _e( 'Enable "HTML script" mode by default (see the bottom of this page for details). Checking this box is not recommended as this mode only works with certain languages.', 'syntaxhighlighter' ); ?></label>--> | |
</fieldset> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-classname"><?php _e( 'Additional CSS Class(es)', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[classname]" type="text" id="syntaxhighlighter-classname" value="<?php echo esc_attr( $this->settings['classname'] ); ?>" class="regular-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-firstline"><?php _e( 'Starting Line Number', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[firstline]" type="text" id="syntaxhighlighter-firstline" value="<?php echo esc_attr( $this->settings['firstline'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-padlinenumbers"><?php _e( 'Line Number Padding', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[padlinenumbers]" id="syntaxhighlighter-padlinenumbers" class="postform"> | |
<?php | |
$linepaddings = array( | |
'false' => __( 'Off', 'syntaxhighlighter' ), | |
'true' => __( 'Automatic', 'syntaxhighlighter' ), | |
1 => 1, | |
2 => 2, | |
3 => 3, | |
4 => 4, | |
5 => 5, | |
); | |
foreach ( $linepaddings as $value => $name ) { | |
echo ' <option value="' . esc_attr( $value ) . '"' . selected( $this->settings['padlinenumbers'], $value, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-tabsize"><?php _e( 'Tab Size', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[tabsize]" type="text" id="syntaxhighlighter-tabsize" value="<?php echo esc_attr( $this->settings['tabsize'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-title"><?php _e( 'Title', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<input name="syntaxhighlighter_settings[title]" type="text" id="syntaxhighlighter-title" value="<?php echo esc_attr( $this->settings['title'] ); ?>" class="regular-text" /><br /> | |
<?php _e( 'Some optional default text to display above each code block or as the clickable text for collapsed code blocks.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
</table> | |
<p class="submit"> | |
<?php | |
if ( function_exists( 'submit_button' ) ) { | |
submit_button( null, 'primary', 'syntaxhighlighter-submit', false ); | |
echo ' '; | |
submit_button( __( 'Reset to Defaults', 'syntaxhighlighter' ), 'primary', 'syntaxhighlighter-defaults', false ); | |
} else { | |
echo '<input type="submit" name="syntaxhighlighter-submit" class="button-primary" value="' . __( 'Save Changes') . '" />' . "\n"; | |
echo '<input type="submit" name="syntaxhighlighter-defaults" id="syntaxhighlighter-defaults" class="button-primary" value="' . __( 'Reset to Defaults', 'syntaxhighlighter' ) . '" />' . "\n"; | |
} | |
?> | |
</p> | |
</form> | |
<h3><?php _e( 'Preview', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'Click "Save Changes" to update this preview.', 'syntaxhighlighter' ); ?> | |
<?php | |
echo '<div'; | |
if ( ! empty( $GLOBALS['content_width'] ) ) | |
echo ' style="max-width:' . intval( $GLOBALS['content_width'] ) . 'px"'; | |
echo '>'; | |
$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 . ']<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>PHP Code Example</title> | |
</head> | |
<body> | |
<h1>' . __( 'PHP Code Example', 'syntaxhighlighter' ) . '</h1> | |
<p><?php echo \'' . __( 'Hello World!', 'syntaxhighlighter' ) . '\'; ?></p> | |
<p>' . __( 'This line is highlighted.', 'syntaxhighlighter' ) . '</p> | |
<div class="foobar"> | |
' . __( ' This is an | |
example of smart | |
tabs.', 'syntaxhighlighter' ) . ' | |
</div> | |
<p><a href="http://wordpress.org/">' . __( 'WordPress' ) . '</a></p> | |
</body> | |
</html>[/sourcecode]' ); | |
$this->codeformat = 1; | |
echo $this->parse_shortcodes( $democode ); | |
$this->codeformat = false; | |
echo '</div>'; | |
?> | |
<h3 style="margin-top:30px"><?php _e( 'Shortcode Parameters', 'syntaxhighlighter' ); ?></h3> | |
<p><?php printf( __( 'These are the parameters you can pass to the shortcode and what they do. For the booleans (i.e. on/off), pass %1$s/%2$s or %3$s/%4$s.', 'syntaxhighlighter' ), '<code>true</code>', '<code>1</code>', '<code>false</code>', '<code>0</code>' ); ?></p> | |
<ul class="ul-disc"> | |
<li><?php printf( _x( '%1$s or %2$s — The language syntax to highlight with. You can alternately just use that as the tag, such as <code>[php]code[/php]</code>. <a href="%3$s">Click here</a> for a list of valid tags (under "aliases").', 'language parameter', 'syntaxhighlighter' ), '<code>lang</code>', '<code>language</code>', 'http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle automatic URL linking.', 'autolinks parameter', 'syntaxhighlighter' ), '<code>autolinks</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Add an additional CSS class to the code box.', 'classname parameter', 'syntaxhighlighter' ), '<code>classname</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle collapsing the code box by default, requiring a click to expand it. Good for large code posts.', 'collapse parameter', 'syntaxhighlighter' ), '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — An interger specifying what number the first line should be (for the line numbering).', 'firstline parameter', 'syntaxhighlighter' ), '<code>firstline</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the left-side line numbering.', 'gutter parameter', 'syntaxhighlighter' ), '<code>gutter</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s — A comma-separated list of line numbers to highlight. You can also specify a range. Example: %2$s', 'highlight parameter', 'syntaxhighlighter' ), '<code>highlight</code>', '<code>2,5-10,12</code>' ); ?></li> | |
<li><?php printf( _x( "%s — Toggle highlighting any extra HTML/XML. Good for when you're mixing HTML/XML with another language, such as having PHP inside an HTML web page. The above preview has it enabled for example. This only works with certain languages.", 'htmlscript parameter', 'syntaxhighlighter' ), '<code>htmlscript</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle light mode which disables the gutter and toolbar all at once.', 'light parameter', 'syntaxhighlighter' ), '<code>light</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Controls line number padding. Valid values are <code>false</code> (no padding), <code>true</code> (automatic padding), or an integer (forced padding).', 'padlinenumbers parameter', 'syntaxhighlighter' ), '<code>padlinenumbers</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s (v3 only) — Sets some text to show up before the code. Very useful when combined with the %2$s parameter.', 'title parameter', 'syntaxhighlighter' ), '<code>title</code>', '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the toolbar (buttons in v2, the about question mark in v3)', 'toolbar parameter', 'syntaxhighlighter' ), '<code>toolbar</code>' ); ?></li> | |
<li><?php printf( _x( '%s (v2 only) — Toggle line wrapping.', 'wraplines parameter', 'syntaxhighlighter'), '<code>wraplines</code>' ); ?></li> | |
</ul> | |
<p><?php _e( 'Some example shortcodes:', 'syntaxhighlighter' ); ?></p> | |
<ul class="ul-disc"> | |
<li><code>[php]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/php]</code></li> | |
<li><code>[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="<?php _e( 'example-filename.php', 'syntaxhighlighter' ); ?>"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/css]</code></li> | |
<li><code>[code lang="js"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/code]</code></li> | |
<li><code>[sourcecode language="plain"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/sourcecode]</code></li> | |
</ul> | |
<?php $this->maybe_output_scripts(); ?> | |
</div> | |
<?php | |
} | |
// Validate the settings sent from the settings page | |
function validate_settings( $settings ) { | |
if ( !empty($_POST['syntaxhighlighter-defaults']) ) { | |
$settings = $this->defaultsettings; | |
$_REQUEST['_wp_http_referer'] = add_query_arg( 'defaults', 'true', $_REQUEST['_wp_http_referer'] ); | |
} else { | |
$settings['shversion'] = ( ! empty($settings['shversion']) && 2 == $settings['shversion'] ) ? 2 : 3; | |
$settings['theme'] = ( ! empty($settings['theme']) && isset($this->themes[$settings['theme']]) ) ? strtolower($settings['theme']) : $this->defaultsettings['theme']; | |
$settings['loadallbrushes'] = ( ! empty($settings['loadallbrushes']) ) ? 1 : 0; | |
$settings['autolinks'] = ( ! empty($settings['autolinks']) ) ? 1 : 0; | |
$settings['collapse'] = ( ! empty($settings['collapse']) ) ? 1 : 0; | |
$settings['gutter'] = ( ! empty($settings['gutter']) ) ? 1 : 0; | |
$settings['light'] = ( ! empty($settings['light']) ) ? 1 : 0; | |
$settings['smarttabs'] = ( ! empty($settings['smarttabs']) ) ? 1 : 0; | |
$settings['toolbar'] = ( ! empty($settings['toolbar']) ) ? 1 : 0; // May be overridden below | |
$settings['wraplines'] = ( ! empty($settings['wraplines']) ) ? 1 : 0; // 2.x only for now | |
// If the version changed, then force change the toolbar version setting | |
if ( $settings['shversion'] != $this->settings['shversion'] ) { | |
$settings['toolbar'] = ( 2 == $settings['shversion'] ) ? 1 : 0; | |
} | |
if ( 'true' != $settings['padlinenumbers'] && 'false' != $settings['padlinenumbers'] ) | |
$settings['padlinenumbers'] = (int) $settings['padlinenumbers']; | |
$settings['classname'] = ( !empty($settings['classname']) ) ? preg_replace( '/[^ A-Za-z0-9_-]*/', '', $settings['classname'] ) : ''; | |
$settings['firstline'] = (int) ( !empty($settings['firstline']) ) ? $settings['firstline'] : $this->defaultsettings['firstline']; | |
$settings['tabsize'] = (int) ( !empty($settings['tabsize']) ) ? $settings['tabsize'] : $this->defaultsettings['tabsize']; | |
} | |
return $settings; | |
} | |
// PHP4 compatibility | |
function SyntaxHighlighter() { | |
$this->__construct(); | |
} | |
} | |
// Start this plugin once all other plugins are fully loaded | |
add_action( 'init', 'SyntaxHighlighter', 5 ); | |
function SyntaxHighlighter() { | |
global $SyntaxHighlighter; | |
$SyntaxHighlighter = new SyntaxHighlighter(); | |
} | |
?> |
<?php /* | |
************************************************************************** | |
Plugin Name: SyntaxHighlighter Evolved | |
Plugin URI: http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/ | |
Version: 3.1.11 | |
Description: Easily post syntax-highlighted code to your site without having to modify the code at all. Uses Alex Gorbatchev's <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter">SyntaxHighlighter</a>. <strong>TIP:</strong> Don't use the Visual editor if you don't want your code mangled. TinyMCE will "clean up" your HTML. | |
Author: Alex Mills (Viper007Bond) | |
Author URI: http://www.viper007bond.com/ | |
************************************************************************** | |
Thanks to: | |
* Alex Gorbatchev for writing the Javascript-powered synatax highlighter script | |
* Andrew Ozz for writing the TinyMCE plugin | |
**************************************************************************/ | |
class SyntaxHighlighter { | |
// All of these variables are private. Filters are provided for things that can be modified. | |
var $pluginver = '3.1.11'; // Plugin version | |
var $agshver = false; // Alex Gorbatchev's SyntaxHighlighter version (dynamically set below due to v2 vs v3) | |
var $shfolder = false; // Controls what subfolder to load SyntaxHighlighter from (v2 or v3) | |
var $settings = array(); // Contains the user's settings | |
var $defaultsettings = array(); // Contains the default settings | |
var $brushes = array(); // Array of aliases => brushes | |
var $shortcodes = array(); // Array of shortcodes to use | |
var $themes = array(); // Array of themes | |
var $usedbrushes = array(); // Stores used brushes so we know what to output | |
var $encoded = false; // Used to mark that a character encode took place | |
var $codeformat = false; // If set, SyntaxHighlighter::get_code_format() will return this value | |
var $content_save_pre_ran = false; // It's possible for the "content_save_pre" filter to run multiple times, so keep track | |
// Initalize the plugin by registering the hooks | |
function __construct() { | |
if ( ! function_exists( 'esc_html' ) ) | |
return; | |
// Load localization domain | |
load_plugin_textdomain( 'syntaxhighlighter', false, '/syntaxhighlighter/localization' ); | |
// 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_head', array( $this, 'output_header_placeholder' ), 15 ); | |
add_action( 'admin_head', array( $this, 'output_header_placeholder' ), 15 ); // For comments | |
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 ); | |
// Register widget hooks | |
// Requires change added in WordPress 2.9 | |
if ( class_exists('WP_Embed') ) { | |
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 | |
) ); | |
// 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'; | |
} | |
// 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-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-groovy', plugins_url( $this->shfolder . '/scripts/shBrushGroovy.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-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 ); | |
// 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 ); | |
// 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', | |
'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', | |
'groovy' => 'groovy', | |
'java' => 'java', | |
'jfx' => 'javafx', | |
'javafx' => 'javafx', | |
'js' => 'jscript', | |
'jscript' => 'jscript', | |
'javascript' => 'jscript', | |
'latex' => 'latex', // Not used as a shortcode | |
'tex' => 'latex', | |
'matlab' => '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', | |
'vb' => 'vb', | |
'vbnet' => 'vb', | |
'xml' => 'xml', | |
'xhtml' => 'xml', | |
'xslt' => 'xml', | |
'html' => 'xml', | |
) ); | |
// 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_true' ); | |
// 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' ) ); | |
} | |
// Add the custom TinyMCE plugin which wraps plugin shortcodes in <pre> 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[] = '<a href="' . admin_url( 'options-general.php?page=syntaxhighlighter' ) . '">' . __( 'Settings', 'syntaxhighlighter' ) . '</a>'; | |
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 "<script type='text/javascript'>\n"; | |
echo " var syntaxHLcodes = '" . implode( '|', $shortcodes ) . "';\n"; | |
echo "</script>\n"; | |
} | |
// A filter function that runs do_shortcode() but only with this plugin's shortcodes | |
function shortcode_hack( $content, $callback ) { | |
global $shortcode_tags; | |
// Backup current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
// Register all of this plugin's shortcodes | |
foreach ( $this->shortcodes as $shortcode ) | |
add_shortcode( $shortcode, $callback ); | |
// Do the shortcodes (only this plugins's are registered) | |
$content = $this->do_shortcode_keep_escaped_tags( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
// This is a clone of do_shortcode() that uses a different callback function | |
// The new callback function will keep escaped tags escaped, i.e. [[foo]] | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_keep_escaped_tags( $content ) { | |
global $shortcode_tags; | |
if (empty($shortcode_tags) || !is_array($shortcode_tags)) | |
return $content; | |
$pattern = get_shortcode_regex(); | |
return preg_replace_callback('/'.$pattern.'/s', array( $this, 'do_shortcode_tag_keep_escaped_tags' ), $content); | |
} | |
// Callback for above do_shortcode_keep_escaped_tags() function | |
// It's a clone of core's do_shortcode_tag() function with a modification to the escaped shortcode return | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_tag_keep_escaped_tags( $m ) { | |
global $shortcode_tags; | |
// allow [[foo]] syntax for escaping a tag | |
if ( $m[1] == '[' && $m[6] == ']' ) { | |
return $m[0]; // This line was modified for this plugin (no substr call) | |
} | |
$tag = $m[2]; | |
$attr = shortcode_parse_atts( $m[3] ); | |
if ( isset( $m[5] ) ) { | |
// enclosing tag - extra parameter | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; | |
} else { | |
// self-closing tag | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, NULL, $tag ) . $m[6]; | |
} | |
} | |
// 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' ) ); | |
} | |
// 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) | |
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' ) ); | |
} | |
// 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 an anchor in the header for the Javascript to use. | |
// In the <head>, we don't know if we'll need this plugin's CSS and JavaScript yet but we will in the footer. | |
function output_header_placeholder() { | |
echo '<style type="text/css" id="syntaxhighlighteranchor"></style>' . "\n"; | |
} | |
// 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 "<script type='text/javascript'>\n"; | |
echo " (function(){\n"; | |
echo " var corecss = document.createElement('link');\n"; | |
echo " var themecss = document.createElement('link');\n"; | |
if ( !is_a($wp_styles, 'WP_Styles') ) | |
$wp_styles = new WP_Styles(); | |
$needcore = false; | |
if ( 'none' == $this->settings['theme'] ) { | |
$needcore = true; | |
} else { | |
$theme = ( !empty($this->themes[$this->settings['theme']]) ) ? strtolower($this->settings['theme']) : $this->defaultsettings['theme']; | |
$theme = 'syntaxhighlighter-theme-' . $theme; | |
// See if the requested theme has been registered | |
if ( !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered[$theme]) && !empty($wp_styles->registered[$theme]->src) ) { | |
// Users can register their own stylesheet and may opt to not load the core stylesheet if they wish for some reason | |
if ( is_array($wp_styles->registered[$theme]->deps) && in_array( 'syntaxhighlighter-core', $wp_styles->registered[$theme]->deps ) ) | |
$needcore = true; | |
} | |
// Otherwise use the default theme | |
else { | |
$theme = 'syntaxhighlighter-theme-' . $this->defaultsettings['theme']; | |
$needcore = true; | |
} | |
} | |
if ( $needcore && !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered['syntaxhighlighter-core']) && !empty($wp_styles->registered['syntaxhighlighter-core']->src) ) : | |
$corecssurl = add_query_arg( 'ver', $this->agshver, $wp_styles->registered['syntaxhighlighter-core']->src ); | |
$corecssurl = apply_filters( 'syntaxhighlighter_csscoreurl', $corecssurl ); | |
?> | |
var corecssurl = "<?php echo esc_js( $corecssurl ); ?>"; | |
if ( corecss.setAttribute ) { | |
corecss.setAttribute( "rel", "stylesheet" ); | |
corecss.setAttribute( "type", "text/css" ); | |
corecss.setAttribute( "href", corecssurl ); | |
} else { | |
corecss.rel = "stylesheet"; | |
corecss.href = corecssurl; | |
} | |
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif $needcore | |
if ( 'none' != $this->settings['theme'] ) : ?> | |
var themecssurl = "<?php echo esc_js( apply_filters( 'syntaxhighlighter_cssthemeurl', add_query_arg( 'ver', $this->agshver, $wp_styles->registered[$theme]->src ) ) ); ?>"; | |
if ( themecss.setAttribute ) { | |
themecss.setAttribute( "rel", "stylesheet" ); | |
themecss.setAttribute( "type", "text/css" ); | |
themecss.setAttribute( "href", themecssurl ); | |
} else { | |
themecss.rel = "stylesheet"; | |
themecss.href = themecssurl; | |
} | |
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss); | |
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif none != theme | |
echo " })();\n"; | |
switch ( $this->settings['shversion'] ) { | |
case 2: | |
echo " SyntaxHighlighter.config.clipboardSwf = '" . esc_js( apply_filters( 'syntaxhighlighter_clipboardurl', plugins_url( 'syntaxhighlighter2/scripts/clipboard.swf', __FILE__ ) ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( 'show source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.viewSource = '" . $this->js_escape_singlequotes( __( 'view source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboard = '" . $this->js_escape_singlequotes( __( 'copy to clipboard', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboardConfirmation = '" . $this->js_escape_singlequotes( __( 'The code is in your clipboard now', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.print = '" . $this->js_escape_singlequotes( __( 'print', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
case 3: | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( '+ expand source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
} | |
if ( 1 != $this->settings['autolinks'] ) | |
echo " SyntaxHighlighter.defaults['auto-links'] = false;\n"; | |
if ( !empty($this->settings['classname']) ) | |
echo " SyntaxHighlighter.defaults['class-name'] = '" . $this->js_escape_singlequotes( $this->settings['classname'] ) . "';\n"; | |
if ( 1 == $this->settings['collapse'] ) | |
echo " SyntaxHighlighter.defaults['collapse'] = true;\n"; | |
if ( 1 != $this->settings['firstline'] ) | |
echo " SyntaxHighlighter.defaults['first-line'] = " . $this->settings['firstline'] . ";\n"; | |
if ( 1 != $this->settings['gutter'] ) | |
echo " SyntaxHighlighter.defaults['gutter'] = false;\n"; | |
/* | |
if ( 1 == $this->settings['htmlscript'] ) | |
echo " SyntaxHighlighter.defaults['html-script'] = true;\n"; | |
*/ | |
if ( 1 == $this->settings['light'] ) | |
echo " SyntaxHighlighter.defaults['light'] = true;\n"; | |
echo " SyntaxHighlighter.defaults['pad-line-numbers'] = "; | |
switch ( $this->settings['padlinenumbers'] ) { | |
case 'true': | |
echo 'true'; | |
break; | |
case 'false'; | |
echo 'false'; | |
break; | |
default; | |
echo (int) $this->settings['padlinenumbers']; | |
} | |
echo ";\n"; | |
if ( 1 != $this->settings['smarttabs'] ) | |
echo " SyntaxHighlighter.defaults['smart-tabs'] = false;\n"; | |
if ( 4 != $this->settings['tabsize'] ) | |
echo " SyntaxHighlighter.defaults['tab-size'] = " . $this->settings['tabsize'] . ";\n"; | |
if ( 1 != $this->settings['toolbar'] ) | |
echo " SyntaxHighlighter.defaults['toolbar'] = false;\n"; | |
// 2.x only for now | |
if ( 1 != $this->settings['wraplines'] ) | |
echo " SyntaxHighlighter.defaults['wrap-lines'] = false;\n"; | |
?> SyntaxHighlighter.all(); | |
</script> | |
<?php | |
} | |
// No-name attribute fixing | |
function attributefix( $atts = array() ) { | |
if ( empty($atts[0]) ) | |
return $atts; | |
// Quoted value | |
if ( 0 !== preg_match( '#=("|\')(.*?)\1#', $atts[0], $match ) ) | |
$atts[0] = $match[2]; | |
// Unquoted value | |
elseif ( '=' == substr( $atts[0], 0, 1 ) ) | |
$atts[0] = substr( $atts[0], 1 ); | |
return $atts; | |
} | |
// Shortcode handler for transforming the shortcodes to their final <pre>'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, | |
), $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', | |
); | |
// 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', | |
) ); | |
$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 ); | |
$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', '<pre class="' . esc_attr( implode( ' ', $params ) ) . '"' . $title . '>' . $code . '</pre>' );; | |
} | |
// Settings page | |
function settings_page() { ?> | |
<script type="text/javascript"> | |
// <![CDATA[ | |
jQuery(document).ready(function($) { | |
// Confirm pressing of the "Reset to Defaults" button | |
$("#syntaxhighlighter-defaults").click(function(){ | |
var areyousure = confirm("<?php echo esc_js( __( 'Are you sure you want to reset your settings to the defaults?', 'syntaxhighlighter' ) ); ?>"); | |
if ( true != areyousure ) return false; | |
}); | |
<?php if ( !empty( $_GET['defaults'] ) ) : ?> | |
$("#message p strong").text("<?php echo esc_js( __( 'Settings reset to defaults.', 'syntaxhighlighter' ) ); ?>"); | |
<?php endif; ?> | |
}); | |
// ]]> | |
</script> | |
<div class="wrap"> | |
<?php if ( function_exists('screen_icon') ) screen_icon(); ?> | |
<h2><?php _e( 'SyntaxHighlighter Settings', 'syntaxhighlighter' ); ?></h2> | |
<form method="post" action="options.php"> | |
<?php settings_fields('syntaxhighlighter_settings'); ?> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-shversion"><?php _e( 'Highlighter Version', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[shversion]" id="syntaxhighlighter-shversion" class="postform"> | |
<?php | |
$versions = array( | |
3 => __( 'Version 3.x', 'syntaxhighlighter' ), | |
2 => __( 'Version 2.x', 'syntaxhighlighter' ), | |
); | |
foreach ( $versions as $version => $name ) { | |
echo ' <option value="' . esc_attr( $version ) . '"' . selected( $this->settings['shversion'], $version, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select><br /> | |
<?php _e( 'Version 3 allows visitors to easily highlight portions of your code with their mouse (either by dragging or double-clicking) and copy it to their clipboard. No toolbar containing a Flash-based button is required.', 'syntaxhighlighter' ); ?><br /> | |
<?php _e( 'Version 2 allows for line wrapping, something that version 3 does not do at this time.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-theme"><?php _e( 'Color Theme', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[theme]" id="syntaxhighlighter-theme" class="postform"> | |
<?php | |
foreach ( $this->themes as $theme => $name ) { | |
echo ' <option value="' . esc_attr( $theme ) . '"' . selected( $this->settings['theme'], $theme, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-loadallbrushes"><input name="syntaxhighlighter_settings[loadallbrushes]" type="checkbox" id="syntaxhighlighter-loadallbrushes" value="1" <?php checked( $this->settings['loadallbrushes'], 1 ); ?> /> <?php _e( 'Always load all language files (for directly using <code><pre></code> tags rather than shortcodes)<br /> If left unchecked (default), then language files will only be loaded when needed<br /> If unsure, leave this box unchecked', 'syntaxhighlighter' ); ?></label> | |
</fieldset> | |
</td> | |
</tr> | |
</table> | |
<h3><?php _e( 'Defaults', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'All of the settings below can be configured on a per-code block basis, but you can control the defaults of all code blocks here.', 'syntaxhighlighter' ); ?></p> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-gutter"><input name="syntaxhighlighter_settings[gutter]" type="checkbox" id="syntaxhighlighter-gutter" value="1" <?php checked( $this->settings['gutter'], 1 ); ?> /> <?php _e( 'Display line numbers', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-toolbar"><input name="syntaxhighlighter_settings[toolbar]" type="checkbox" id="syntaxhighlighter-toolbar" value="1" <?php checked( $this->settings['toolbar'], 1 ); ?> /> <?php _e( 'Display the toolbar', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-autolinks"><input name="syntaxhighlighter_settings[autolinks]" type="checkbox" id="syntaxhighlighter-autolinks" value="1" <?php checked( $this->settings['autolinks'], 1 ); ?> /> <?php _e( 'Automatically make URLs clickable', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-collapse"><input name="syntaxhighlighter_settings[collapse]" type="checkbox" id="syntaxhighlighter-collapse" value="1" <?php checked( $this->settings['collapse'], 1 ); ?> /> <?php _e( 'Collapse code boxes', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-light"><input name="syntaxhighlighter_settings[light]" type="checkbox" id="syntaxhighlighter-light" value="1" <?php checked( $this->settings['light'], 1 ); ?> /> <?php _e( 'Use the light display mode, best for single lines of code', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-smarttabs"><input name="syntaxhighlighter_settings[smarttabs]" type="checkbox" id="syntaxhighlighter-smarttabs" value="1" <?php checked( $this->settings['smarttabs'], 1 ); ?> /> <?php _e( 'Use smart tabs allowing tabs being used for alignment', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-wraplines"><input name="syntaxhighlighter_settings[wraplines]" type="checkbox" id="syntaxhighlighter-wraplines" value="1" <?php checked( $this->settings['wraplines'], 1 ); ?> /> <?php _e( 'Wrap long lines (v2.x only, disabling this will make a scrollbar show instead)', 'syntaxhighlighter' ); ?></label><br /> | |
<!--<label for="syntaxhighlighter-htmlscript"><input name="syntaxhighlighter_settings[htmlscript]" type="checkbox" id="syntaxhighlighter-htmlscript" value="1" <?php checked( $this->settings['htmlscript'], 1 ); ?> /> <?php _e( 'Enable "HTML script" mode by default (see the bottom of this page for details). Checking this box is not recommended as this mode only works with certain languages.', 'syntaxhighlighter' ); ?></label>--> | |
</fieldset> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-classname"><?php _e( 'Additional CSS Class(es)', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[classname]" type="text" id="syntaxhighlighter-classname" value="<?php echo esc_attr( $this->settings['classname'] ); ?>" class="regular-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-firstline"><?php _e( 'Starting Line Number', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[firstline]" type="text" id="syntaxhighlighter-firstline" value="<?php echo esc_attr( $this->settings['firstline'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-padlinenumbers"><?php _e( 'Line Number Padding', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[padlinenumbers]" id="syntaxhighlighter-padlinenumbers" class="postform"> | |
<?php | |
$linepaddings = array( | |
'false' => __( 'Off', 'syntaxhighlighter' ), | |
'true' => __( 'Automatic', 'syntaxhighlighter' ), | |
1 => 1, | |
2 => 2, | |
3 => 3, | |
4 => 4, | |
5 => 5, | |
); | |
foreach ( $linepaddings as $value => $name ) { | |
echo ' <option value="' . esc_attr( $value ) . '"' . selected( $this->settings['padlinenumbers'], $value, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-tabsize"><?php _e( 'Tab Size', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[tabsize]" type="text" id="syntaxhighlighter-tabsize" value="<?php echo esc_attr( $this->settings['tabsize'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-title"><?php _e( 'Title', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<input name="syntaxhighlighter_settings[title]" type="text" id="syntaxhighlighter-title" value="<?php echo esc_attr( $this->settings['title'] ); ?>" class="regular-text" /><br /> | |
<?php _e( 'Some optional default text to display above each code block or as the clickable text for collapsed code blocks.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
</table> | |
<p class="submit"> | |
<?php | |
if ( function_exists( 'submit_button' ) ) { | |
submit_button( null, 'primary', 'syntaxhighlighter-submit', false ); | |
echo ' '; | |
submit_button( __( 'Reset to Defaults', 'syntaxhighlighter' ), 'primary', 'syntaxhighlighter-defaults', false ); | |
} else { | |
echo '<input type="submit" name="syntaxhighlighter-submit" class="button-primary" value="' . __( 'Save Changes') . '" />' . "\n"; | |
echo '<input type="submit" name="syntaxhighlighter-defaults" id="syntaxhighlighter-defaults" class="button-primary" value="' . __( 'Reset to Defaults', 'syntaxhighlighter' ) . '" />' . "\n"; | |
} | |
?> | |
</p> | |
</form> | |
<h3><?php _e( 'Preview', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'Click "Save Changes" to update this preview.', 'syntaxhighlighter' ); ?> | |
<?php | |
echo '<div'; | |
if ( ! empty( $GLOBALS['content_width'] ) ) | |
echo ' style="max-width:' . intval( $GLOBALS['content_width'] ) . 'px"'; | |
echo '>'; | |
$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 . ']<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>PHP Code Example</title> | |
</head> | |
<body> | |
<h1>' . __( 'PHP Code Example', 'syntaxhighlighter' ) . '</h1> | |
<p><?php echo \'' . __( 'Hello World!', 'syntaxhighlighter' ) . '\'; ?></p> | |
<p>' . __( 'This line is highlighted.', 'syntaxhighlighter' ) . '</p> | |
<div class="foobar"> | |
' . __( ' This is an | |
example of smart | |
tabs.', 'syntaxhighlighter' ) . ' | |
</div> | |
<p><a href="http://wordpress.org/">' . __( 'WordPress' ) . '</a></p> | |
</body> | |
</html>[/sourcecode]' ); | |
$this->codeformat = 1; | |
echo $this->parse_shortcodes( $democode ); | |
$this->codeformat = false; | |
echo '</div>'; | |
?> | |
<h3 style="margin-top:30px"><?php _e( 'Shortcode Parameters', 'syntaxhighlighter' ); ?></h3> | |
<p><?php printf( __( 'These are the parameters you can pass to the shortcode and what they do. For the booleans (i.e. on/off), pass %1$s/%2$s or %3$s/%4$s.', 'syntaxhighlighter' ), '<code>true</code>', '<code>1</code>', '<code>false</code>', '<code>0</code>' ); ?></p> | |
<ul class="ul-disc"> | |
<li><?php printf( _x( '%1$s or %2$s — The language syntax to highlight with. You can alternately just use that as the tag, such as <code>[php]code[/php]</code>. <a href="%3$s">Click here</a> for a list of valid tags (under "aliases").', 'language parameter', 'syntaxhighlighter' ), '<code>lang</code>', '<code>language</code>', 'http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle automatic URL linking.', 'autolinks parameter', 'syntaxhighlighter' ), '<code>autolinks</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Add an additional CSS class to the code box.', 'classname parameter', 'syntaxhighlighter' ), '<code>classname</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle collapsing the code box by default, requiring a click to expand it. Good for large code posts.', 'collapse parameter', 'syntaxhighlighter' ), '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — An interger specifying what number the first line should be (for the line numbering).', 'firstline parameter', 'syntaxhighlighter' ), '<code>firstline</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the left-side line numbering.', 'gutter parameter', 'syntaxhighlighter' ), '<code>gutter</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s — A comma-separated list of line numbers to highlight. You can also specify a range. Example: %2$s', 'highlight parameter', 'syntaxhighlighter' ), '<code>highlight</code>', '<code>2,5-10,12</code>' ); ?></li> | |
<li><?php printf( _x( "%s — Toggle highlighting any extra HTML/XML. Good for when you're mixing HTML/XML with another language, such as having PHP inside an HTML web page. The above preview has it enabled for example. This only works with certain languages.", 'htmlscript parameter', 'syntaxhighlighter' ), '<code>htmlscript</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle light mode which disables the gutter and toolbar all at once.', 'light parameter', 'syntaxhighlighter' ), '<code>light</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Controls line number padding. Valid values are <code>false</code> (no padding), <code>true</code> (automatic padding), or an integer (forced padding).', 'padlinenumbers parameter', 'syntaxhighlighter' ), '<code>padlinenumbers</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s (v3 only) — Sets some text to show up before the code. Very useful when combined with the %2$s parameter.', 'title parameter', 'syntaxhighlighter' ), '<code>title</code>', '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the toolbar (buttons in v2, the about question mark in v3)', 'toolbar parameter', 'syntaxhighlighter' ), '<code>toolbar</code>' ); ?></li> | |
<li><?php printf( _x( '%s (v2 only) — Toggle line wrapping.', 'wraplines parameter', 'syntaxhighlighter'), '<code>wraplines</code>' ); ?></li> | |
</ul> | |
<p><?php _e( 'Some example shortcodes:', 'syntaxhighlighter' ); ?></p> | |
<ul class="ul-disc"> | |
<li><code>[php]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/php]</code></li> | |
<li><code>[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="<?php _e( 'example-filename.php', 'syntaxhighlighter' ); ?>"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/css]</code></li> | |
<li><code>[code lang="js"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/code]</code></li> | |
<li><code>[sourcecode language="plain"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/sourcecode]</code></li> | |
</ul> | |
<?php $this->maybe_output_scripts(); ?> | |
</div> | |
<?php | |
} | |
// Validate the settings sent from the settings page | |
function validate_settings( $settings ) { | |
if ( !empty($_POST['syntaxhighlighter-defaults']) ) { | |
$settings = $this->defaultsettings; | |
$_REQUEST['_wp_http_referer'] = add_query_arg( 'defaults', 'true', $_REQUEST['_wp_http_referer'] ); | |
} else { | |
$settings['shversion'] = ( ! empty($settings['shversion']) && 2 == $settings['shversion'] ) ? 2 : 3; | |
$settings['theme'] = ( ! empty($settings['theme']) && isset($this->themes[$settings['theme']]) ) ? strtolower($settings['theme']) : $this->defaultsettings['theme']; | |
$settings['loadallbrushes'] = ( ! empty($settings['loadallbrushes']) ) ? 1 : 0; | |
$settings['autolinks'] = ( ! empty($settings['autolinks']) ) ? 1 : 0; | |
$settings['collapse'] = ( ! empty($settings['collapse']) ) ? 1 : 0; | |
$settings['gutter'] = ( ! empty($settings['gutter']) ) ? 1 : 0; | |
$settings['light'] = ( ! empty($settings['light']) ) ? 1 : 0; | |
$settings['smarttabs'] = ( ! empty($settings['smarttabs']) ) ? 1 : 0; | |
$settings['toolbar'] = ( ! empty($settings['toolbar']) ) ? 1 : 0; // May be overridden below | |
$settings['wraplines'] = ( ! empty($settings['wraplines']) ) ? 1 : 0; // 2.x only for now | |
// If the version changed, then force change the toolbar version setting | |
if ( $settings['shversion'] != $this->settings['shversion'] ) { | |
$settings['toolbar'] = ( 2 == $settings['shversion'] ) ? 1 : 0; | |
} | |
if ( 'true' != $settings['padlinenumbers'] && 'false' != $settings['padlinenumbers'] ) | |
$settings['padlinenumbers'] = (int) $settings['padlinenumbers']; | |
$settings['classname'] = ( !empty($settings['classname']) ) ? preg_replace( '/[^ A-Za-z0-9_-]*/', '', $settings['classname'] ) : ''; | |
$settings['firstline'] = (int) ( !empty($settings['firstline']) ) ? $settings['firstline'] : $this->defaultsettings['firstline']; | |
$settings['tabsize'] = (int) ( !empty($settings['tabsize']) ) ? $settings['tabsize'] : $this->defaultsettings['tabsize']; | |
} | |
return $settings; | |
} | |
// PHP4 compatibility | |
function SyntaxHighlighter() { | |
$this->__construct(); | |
} | |
} | |
// Start this plugin once all other plugins are fully loaded | |
add_action( 'init', 'SyntaxHighlighter', 5 ); | |
function SyntaxHighlighter() { | |
global $SyntaxHighlighter; | |
$SyntaxHighlighter = new SyntaxHighlighter(); | |
} | |
?> |
Yep, the eyes do not deceive. This is exactly the same method embed
uses, except applied with a 7
instead of 8
and use the dummy function __return_true
instead of __return_false
. If you look around, you’ll start to see this technique used a lot. Despite this, somehow I don’t get the feeling this is going to make the next edition of anyone’s programming design patterns book—unless the title of the book is WordPress Development Hacks. (Note to self: write WordPress Development Hacks.)
But then how does it actually protect that when the_content 10
triggers wpautop
?
<?php /* | |
************************************************************************** | |
Plugin Name: SyntaxHighlighter Evolved | |
Plugin URI: http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/ | |
Version: 3.1.11 | |
Description: Easily post syntax-highlighted code to your site without having to modify the code at all. Uses Alex Gorbatchev's <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter">SyntaxHighlighter</a>. <strong>TIP:</strong> Don't use the Visual editor if you don't want your code mangled. TinyMCE will "clean up" your HTML. | |
Author: Alex Mills (Viper007Bond) | |
Author URI: http://www.viper007bond.com/ | |
************************************************************************** | |
Thanks to: | |
* Alex Gorbatchev for writing the Javascript-powered synatax highlighter script | |
* Andrew Ozz for writing the TinyMCE plugin | |
**************************************************************************/ | |
class SyntaxHighlighter { | |
// All of these variables are private. Filters are provided for things that can be modified. | |
var $pluginver = '3.1.11'; // Plugin version | |
var $agshver = false; // Alex Gorbatchev's SyntaxHighlighter version (dynamically set below due to v2 vs v3) | |
var $shfolder = false; // Controls what subfolder to load SyntaxHighlighter from (v2 or v3) | |
var $settings = array(); // Contains the user's settings | |
var $defaultsettings = array(); // Contains the default settings | |
var $brushes = array(); // Array of aliases => brushes | |
var $shortcodes = array(); // Array of shortcodes to use | |
var $themes = array(); // Array of themes | |
var $usedbrushes = array(); // Stores used brushes so we know what to output | |
var $encoded = false; // Used to mark that a character encode took place | |
var $codeformat = false; // If set, SyntaxHighlighter::get_code_format() will return this value | |
var $content_save_pre_ran = false; // It's possible for the "content_save_pre" filter to run multiple times, so keep track | |
// Initalize the plugin by registering the hooks | |
function __construct() { | |
if ( ! function_exists( 'esc_html' ) ) | |
return; | |
// Load localization domain | |
load_plugin_textdomain( 'syntaxhighlighter', false, '/syntaxhighlighter/localization' ); | |
// 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_head', array( $this, 'output_header_placeholder' ), 15 ); | |
add_action( 'admin_head', array( $this, 'output_header_placeholder' ), 15 ); // For comments | |
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 ); | |
// Register widget hooks | |
// Requires change added in WordPress 2.9 | |
if ( class_exists('WP_Embed') ) { | |
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 | |
) ); | |
// 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'; | |
} | |
// 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-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-groovy', plugins_url( $this->shfolder . '/scripts/shBrushGroovy.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-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 ); | |
// 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 ); | |
// 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', | |
'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', | |
'groovy' => 'groovy', | |
'java' => 'java', | |
'jfx' => 'javafx', | |
'javafx' => 'javafx', | |
'js' => 'jscript', | |
'jscript' => 'jscript', | |
'javascript' => 'jscript', | |
'latex' => 'latex', // Not used as a shortcode | |
'tex' => 'latex', | |
'matlab' => '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', | |
'vb' => 'vb', | |
'vbnet' => 'vb', | |
'xml' => 'xml', | |
'xhtml' => 'xml', | |
'xslt' => 'xml', | |
'html' => 'xml', | |
) ); | |
// 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_true' ); | |
// 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' ) ); | |
} | |
// Add the custom TinyMCE plugin which wraps plugin shortcodes in <pre> 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[] = '<a href="' . admin_url( 'options-general.php?page=syntaxhighlighter' ) . '">' . __( 'Settings', 'syntaxhighlighter' ) . '</a>'; | |
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 "<script type='text/javascript'>\n"; | |
echo " var syntaxHLcodes = '" . implode( '|', $shortcodes ) . "';\n"; | |
echo "</script>\n"; | |
} | |
// A filter function that runs do_shortcode() but only with this plugin's shortcodes | |
function shortcode_hack( $content, $callback ) { | |
global $shortcode_tags; | |
// Backup current registered shortcodes and clear them all out | |
$orig_shortcode_tags = $shortcode_tags; | |
remove_all_shortcodes(); | |
// Register all of this plugin's shortcodes | |
foreach ( $this->shortcodes as $shortcode ) | |
add_shortcode( $shortcode, $callback ); | |
// Do the shortcodes (only this plugins's are registered) | |
$content = $this->do_shortcode_keep_escaped_tags( $content ); | |
// Put the original shortcodes back | |
$shortcode_tags = $orig_shortcode_tags; | |
return $content; | |
} | |
// This is a clone of do_shortcode() that uses a different callback function | |
// The new callback function will keep escaped tags escaped, i.e. [[foo]] | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_keep_escaped_tags( $content ) { | |
global $shortcode_tags; | |
if (empty($shortcode_tags) || !is_array($shortcode_tags)) | |
return $content; | |
$pattern = get_shortcode_regex(); | |
return preg_replace_callback('/'.$pattern.'/s', array( $this, 'do_shortcode_tag_keep_escaped_tags' ), $content); | |
} | |
// Callback for above do_shortcode_keep_escaped_tags() function | |
// It's a clone of core's do_shortcode_tag() function with a modification to the escaped shortcode return | |
// Up to date as of r18324 (3.2) | |
function do_shortcode_tag_keep_escaped_tags( $m ) { | |
global $shortcode_tags; | |
// allow [[foo]] syntax for escaping a tag | |
if ( $m[1] == '[' && $m[6] == ']' ) { | |
return $m[0]; // This line was modified for this plugin (no substr call) | |
} | |
$tag = $m[2]; | |
$attr = shortcode_parse_atts( $m[3] ); | |
if ( isset( $m[5] ) ) { | |
// enclosing tag - extra parameter | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; | |
} else { | |
// self-closing tag | |
return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, NULL, $tag ) . $m[6]; | |
} | |
} | |
// 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' ) ); | |
} | |
// 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) | |
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' ) ); | |
} | |
// 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 an anchor in the header for the Javascript to use. | |
// In the <head>, we don't know if we'll need this plugin's CSS and JavaScript yet but we will in the footer. | |
function output_header_placeholder() { | |
echo '<style type="text/css" id="syntaxhighlighteranchor"></style>' . "\n"; | |
} | |
// 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 "<script type='text/javascript'>\n"; | |
echo " (function(){\n"; | |
echo " var corecss = document.createElement('link');\n"; | |
echo " var themecss = document.createElement('link');\n"; | |
if ( !is_a($wp_styles, 'WP_Styles') ) | |
$wp_styles = new WP_Styles(); | |
$needcore = false; | |
if ( 'none' == $this->settings['theme'] ) { | |
$needcore = true; | |
} else { | |
$theme = ( !empty($this->themes[$this->settings['theme']]) ) ? strtolower($this->settings['theme']) : $this->defaultsettings['theme']; | |
$theme = 'syntaxhighlighter-theme-' . $theme; | |
// See if the requested theme has been registered | |
if ( !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered[$theme]) && !empty($wp_styles->registered[$theme]->src) ) { | |
// Users can register their own stylesheet and may opt to not load the core stylesheet if they wish for some reason | |
if ( is_array($wp_styles->registered[$theme]->deps) && in_array( 'syntaxhighlighter-core', $wp_styles->registered[$theme]->deps ) ) | |
$needcore = true; | |
} | |
// Otherwise use the default theme | |
else { | |
$theme = 'syntaxhighlighter-theme-' . $this->defaultsettings['theme']; | |
$needcore = true; | |
} | |
} | |
if ( $needcore && !empty($wp_styles) && !empty($wp_styles->registered) && !empty($wp_styles->registered['syntaxhighlighter-core']) && !empty($wp_styles->registered['syntaxhighlighter-core']->src) ) : | |
$corecssurl = add_query_arg( 'ver', $this->agshver, $wp_styles->registered['syntaxhighlighter-core']->src ); | |
$corecssurl = apply_filters( 'syntaxhighlighter_csscoreurl', $corecssurl ); | |
?> | |
var corecssurl = "<?php echo esc_js( $corecssurl ); ?>"; | |
if ( corecss.setAttribute ) { | |
corecss.setAttribute( "rel", "stylesheet" ); | |
corecss.setAttribute( "type", "text/css" ); | |
corecss.setAttribute( "href", corecssurl ); | |
} else { | |
corecss.rel = "stylesheet"; | |
corecss.href = corecssurl; | |
} | |
document.getElementsByTagName("head")[0].insertBefore( corecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif $needcore | |
if ( 'none' != $this->settings['theme'] ) : ?> | |
var themecssurl = "<?php echo esc_js( apply_filters( 'syntaxhighlighter_cssthemeurl', add_query_arg( 'ver', $this->agshver, $wp_styles->registered[$theme]->src ) ) ); ?>"; | |
if ( themecss.setAttribute ) { | |
themecss.setAttribute( "rel", "stylesheet" ); | |
themecss.setAttribute( "type", "text/css" ); | |
themecss.setAttribute( "href", themecssurl ); | |
} else { | |
themecss.rel = "stylesheet"; | |
themecss.href = themecssurl; | |
} | |
//document.getElementById("syntaxhighlighteranchor").appendChild(themecss); | |
document.getElementsByTagName("head")[0].insertBefore( themecss, document.getElementById("syntaxhighlighteranchor") ); | |
<?php | |
endif; // Endif none != theme | |
echo " })();\n"; | |
switch ( $this->settings['shversion'] ) { | |
case 2: | |
echo " SyntaxHighlighter.config.clipboardSwf = '" . esc_js( apply_filters( 'syntaxhighlighter_clipboardurl', plugins_url( 'syntaxhighlighter2/scripts/clipboard.swf', __FILE__ ) ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( 'show source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.viewSource = '" . $this->js_escape_singlequotes( __( 'view source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboard = '" . $this->js_escape_singlequotes( __( 'copy to clipboard', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.copyToClipboardConfirmation = '" . $this->js_escape_singlequotes( __( 'The code is in your clipboard now', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.print = '" . $this->js_escape_singlequotes( __( 'print', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
case 3: | |
echo " SyntaxHighlighter.config.strings.expandSource = '" . $this->js_escape_singlequotes( __( '+ expand source', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.help = '" . $this->js_escape_singlequotes( __( '?', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.alert = '" . $this->js_escape_singlequotes( __( 'SyntaxHighlighter\n\n', 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.noBrush = '" . $this->js_escape_singlequotes( __( "Can't find brush for: ", 'syntaxhighlighter' ) ) . "';\n"; | |
echo " SyntaxHighlighter.config.strings.brushNotHtmlScript = '" . $this->js_escape_singlequotes( __( "Brush wasn't configured for html-script option: ", 'syntaxhighlighter' ) ) . "';\n"; | |
break; | |
} | |
if ( 1 != $this->settings['autolinks'] ) | |
echo " SyntaxHighlighter.defaults['auto-links'] = false;\n"; | |
if ( !empty($this->settings['classname']) ) | |
echo " SyntaxHighlighter.defaults['class-name'] = '" . $this->js_escape_singlequotes( $this->settings['classname'] ) . "';\n"; | |
if ( 1 == $this->settings['collapse'] ) | |
echo " SyntaxHighlighter.defaults['collapse'] = true;\n"; | |
if ( 1 != $this->settings['firstline'] ) | |
echo " SyntaxHighlighter.defaults['first-line'] = " . $this->settings['firstline'] . ";\n"; | |
if ( 1 != $this->settings['gutter'] ) | |
echo " SyntaxHighlighter.defaults['gutter'] = false;\n"; | |
/* | |
if ( 1 == $this->settings['htmlscript'] ) | |
echo " SyntaxHighlighter.defaults['html-script'] = true;\n"; | |
*/ | |
if ( 1 == $this->settings['light'] ) | |
echo " SyntaxHighlighter.defaults['light'] = true;\n"; | |
echo " SyntaxHighlighter.defaults['pad-line-numbers'] = "; | |
switch ( $this->settings['padlinenumbers'] ) { | |
case 'true': | |
echo 'true'; | |
break; | |
case 'false'; | |
echo 'false'; | |
break; | |
default; | |
echo (int) $this->settings['padlinenumbers']; | |
} | |
echo ";\n"; | |
if ( 1 != $this->settings['smarttabs'] ) | |
echo " SyntaxHighlighter.defaults['smart-tabs'] = false;\n"; | |
if ( 4 != $this->settings['tabsize'] ) | |
echo " SyntaxHighlighter.defaults['tab-size'] = " . $this->settings['tabsize'] . ";\n"; | |
if ( 1 != $this->settings['toolbar'] ) | |
echo " SyntaxHighlighter.defaults['toolbar'] = false;\n"; | |
// 2.x only for now | |
if ( 1 != $this->settings['wraplines'] ) | |
echo " SyntaxHighlighter.defaults['wrap-lines'] = false;\n"; | |
?> SyntaxHighlighter.all(); | |
</script> | |
<?php | |
} | |
// No-name attribute fixing | |
function attributefix( $atts = array() ) { | |
if ( empty($atts[0]) ) | |
return $atts; | |
// Quoted value | |
if ( 0 !== preg_match( '#=("|\')(.*?)\1#', $atts[0], $match ) ) | |
$atts[0] = $match[2]; | |
// Unquoted value | |
elseif ( '=' == substr( $atts[0], 0, 1 ) ) | |
$atts[0] = substr( $atts[0], 1 ); | |
return $atts; | |
} | |
// Shortcode handler for transforming the shortcodes to their final <pre>'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, | |
), $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', | |
); | |
// 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', | |
) ); | |
$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 ); | |
$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', '<pre class="' . esc_attr( implode( ' ', $params ) ) . '"' . $title . '>' . $code . '</pre>' );; | |
} | |
// Settings page | |
function settings_page() { ?> | |
<script type="text/javascript"> | |
// <![CDATA[ | |
jQuery(document).ready(function($) { | |
// Confirm pressing of the "Reset to Defaults" button | |
$("#syntaxhighlighter-defaults").click(function(){ | |
var areyousure = confirm("<?php echo esc_js( __( 'Are you sure you want to reset your settings to the defaults?', 'syntaxhighlighter' ) ); ?>"); | |
if ( true != areyousure ) return false; | |
}); | |
<?php if ( !empty( $_GET['defaults'] ) ) : ?> | |
$("#message p strong").text("<?php echo esc_js( __( 'Settings reset to defaults.', 'syntaxhighlighter' ) ); ?>"); | |
<?php endif; ?> | |
}); | |
// ]]> | |
</script> | |
<div class="wrap"> | |
<?php if ( function_exists('screen_icon') ) screen_icon(); ?> | |
<h2><?php _e( 'SyntaxHighlighter Settings', 'syntaxhighlighter' ); ?></h2> | |
<form method="post" action="options.php"> | |
<?php settings_fields('syntaxhighlighter_settings'); ?> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-shversion"><?php _e( 'Highlighter Version', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[shversion]" id="syntaxhighlighter-shversion" class="postform"> | |
<?php | |
$versions = array( | |
3 => __( 'Version 3.x', 'syntaxhighlighter' ), | |
2 => __( 'Version 2.x', 'syntaxhighlighter' ), | |
); | |
foreach ( $versions as $version => $name ) { | |
echo ' <option value="' . esc_attr( $version ) . '"' . selected( $this->settings['shversion'], $version, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select><br /> | |
<?php _e( 'Version 3 allows visitors to easily highlight portions of your code with their mouse (either by dragging or double-clicking) and copy it to their clipboard. No toolbar containing a Flash-based button is required.', 'syntaxhighlighter' ); ?><br /> | |
<?php _e( 'Version 2 allows for line wrapping, something that version 3 does not do at this time.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-theme"><?php _e( 'Color Theme', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[theme]" id="syntaxhighlighter-theme" class="postform"> | |
<?php | |
foreach ( $this->themes as $theme => $name ) { | |
echo ' <option value="' . esc_attr( $theme ) . '"' . selected( $this->settings['theme'], $theme, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Load All Brushes', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-loadallbrushes"><input name="syntaxhighlighter_settings[loadallbrushes]" type="checkbox" id="syntaxhighlighter-loadallbrushes" value="1" <?php checked( $this->settings['loadallbrushes'], 1 ); ?> /> <?php _e( 'Always load all language files (for directly using <code><pre></code> tags rather than shortcodes)<br /> If left unchecked (default), then language files will only be loaded when needed<br /> If unsure, leave this box unchecked', 'syntaxhighlighter' ); ?></label> | |
</fieldset> | |
</td> | |
</tr> | |
</table> | |
<h3><?php _e( 'Defaults', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'All of the settings below can be configured on a per-code block basis, but you can control the defaults of all code blocks here.', 'syntaxhighlighter' ); ?></p> | |
<table class="form-table"> | |
<tr valign="top"> | |
<th scope="row"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></th> | |
<td> | |
<fieldset> | |
<legend class="hidden"><?php _e( 'Miscellaneous', 'syntaxhighlighter' ); ?></legend> | |
<label for="syntaxhighlighter-gutter"><input name="syntaxhighlighter_settings[gutter]" type="checkbox" id="syntaxhighlighter-gutter" value="1" <?php checked( $this->settings['gutter'], 1 ); ?> /> <?php _e( 'Display line numbers', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-toolbar"><input name="syntaxhighlighter_settings[toolbar]" type="checkbox" id="syntaxhighlighter-toolbar" value="1" <?php checked( $this->settings['toolbar'], 1 ); ?> /> <?php _e( 'Display the toolbar', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-autolinks"><input name="syntaxhighlighter_settings[autolinks]" type="checkbox" id="syntaxhighlighter-autolinks" value="1" <?php checked( $this->settings['autolinks'], 1 ); ?> /> <?php _e( 'Automatically make URLs clickable', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-collapse"><input name="syntaxhighlighter_settings[collapse]" type="checkbox" id="syntaxhighlighter-collapse" value="1" <?php checked( $this->settings['collapse'], 1 ); ?> /> <?php _e( 'Collapse code boxes', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-light"><input name="syntaxhighlighter_settings[light]" type="checkbox" id="syntaxhighlighter-light" value="1" <?php checked( $this->settings['light'], 1 ); ?> /> <?php _e( 'Use the light display mode, best for single lines of code', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-smarttabs"><input name="syntaxhighlighter_settings[smarttabs]" type="checkbox" id="syntaxhighlighter-smarttabs" value="1" <?php checked( $this->settings['smarttabs'], 1 ); ?> /> <?php _e( 'Use smart tabs allowing tabs being used for alignment', 'syntaxhighlighter' ); ?></label><br /> | |
<label for="syntaxhighlighter-wraplines"><input name="syntaxhighlighter_settings[wraplines]" type="checkbox" id="syntaxhighlighter-wraplines" value="1" <?php checked( $this->settings['wraplines'], 1 ); ?> /> <?php _e( 'Wrap long lines (v2.x only, disabling this will make a scrollbar show instead)', 'syntaxhighlighter' ); ?></label><br /> | |
<!--<label for="syntaxhighlighter-htmlscript"><input name="syntaxhighlighter_settings[htmlscript]" type="checkbox" id="syntaxhighlighter-htmlscript" value="1" <?php checked( $this->settings['htmlscript'], 1 ); ?> /> <?php _e( 'Enable "HTML script" mode by default (see the bottom of this page for details). Checking this box is not recommended as this mode only works with certain languages.', 'syntaxhighlighter' ); ?></label>--> | |
</fieldset> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-classname"><?php _e( 'Additional CSS Class(es)', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[classname]" type="text" id="syntaxhighlighter-classname" value="<?php echo esc_attr( $this->settings['classname'] ); ?>" class="regular-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-firstline"><?php _e( 'Starting Line Number', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[firstline]" type="text" id="syntaxhighlighter-firstline" value="<?php echo esc_attr( $this->settings['firstline'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-padlinenumbers"><?php _e( 'Line Number Padding', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<select name="syntaxhighlighter_settings[padlinenumbers]" id="syntaxhighlighter-padlinenumbers" class="postform"> | |
<?php | |
$linepaddings = array( | |
'false' => __( 'Off', 'syntaxhighlighter' ), | |
'true' => __( 'Automatic', 'syntaxhighlighter' ), | |
1 => 1, | |
2 => 2, | |
3 => 3, | |
4 => 4, | |
5 => 5, | |
); | |
foreach ( $linepaddings as $value => $name ) { | |
echo ' <option value="' . esc_attr( $value ) . '"' . selected( $this->settings['padlinenumbers'], $value, false ) . '>' . esc_html( $name ) . " </option>\n"; | |
} | |
?> | |
</select> | |
</td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-tabsize"><?php _e( 'Tab Size', 'syntaxhighlighter' ); ?></label></th> | |
<td><input name="syntaxhighlighter_settings[tabsize]" type="text" id="syntaxhighlighter-tabsize" value="<?php echo esc_attr( $this->settings['tabsize'] ); ?>" class="small-text" /></td> | |
</tr> | |
<tr valign="top"> | |
<th scope="row"><label for="syntaxhighlighter-title"><?php _e( 'Title', 'syntaxhighlighter' ); ?></label></th> | |
<td> | |
<input name="syntaxhighlighter_settings[title]" type="text" id="syntaxhighlighter-title" value="<?php echo esc_attr( $this->settings['title'] ); ?>" class="regular-text" /><br /> | |
<?php _e( 'Some optional default text to display above each code block or as the clickable text for collapsed code blocks.', 'syntaxhighlighter' ); ?> | |
</td> | |
</tr> | |
</table> | |
<p class="submit"> | |
<?php | |
if ( function_exists( 'submit_button' ) ) { | |
submit_button( null, 'primary', 'syntaxhighlighter-submit', false ); | |
echo ' '; | |
submit_button( __( 'Reset to Defaults', 'syntaxhighlighter' ), 'primary', 'syntaxhighlighter-defaults', false ); | |
} else { | |
echo '<input type="submit" name="syntaxhighlighter-submit" class="button-primary" value="' . __( 'Save Changes') . '" />' . "\n"; | |
echo '<input type="submit" name="syntaxhighlighter-defaults" id="syntaxhighlighter-defaults" class="button-primary" value="' . __( 'Reset to Defaults', 'syntaxhighlighter' ) . '" />' . "\n"; | |
} | |
?> | |
</p> | |
</form> | |
<h3><?php _e( 'Preview', 'syntaxhighlighter' ); ?></h3> | |
<p><?php _e( 'Click "Save Changes" to update this preview.', 'syntaxhighlighter' ); ?> | |
<?php | |
echo '<div'; | |
if ( ! empty( $GLOBALS['content_width'] ) ) | |
echo ' style="max-width:' . intval( $GLOBALS['content_width'] ) . 'px"'; | |
echo '>'; | |
$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 . ']<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>PHP Code Example</title> | |
</head> | |
<body> | |
<h1>' . __( 'PHP Code Example', 'syntaxhighlighter' ) . '</h1> | |
<p><?php echo \'' . __( 'Hello World!', 'syntaxhighlighter' ) . '\'; ?></p> | |
<p>' . __( 'This line is highlighted.', 'syntaxhighlighter' ) . '</p> | |
<div class="foobar"> | |
' . __( ' This is an | |
example of smart | |
tabs.', 'syntaxhighlighter' ) . ' | |
</div> | |
<p><a href="http://wordpress.org/">' . __( 'WordPress' ) . '</a></p> | |
</body> | |
</html>[/sourcecode]' ); | |
$this->codeformat = 1; | |
echo $this->parse_shortcodes( $democode ); | |
$this->codeformat = false; | |
echo '</div>'; | |
?> | |
<h3 style="margin-top:30px"><?php _e( 'Shortcode Parameters', 'syntaxhighlighter' ); ?></h3> | |
<p><?php printf( __( 'These are the parameters you can pass to the shortcode and what they do. For the booleans (i.e. on/off), pass %1$s/%2$s or %3$s/%4$s.', 'syntaxhighlighter' ), '<code>true</code>', '<code>1</code>', '<code>false</code>', '<code>0</code>' ); ?></p> | |
<ul class="ul-disc"> | |
<li><?php printf( _x( '%1$s or %2$s — The language syntax to highlight with. You can alternately just use that as the tag, such as <code>[php]code[/php]</code>. <a href="%3$s">Click here</a> for a list of valid tags (under "aliases").', 'language parameter', 'syntaxhighlighter' ), '<code>lang</code>', '<code>language</code>', 'http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle automatic URL linking.', 'autolinks parameter', 'syntaxhighlighter' ), '<code>autolinks</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Add an additional CSS class to the code box.', 'classname parameter', 'syntaxhighlighter' ), '<code>classname</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle collapsing the code box by default, requiring a click to expand it. Good for large code posts.', 'collapse parameter', 'syntaxhighlighter' ), '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — An interger specifying what number the first line should be (for the line numbering).', 'firstline parameter', 'syntaxhighlighter' ), '<code>firstline</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the left-side line numbering.', 'gutter parameter', 'syntaxhighlighter' ), '<code>gutter</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s — A comma-separated list of line numbers to highlight. You can also specify a range. Example: %2$s', 'highlight parameter', 'syntaxhighlighter' ), '<code>highlight</code>', '<code>2,5-10,12</code>' ); ?></li> | |
<li><?php printf( _x( "%s — Toggle highlighting any extra HTML/XML. Good for when you're mixing HTML/XML with another language, such as having PHP inside an HTML web page. The above preview has it enabled for example. This only works with certain languages.", 'htmlscript parameter', 'syntaxhighlighter' ), '<code>htmlscript</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle light mode which disables the gutter and toolbar all at once.', 'light parameter', 'syntaxhighlighter' ), '<code>light</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Controls line number padding. Valid values are <code>false</code> (no padding), <code>true</code> (automatic padding), or an integer (forced padding).', 'padlinenumbers parameter', 'syntaxhighlighter' ), '<code>padlinenumbers</code>' ); ?></li> | |
<li><?php printf( _x( '%1$s (v3 only) — Sets some text to show up before the code. Very useful when combined with the %2$s parameter.', 'title parameter', 'syntaxhighlighter' ), '<code>title</code>', '<code>collapse</code>' ); ?></li> | |
<li><?php printf( _x( '%s — Toggle the toolbar (buttons in v2, the about question mark in v3)', 'toolbar parameter', 'syntaxhighlighter' ), '<code>toolbar</code>' ); ?></li> | |
<li><?php printf( _x( '%s (v2 only) — Toggle line wrapping.', 'wraplines parameter', 'syntaxhighlighter'), '<code>wraplines</code>' ); ?></li> | |
</ul> | |
<p><?php _e( 'Some example shortcodes:', 'syntaxhighlighter' ); ?></p> | |
<ul class="ul-disc"> | |
<li><code>[php]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/php]</code></li> | |
<li><code>[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="<?php _e( 'example-filename.php', 'syntaxhighlighter' ); ?>"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/css]</code></li> | |
<li><code>[code lang="js"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/code]</code></li> | |
<li><code>[sourcecode language="plain"]<?php _e( 'your code here', 'syntaxhighlighter' ); ?>[/sourcecode]</code></li> | |
</ul> | |
<?php $this->maybe_output_scripts(); ?> | |
</div> | |
<?php | |
} | |
// Validate the settings sent from the settings page | |
function validate_settings( $settings ) { | |
if ( !empty($_POST['syntaxhighlighter-defaults']) ) { | |
$settings = $this->defaultsettings; | |
$_REQUEST['_wp_http_referer'] = add_query_arg( 'defaults', 'true', $_REQUEST['_wp_http_referer'] ); | |
} else { | |
$settings['shversion'] = ( ! empty($settings['shversion']) && 2 == $settings['shversion'] ) ? 2 : 3; | |
$settings['theme'] = ( ! empty($settings['theme']) && isset($this->themes[$settings['theme']]) ) ? strtolower($settings['theme']) : $this->defaultsettings['theme']; | |
$settings['loadallbrushes'] = ( ! empty($settings['loadallbrushes']) ) ? 1 : 0; | |
$settings['autolinks'] = ( ! empty($settings['autolinks']) ) ? 1 : 0; | |
$settings['collapse'] = ( ! empty($settings['collapse']) ) ? 1 : 0; | |
$settings['gutter'] = ( ! empty($settings['gutter']) ) ? 1 : 0; | |
$settings['light'] = ( ! empty($settings['light']) ) ? 1 : 0; | |
$settings['smarttabs'] = ( ! empty($settings['smarttabs']) ) ? 1 : 0; | |
$settings['toolbar'] = ( ! empty($settings['toolbar']) ) ? 1 : 0; // May be overridden below | |
$settings['wraplines'] = ( ! empty($settings['wraplines']) ) ? 1 : 0; // 2.x only for now | |
// If the version changed, then force change the toolbar version setting | |
if ( $settings['shversion'] != $this->settings['shversion'] ) { | |
$settings['toolbar'] = ( 2 == $settings['shversion'] ) ? 1 : 0; | |
} | |
if ( 'true' != $settings['padlinenumbers'] && 'false' != $settings['padlinenumbers'] ) | |
$settings['padlinenumbers'] = (int) $settings['padlinenumbers']; | |
$settings['classname'] = ( !empty($settings['classname']) ) ? preg_replace( '/[^ A-Za-z0-9_-]*/', '', $settings['classname'] ) : ''; | |
$settings['firstline'] = (int) ( !empty($settings['firstline']) ) ? $settings['firstline'] : $this->defaultsettings['firstline']; | |
$settings['tabsize'] = (int) ( !empty($settings['tabsize']) ) ? $settings['tabsize'] : $this->defaultsettings['tabsize']; | |
} | |
return $settings; | |
} | |
// PHP4 compatibility | |
function SyntaxHighlighter() { | |
$this->__construct(); | |
} | |
} | |
// Start this plugin once all other plugins are fully loaded | |
add_action( 'init', 'SyntaxHighlighter', 5 ); | |
function SyntaxHighlighter() { | |
global $SyntaxHighlighter; | |
$SyntaxHighlighter = new SyntaxHighlighter(); | |
} | |
?> |
It wraps it in <pre>
tags—the only known and not-that-reliable-way2 to protect yourself from wpautop
.
Why not disable wpautop
and be done with
Because it’s been part of WordPress since 2002. I don’t know when it became part of core, but it’s been there as long as I’ve been blogging. The reality is if you disable it in your plugin, you are creating a nasty side-effect for everyone (both developer and content creator) out there who expects and/or depends on it.
When PHP had magic_quotes
people had to check for it before acting. But there is no habit (to my knowledge) of plugins branching on has_action('the_content', 'wpautop')
It doesn’t happen.
If you want to disable wpautop, that’s a separate plugin… or plugins for someone to install or hack into their theme. Not something for a plugin to be depending on.
For similar reasons, it’d be a bad idea to change when and where shortcode processing is done (for any but your own shortcodes), move wpautop into the shortcode handler, or otherwise replace or rename wpautop
. We live in a bizarro universe created by the what, where, when, how of wpautop
, and everyone has been coding around it for the last 13 years. (FYWP #260)
Okay, so how did we get here?
I don’t know. My guess is that because embeds were on their own line, it made sense to cause embeds to trigger before wpautop
, but because of early shortcodes like caption
, they wanted shortcode processing to be done after wpautop
. This, however caused no end to bug reports involving unintended <p>
tags appearing around lone shortcode blocks and thus a hack on a hack in core to undo the madness… but just for the outsides, thank you ma’am (because caption
needs wpautop
). (FYWP #261)
/** | |
* Don't auto-p wrap shortcodes that stand alone | |
* | |
* Ensures that shortcodes are not wrapped in `<p>...</p>`. | |
* | |
* @since 2.9.0 | |
* | |
* @param string $pee The content. | |
* @return string The filtered content. | |
*/ | |
function shortcode_unautop( $pee ) { | |
global $shortcode_tags; | |
if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) { | |
return $pee; | |
} | |
$tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) ); | |
$spaces = wp_spaces_regexp(); | |
$pattern = | |
'/' | |
. '<p>' // Opening paragraph | |
. '(?:' . $spaces . ')*+' // Optional leading whitespace | |
. '(' // 1: The shortcode | |
. '\\[' // Opening bracket | |
. "($tagregexp)" // 2: Shortcode name | |
. '(?![\\w-])' // Not followed by word character or hyphen | |
// Unroll the loop: Inside the opening shortcode tag | |
. '[^\\]\\/]*' // Not a closing bracket or forward slash | |
. '(?:' | |
. '\\/(?!\\])' // A forward slash not followed by a closing bracket | |
. '[^\\]\\/]*' // Not a closing bracket or forward slash | |
. ')*?' | |
. '(?:' | |
. '\\/\\]' // Self closing tag and closing bracket | |
. '|' | |
. '\\]' // Closing bracket | |
. '(?:' // Unroll the loop: Optionally, anything between the opening and closing shortcode tags | |
. '[^\\[]*+' // Not an opening bracket | |
. '(?:' | |
. '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag | |
. '[^\\[]*+' // Not an opening bracket | |
. ')*+' | |
. '\\[\\/\\2\\]' // Closing shortcode tag | |
. ')?' | |
. ')' | |
. ')' | |
. '(?:' . $spaces . ')*+' // optional trailing whitespace | |
. '<\\/p>' // closing paragraph | |
. '/s'; | |
return preg_replace( $pattern, '$1', $pee ); | |
} | |
(I like how even the core developers call content that has been autop
eed on, $pee
.)
Still it’s crazy there is nothing built into the wpautop
function that hard-protects a block of content from being processed by it without also generating a side-effect of generating semantic markup. It’s just as strange that there is no unique attribute placed on wpautop
-generated <p>
and <br />
tags that would make it possible for someone downstream to reverse, that doesn’t mean that people haven’t tried, it’s just that the code examples I’ve seen are so misguided and poorly-written as to cause my mood to oscillate frequently between disgust and pity. (FYWP #262)
Here’s a thought. On what planet would any output from oEmbeds want or need to be wp_autop
eed on?
I have no idea. I’m just frustrated. I’m not the only one.
FYWP.
- It’s not easy to blame WordPress for using regex parsing for this since PHP itself doesn’t make parsing anything other than PHP feasible. It could still be better coded, but due to the dependency-stability principle can’t be without introducing serious regressions. ↩
-
It’s not reliable since
<pre>
tags cannot be nested. (The way wpautop works is by nongryeedy matching the string<pre…</pre>
, replacing it with it’s own tag and then injecting the match back in. Along the way it will delete nested</pre>
close tags as improperly formatted. ↩