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 '
';
}
/* Saves post metadata */
function rawhtml_save_postdata($post_id) {
// Verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
if ( !isset($_POST['rawhtml_nonce']) ) {
return $post_id;
}
if ( !wp_verify_nonce($_POST['rawhtml_nonce'], plugin_basename(RAWHTML_PLUGIN_FILE)) ) {
return $post_id;
}
if ( !current_user_can('edit_post', $post_id) ) {
return $post_id;
}
// OK, we're authenticated: we need to find and save the data
$fields = rawhtml_get_settings_fields();
$new_settings = array();
foreach ($fields as $field) {
if ( !empty($_POST['rawhtml_' . $field]) ) {
$new_settings[$field] = true;
} else {
$new_settings[$field] = false;
}
}
rawhtml_save_post_settings($post_id, $new_settings);
return true;
}
//Add our panel to the "Screen Options" box
add_screen_options_panel(
'rawhtml-default-settings', //Panel ID
'Raw HTML defaults', //Panel title.
'rawhtml_default_settings_panel', //The function that generates panel contents.
array('post', 'page'), //Pages/screens where the panel is displayed.
'rawhtml_save_new_defaults', //The function that gets triggered when settings are submitted/saved.
true //Auto-submit settings (via AJAX) when they change.
);
/**
* Retrieve the "disable_*" flags associated with a post.
* If no flags have been set, this function will return the default settings.
*
* Note: Will transparently upgrade the old one-meta-key-per-flag storage system
* to the new one-key-per-post one.
*
* @param int $post_id
* @return array Flag values as an associative array.
*/
function rawhtml_get_post_settings($post_id) {
$defaults = rawhtml_get_default_settings();
$fields = rawhtml_get_settings_fields();
//Per-post settings should be stored as a comma-separated list of 1/0 flags.
$settings = get_post_meta($post_id, '_rawhtml_settings', true);
if ( $settings != '' ) {
$settings = explode(',', $settings, count($fields));
$settings = array_combine($fields, $settings);
foreach($settings as $field => $value) {
$settings[$field] = (bool)intval($value);
}
} else {
//Older versions of the plugin stored each flag in a separate meta key.
$settings = array();
$should_upgrade_settings = false;
foreach($fields as $field) {
$value = get_post_meta($post_id, '_'.$field, true);
if ( $value == '' ){
$value = get_post_meta($post_id, $field, true);
}
if ( $value == '' ){
$value = $defaults[$field];
} else {
$value = (bool)intval($value);
$should_upgrade_settings = true;
}
$settings[$field] = $value;
}
if ( $should_upgrade_settings ) {
rawhtml_save_post_settings($post_id, $settings);
rawhtml_delete_old_post_settings($post_id);
}
}
return $settings;
}
/**
* Save the "disable_*" flag values set for a post.
*
* @param int $post_id Post ID.
* @param array $settings Flag values as an associative array.
* @return void
*/
function rawhtml_save_post_settings($post_id, $settings) {
$fields = rawhtml_get_settings_fields();
$ordered_settings = array();
foreach($fields as $field) {
$ordered_settings[$field] = $settings[$field] ? '1' : '0';
}
update_post_meta($post_id, '_rawhtml_settings', implode(',', $ordered_settings));
}
/**
* Delete the old one-key-per-flag metadata that older versions of the plugin
* used to store per-post settings.
*
* @param int $post_id
*/
function rawhtml_delete_old_post_settings($post_id) {
$fields = rawhtml_get_settings_fields();
foreach($fields as $field) {
delete_post_meta($post_id, $field);
}
foreach($fields as $field) {
delete_post_meta($post_id, '_' . $field);
}
}
function rawhtml_get_settings_fields() {
return array('disable_wpautop', 'disable_wptexturize', 'disable_convert_chars', 'disable_convert_smilies');
}
/**
* Retrieve the default settings for our post/page meta box.
* Settings are saved in user meta.
*
* @return array
*/
function rawhtml_get_default_settings(){
//By default, all tweaks are disabled
$defaults = array(
'disable_wptexturize' => false,
'disable_wpautop' => false,
'disable_convert_chars' => false,
'disable_convert_smilies' => false,
);
if ( !function_exists('wp_get_current_user') || !function_exists('get_user_meta') ){
return $defaults;
}
//Get current defaults, if any
$user = wp_get_current_user();
$user_defaults = get_user_meta($user->ID, 'rawhtml_defaults', true);
if ( is_array($user_defaults) ){
$defaults = array_merge($defaults, $user_defaults);
}
return $defaults;
}
/**
* Update default settings for our post/page meta box.
*
* @param array $new_defaults
* @return bool True on success, false on failure.
*/
function rawhtml_set_default_settings($new_defaults){
if ( !function_exists('wp_get_current_user') || !function_exists('update_user_meta') ){
return false;
}
//Get current defaults, if any
$user = wp_get_current_user();
if ( isset($user) && $user && isset($user->ID) ){
return update_user_meta($user->ID, 'rawhtml_defaults', $new_defaults);
} else {
return false;
}
}
/**
* Generate the "Raw HTML defaults" panel for Screen Options.
*
* @return string
*/
function rawhtml_default_settings_panel(){
$defaults = rawhtml_get_default_settings();
//Output checkboxes
$fields = array(
'disable_wptexturize' => 'Disable wptexturize',
'disable_wpautop' => 'Disable automatic paragraphs',
'disable_convert_chars' => 'Disable convert_chars',
'disable_convert_smilies' => 'Disable smilies',
);
$output = '
";
return $output;
}
/**
* Process the "Raw HTML defaults" form fields and save new settings
*
* @param array $params
* @return void
*/
function rawhtml_save_new_defaults($params){
//Get current defaults
$defaults = rawhtml_get_default_settings();
//Read new values from the submitted form
foreach($defaults as $field => $old_value){
if ( isset($params['rawhtml_default-'.$field]) && ($params['rawhtml_default-'.$field] == 'on') ){
$defaults[$field] = true;
} else {
$defaults[$field] = false;
}
}
//Store the new defaults
rawhtml_set_default_settings($defaults);
}