ID) ) { return $func($content); } $settings = rawhtml_get_post_settings($post->ID); if ( $settings['disable_' . $func] ) { return $content; } else { return $func($content); } } //Stub filters that replace the WP defaults function maybe_wptexturize($content){ return maybe_use_filter('wptexturize', $content); } function maybe_wpautop($content){ return maybe_use_filter('wpautop', $content); } function maybe_convert_chars($content){ return maybe_use_filter('convert_chars', $content); } function maybe_convert_smilies($content){ return maybe_use_filter('convert_smilies', $content); } // Disable default filters and add our conditional filters function rawhtml_add_conditional_filters($param = null){ static $filters_added = false; static $filters = array( 'the_content' => array( 'wpautop' => 10, 'wptexturize' => 10, 'convert_chars' => 10, 'convert_smilies' => 20, ), 'the_excerpt' => array( 'wpautop' => 10, 'wptexturize' => 10, 'convert_chars' => 10, 'convert_smilies' => 20, ), ); // Set up the callbacks when one of the target filters is called for the first time. // This way there's less of a chance that Raw HTML will accidentally apply a filter // that another plugin has removed (e.g. via "remove_filter('the_content', 'wpautop')"). if ( $filters_added || !isset($filters[current_filter()]) ) { return $param; } foreach ( $filters as $tag => $functions ){ foreach ( $functions as $func => $priority ){ if ( remove_filter($tag, $func, $priority) ){ add_filter( $tag, 'maybe_'.$func, $priority ); } } } $filters_added = true; return $param; } class wsRawHtmlWrappedFilter { /** * @var string */ private $callback; public function __construct($callback) { $this->callback = $callback; } /** * Apply $callback to $content unless it's been disabled for the current post. * * @param string $content * @return string */ public function maybe_apply($content) { $func = $this->callback; global $post; if ( !isset($post, $post->ID) ) { return $func($content); } $settings = rawhtml_get_post_settings($post->ID); if ( $settings['disable_' . $func] ) { return $content; } else { return $func($content); } } } class wsRawHtmlFilterInterceptor { private $filters = array( 'the_content' => array( 'wpautop' => 10, 'wptexturize' => 10, 'convert_chars' => 10, 'convert_smilies' => 20, ), 'the_excerpt' => array( 'wpautop' => 10, 'wptexturize' => 10, 'convert_chars' => 10, 'convert_smilies' => 20, ), ); private $wrapped_handlers = array(); public function __construct() { // Since WP 4.7.0 it's possible to add/remove callbacks to the current filter or action. // This means we can add the conditional filters right before the default filters would run, // which improves our ability to detect if any of the default filters have been removed // by someone else. For example, do_blocks() removes wpautop() in the_content (priority: 9). foreach($this->filters as $tag => $functions) { add_filter($tag, array($this, 'wrap_filters'), 9, 1); } } public function wrap_filters($content = '') { $tag = current_filter(); if ( !isset($this->filters[$tag]) ) { return $content; } //Find any filters that still need to be wrapped. global $wp_filter; foreach($this->filters[$tag] as $callback => $priority) { if ( !isset($wp_filter[$tag][$priority][$callback]['function']) ) { continue; } $current_callback = $wp_filter[$tag][$priority][$callback]['function']; if ( is_string($current_callback) && ($current_callback === $callback) ) { // Wrap the default callback in a conditional handler. $handler = $this->get_handler($callback); // We must update the whole list of callbacks instead of just the 'function' // member of a specific callback because $wp_filter[tag] is not a real array // but a class that implements ArrayAccess (update happens via offsetSet). $callback_list = $wp_filter[$tag][$priority]; $callback_list[$callback]['function'] = array($handler, 'maybe_apply'); $wp_filter[$tag][$priority] = $callback_list; } } return $content; } /** * @param string $callback * @return wsRawHtmlWrappedFilter */ private function get_handler($callback) { if ( !isset($this->wrapped_handlers[$callback]) ) { $this->wrapped_handlers[$callback] = new wsRawHtmlWrappedFilter($callback); } return $this->wrapped_handlers[$callback]; } } // Performance optimization: Start watching for content filters only after everything has // been loaded and parsed. Running on every hook before that would be a waste. function rawhtml_add_filter_initializer() { if ( class_exists('WP_Hook', false) ) { global $wsh_raw_interceptor; if ( !isset($wsh_raw_interceptor) ) { $wsh_raw_interceptor = new wsRawHtmlFilterInterceptor(); } } else { add_action('all', 'rawhtml_add_conditional_filters'); } } add_action('parse_query', 'rawhtml_add_filter_initializer', 1000, 0); // Add a custom meta box for per-post settings add_action('admin_menu', 'rawhtml_add_custom_box'); add_action('save_post', 'rawhtml_save_postdata'); /* Adds a custom section to the "advanced" Post and Page edit screens */ function rawhtml_add_custom_box() { //WP 3.0+ if( function_exists('add_meta_box') && function_exists('post_type_supports') ) { $types = get_post_types(array('show_ui' => true, 'show_in_menu' => true), 'names'); foreach( $types as $type ) { if ( !post_type_supports($type, 'editor') ) { continue; } add_meta_box( 'rawhtml_meta_box', 'Raw HTML', 'rawhtml_meta_box', $type, 'side' ); } } } /* Displays the custom box */ function rawhtml_meta_box(){ global $post; // Use nonce for verification echo ''; //Output checkboxes $fields = array( 'disable_wptexturize' => array('Disable wptexturize', 'wptexturize is responsible for smart quotes and other typographic characters'), 'disable_wpautop' => array('Disable automatic paragraphs', null), 'disable_convert_chars' => array('Disable convert_chars', 'convert_chars converts ampersand to HTML entities and "fixes" some Unicode character'), 'disable_convert_smilies' => array('Disable smilies', null), ); $settings = rawhtml_get_post_settings($post->ID); echo '