Options Framework: Sanitization Filters

I’ve been steadily improving the Options Framework plugin these last several weeks and there’s several nice changes in this 0.6 release. The biggest new feature is sanitization filters and better validation, but there’s also a slew of other less noticeable code improvements.

Unfortunately, a couple things that worked in previous versions will break with this release unless theme code is updated. I debated these changes quite a bit but decided it was best to do this while the plugin is still relatively young and would affect the least amount of people.

Validation Filters

Validation filters are the most exciting update in the Options Framework. Not only is there better sanitization (read security), it’s also now possible to change the default validation on any input without hacking the plugin directly.

Several users had mentioned that they wanted to enter some html in the textareas. I updated this so $allowedtags can be used. But if you wanted a different sanitization method, the default can now be removed and a new one added in via your themes functions. (Edit: $allowedposttags might be used soon instead)

Since, “embed” is not one of the $allowedtags- here’s how you could remove the default santization from a text area and allow “embed” to be used:

/* 
 * This is an example of how to override a default filter
 * for 'textarea' sanitization and use a different one.
 */

add_action('admin_init','optionscheck_change_santiziation', 100);

function optionscheck_change_santiziation() {
	remove_filter( 'of_sanitize_textarea', 'of_sanitize_textarea' );
	add_filter( 'of_sanitize_textarea', 'of_sanitize_textarea_custom' );
}

function of_sanitize_textarea_custom($input) {
	global $allowedtags;
        $of_custom_allowedtags["embed"] = array(
             "src" => array(),
             "type" => array(),
             "allowfullscreen" => array(),
             "allowscriptaccess" => array(),
             "height" => array(),
	     "width" => array()
	);

        $of_custom_allowedtags = array_merge($of_custom_allowedtags, $allowedtags);
        $output = wp_kses( $input, $of_custom_allowedtags);
	return $output;
}

Allow Script and Embed Tags

A lot of folks have been asking how to include all the $allowedpsttags in addition to script and embed tags. Here’s how you would do it (hat tip Jake Caputo):

/* 
 * This is an example of how to override a default filter
 * for 'textarea' sanitization and $allowedposttags + embed and script.
 */
add_action('admin_init','optionscheck_change_santiziation', 100);
 
function optionscheck_change_santiziation() {
    remove_filter( 'of_sanitize_textarea', 'of_sanitize_textarea' );
    add_filter( 'of_sanitize_textarea', 'custom_sanitize_textarea' );
}
 
function custom_sanitize_textarea($input) {
    global $allowedposttags;
    $custom_allowedtags["embed"] = array(
      "src" => array(),
      "type" => array(),
      "allowfullscreen" => array(),
      "allowscriptaccess" => array(),
      "height" => array(),
          "width" => array()
      );
      $custom_allowedtags["script"] = array();
 
      $custom_allowedtags = array_merge($custom_allowedtags, $allowedposttags);
      $output = wp_kses( $input, $custom_allowedtags);
    return $output;
}

Checkbox Code Has Changed

The most noticeable change in version 0.6 is how checkbox data is stored. The reason it’s the most noticeable is because it’s the one most likely to break a current implementation. Instead of storing checkbox data as “true” or “false”, I’ve changed it to “1” or “0”, which is how WordPress itself stores checkbox values and much smarter way to do it. I’ve written about this more in another post called “Checkboxes and Booleans”.

If you currently are using checkboxes in your theme, you should update options.php to use a default value of “0” or “1”. You should also update how the theme reads checkbox data to something like:

if ( of_get_option('checkbox_option', "0") ) {
	do_action();
}

Helper Function Returns Boolean False by Default

Instead of having the helper function of_get_option return the string “false” by default, I’ve updated it to return the boolean false (thanks so a suggestion from @sawyerh). Again, there’s more about this in my post on booleans. This should just make it easier to check if options are saved in the database. This change isn’t likely to break something if it isn’t updated, but the new code for your theme should look like this:

if ( !function_exists( 'of_get_option' ) ) {
function of_get_option($name, $default = false) {
	
	$optionsframework_settings = get_option('optionsframework');
	
	// Gets the unique option id
	$option_name = $optionsframework_settings['id'];
	
	if ( get_option($option_name) ) {
		$options = get_option($option_name);
	}
		
	if ( !empty($options[$name]) ) {
		return $options[$name];
	} else {
		return $default;
	}
}
}

Better Handling of Option Arrays

I updated the code for how arrays of options (like multicheckbox, background, and typography) are processed and stored. I don’t think should affect current users- but it’s something to keep an eye on. The new code allows the options to be stored directly into the array rather than having an additional step in the validation to process it.

Better Display of Messages

For anyone who dug into the code of 0.5, you probably saw my very awkward way of displaying the “Options Reset” and “Options Saved” messages. I had an option in the database for saving the message which was set when the form was submitted and then later cleared after the message was displayed.

By digging through WordPress core I found a more elegant way to do it. At the top of the options page I now use settings_errors(), which automatically displays any messages.

The messages are set in optionsframework_validate() as it processes the form. So, the reset message it now set like so:

// If the reset button was clicked
	if (!empty($_POST['reset'])) {
		// If options are deleted sucessfully update the error message
		if (delete_option($option_name) ) {
			add_settings_error('options-framework', 'restore_defaults', __('Default options restored.'), 'updated fade');
		}
	}

Thanks and Credits

I owe a big thanks to Michael Fields for this release. He advised me on how to implement the validation filters and did a thorough code review which caught many of the html escaping errors. If you need a really solid WordPress developer to check your code, I’d highly recommend him.

I was only able to have a code review done thanks to several generous donations. Having cash to spend on a third party code check was extremely helpful, made the plugin much stronger, and was well spent! If you use the plugin and can contribute, a PayPal donation will make my day.

And finally, several people have been helping out with testing and development on GitHub. I really appreciate it! I’m probably leaving someone out, but big thanks to @mantone, @rejithomas, @sawyerh, @samargulies, @mfields, @adampickeringq, @djevrek, @utkarshkukreti, @celtic7, @helgatheviking, @johnraz, @jmilone, @thumbnet, and @pastorjack.

Update: Version 0.6 has been released

About Devin

I am a developer based in Austin, Texas. I run a little theme shop called DevPress and help manage a WooCommerce shop with Universal Yums. Find me on twitter @devinsays.

72 Responses

  1. kathy

    great job devin! saw this got updated in my WP panels. and an update to portfolio press. seriously, how do you get so much done? geez.

    also i think you gave me a shoutout, but you forgot the THE in my handle. @helgatheviking (although that isn’t actually my twitter handle and i just checked.. there is some OTHER person masquerading as helgatheviking. grumble.

      1. kathy

        just looking at all the filters now. very cool. but it seems that you’re still stuck using 1 type of sanitation for all inputs of the same type. for instance, what about leaving textareas as they are most of the time, but toggling off the validation only on specific textareas?

      2. kathy

        thought i was going to have to report an epic fail on my part, but it just clicked.

        i think you have a typo above in your remove_filter. you don’t have a function called sanitize_text_field… both your function and filter are called of_sanitize_textarea. so your example wasn’t actually removing the filter for me.

        you said you left $option in there, but it took me a while to figure out how to access it. in case anyone else needs to know, the key was that i needed to tell my add_filter to accept 2 arguments.


        add_action('admin_init','optionscheck_change_santiziation', 100);

        function optionscheck_change_santiziation() {
        remove_filter( 'of_sanitize_textarea', 'of_sanitize_textarea' );
        add_filter( 'of_sanitize_textarea', 'my_sanitize_textarea',10,2 );
        }

        function my_sanitize_textarea($input, $option) {
        global $allowedtags;

        if($option['validate'] == 'none'){
        return $input;
        } else {
        $output = wp_kses( $input, $allowedtags);
        return $output;
        }
        }

        i’m probably going to leave my options array w/ the validate parameter = none anywhere i don’t care to validate… i think it will be a bit more “set it and forget it” than checking against the $option[‘id’]

        thanks devin!

      3. kathy

        a little more progress. in lieu of not sanitizing at all, i found a good post on how to add items to your allowed tags. i figure once for accepting embeds and another time for allowing scripts, and i should be good.


        function my_sanitize_textarea($input, $option) {
        global $allowedtags;

        if($option['validate'] == 'embed'){
        $my_tags = array();
        $my_tags = $allowedtags; //duplicate $allowed tags

        $my_tags["iframe"] = array( //add new allowed tags
        "src" => array(),
        "height" => array(),
        "width" => array(),
        "frameborder" => array();
        "allowfullscreen" => array();
        );

        $my_tags["object"] = array(
        "height" => array(),
        "width" => array()
        );

        $my_tags["param"] = array(
        "name" => array(),
        "value" => array()
        );

        $my_tags["embed"] = array(
        "src" => array(),
        "type" => array(),
        "allowfullscreen" => array(),
        "allowscriptaccess" => array(),
        "height" => array(),
        "width" => array()
        );

        $output = wp_kses( $input, $my_tags); //kses filter against my new list of allowed tags
        return $output;
        } else {
        $output = wp_kses( $input, $allowedtags);
        return $output;
        }
        }

  2. brian

    is there a way i can make this on its own tab and change it so editors can have access to it? I usually set my clients up as editors and hide the appearance tab from them! :) thanks for such a great plugin.

    BH

  3. James

    I’ve been doing extensive testing with v.0.6. It’s great work. One minor suggestion. Add a second “Save Options” button in the header of the options panel. I think it’s the way they have it set up in Canvas and it really helps usability (IMO) because the the most used options tend to be at the top of the page. Depending on how your options panel is set up, having to scroll to the bottom of the page every time can be a bit of a pain. A minor point but something to consider for a future update. Thanks again for the outstanding work.

      1. James

        Hey Devin, just downloaded the development version. Will let you know if I come up with anything. Thanks.

  4. kathy

    agree with james. something else i was going to try to take a stab at was having the panel “remember” which tab was last active after the save. if you’d been using the jquery UI tabs i’d know how to do that, but i’m less sure w/ your custom js.

  5. Hi Devin,

    Great plugin, have you considered adding a wysiwyg editor to the options? I’ve used it sometimes before it’s useful to customize say footer text to allow users to bold, underline text etc without using html tags.

      1. Gregg

        Seems like the text editor has been added after all? It’s in the demo, but for some reason, paragraphs don’t work. Other tags are working fine (bold, links, etc). Any ideas?

  6. Paul

    What you did with this is a big contribution for me. I really appreciate you sharing it and as soon as I get a project that uses the framework I will donate.

    thanks so much,
    Paul

  7. Great framework, mate.

    I really can’t get HTML allowed in the text inputs, though.. I’ve tried absolutely everything with the code you’ve given above to remove the filter and add the new one.. for a start I’m not 100% sure where the code is supposed to go as it doesn’t say, but I’ve tried it now in every file in the framework folder as well as functions.php

    I’ve tried some code from the comments, as well. I just cannot get the fields to allow and save HTML code.

    :/

    1. Okay so now I see this code does work for text fields, I can now save HTML to them.. but the paragraph above the code block says it’s for textareas which seems misleading? I’m assuming I can use the same code but tweak it to remove the filters from textareas, as well.

      Again, thanks for a great options framework.

      1. I should probably clarify that I’m looking to add tags to textareas – just saying HTML above was pretty vague as anchors tags and the like do work.

  8. AJ

    First, for the record, you’ve done a lot of great work with this framework and overall I give it a thumbs up! However…..with regards to the textarea, this still continues to be a major frustration because all of the tips and suggestions to allow pasting of code into the textarea is not working for me and I’ve tried many things as well. So for the theme I wanted to do, the google analytics code option is being stripped out, plus if someone wants to paste a php tag into the textarea (for example when using a slideshow plugin and wanting to add this to a part of a page), it gets stripped out totally. I’m not sure what the solution is for my situation, but in the mean time, I will have to scrap any options that lets a person paste code. I should note though, basic html seems to work ok, but javascript and php tags are stripped.

      1. AJ

        Hi Devin…yes, but to confirm, where am I pasting that code…just so I know I’m doing it in the right place? I tried a couple areas, in my functions.php and also the options-sanitize.php under the /* Textarea */ comment and I got errors, like this:
        Warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'custom_sanitize_textarea' was given in /mysite/html/proto/wp-includes/plugin.php on line 170

        Warning: Cannot modify header information - headers already sent by (output started at /mysite/html/proto/wp-includes/plugin.php:170) in /mysite/html/proto/wp-includes/pluggable.php on line 934

  9. James

    Hi Devin, first off, I love the framework and it’s proved extremely usfull, however it’s been extremely frustrating trying to get HTML in any boxes. I’ve placed the code in every functions file I could find and I’ve had no luck.. Can you explain where EXACTLY it’s got to go? I’d be extremely grateful.
    Many thanks.

  10. Hello Devin, it’s a really great update and tutorials! Thanks! :D

    However, error message appeared when i tried your ‘allow scripts tag’ snippet.

    I think you should change the $of_custom_allowedtags variable into $custom_allowedtags. A typo, i guess :)

    1. AJ

      Nice catch ….although, still not working for me as the field is stripped of it’s content when I click Save Options. HTML, PHP, etc., still not doable for me. I will have to forget about this one and have people manually paste their code in the theme files. Cheers!

  11. Devin,

    Check again your code in Allow Script and Embed Tags , you write function custom_sanitize_textarea_custom($input) it should be function custom_sanitize_textarea($input) :)

  12. Zach

    Hey Devin,
    Just had a real-world usecase for the sanitized text area (for adding in script/link tags to the header) which is awesome BTW!

    One thing I’m running into is that it looks like when I introduce the sanitized function, I get the ‘Cheatin’ uh?’ message for user roles that do not have access to the main WP options screen I’m assuming. The entire theme options works fine, except for when I add these options in (which unfortunately effects the entire them options panel). Any advice on how to fix that? Thanks!

  13. Great plugin Devin,
    Just Like the others, I really get headache in “textarea”. It won’t render all of global html tags. It’s just only can render a few html tags. In my issue, I can/t include the “” tag. It’s really gave me frustration.

  14. Ceyhun

    Hello, I tried to do this for link target, but it didn’t work.

    $of_custom_allowedtags[“a”] = array(
    “target” => array()
    );

  15. Wailynnoo


    <form action="http://www.aweber.com/scripts/addlead.pl" method="post">
    <input type="hidden" value="1" name="meta_message">
    <input type="hidden" value="email" name="meta_required">
    <input id="acpro_inp4" class="email" type="text" size="20" value="[email protected]" name="email">
    <input id="" class="submit" name="submit" value="Submint">
    </form>

    I want to add above code in my textarea but i can’t. Could you help me? Thank!

  16. Deryck Oñate Espinel

    Hi, I’m using a textarea (because editor does not work, does not show) and when I save the content the framework automatically strips all my HTML tags. I’m trying to include a but the sanitisers converts to plain text.

    I have checked $allowedposttags an it shows and as allowed tags.

    Any suggestion? Thanks in advance.

  17. Martin Schaer

    Thank you for the framework! Is there a way to allow element attributes using regular expressions? I need to allow data-[something]=”[value]”…

  18. Is it just me, or are the sanitization filters no longer working? After upgrading to WP 3.5, the scripts I had used in a textarea are now displayed without any of the attributes; empty script tags. They worked previously.

  19. hi Devin, if i want set color link of theme? i have added to function.php


    $link = of_get_option('a_color');
    if ($link) {
    echo ' a:link {color:'.$link['color'].'}';
    }

    and on file option.php


    $options[] = array( "name" => "Link color",
    "desc" => "Choose the link (a:link) color.",
    "id" => "a_color",
    "std" => "",
    "type" => "color");

    can you help me?

  20. John Brown

    Is this still information still relevant?

    The reason I am asking is that I had been playing around with the filters to allow HTML in a text area with the Options Framework Theme 1.6 and I couldn’t get it to work.

    Then I integrated the options framework theme with the theme I have been working on and inputted HTML worked without adding any sort of filters. So, I was a bit perplexed. Thanks. Working with the Roots Theme.

  21. Ashraf

    Hi Devin

    Thank you for this great plugin.How can I use options.php in plugin folder?I mean I want to put options.php in plugin folder not in theme folder.Possible?

  22. Christopher Nowlan

    Hi Devin,
    Im just trying to add input area into the theme options page where a user can place a url to outside website. Eg for facebook or twitter.

    Chris

      1. Christopher Nowlan

        Hi devin,
        It works partly, but it puts the local web address before the outside website

  23. Hi Devin. Thanks for a great tool.

    Do you have any idea how one might go about disabling all filtering of content inside tags in a textarea? CDATA comments in Javascript seem to cause the framework to strip out both the CDATA and the Javascript itself.

    When i enter this in the textarea :

    /**/

    It gets changed to :

    /**/

  24. Very Nice & easy to use plugin Devin, I have just one question that supposes I have created option for add social link, can I give an option to a user can easily add other social link fields thru dashboard they want and will show on the site?

Leave a Reply