3 * HTML/XHTML filter that only allows some elements and attributes
5 * Added wp_ prefix to avoid conflicts with existing kses users
8 * @copyright (C) 2002, 2003, 2005
9 * @author Ulf Harnhammar <metaur@users.sourceforge.net>
15 * *** CONTACT INFORMATION ***
16 * E-mail: metaur at users dot sourceforge dot net
17 * Web page: http://sourceforge.net/projects/kses
18 * Paper mail: Ulf Harnhammar
23 * [kses strips evil scripts!]
27 * You can override this in your my-hacks.php file You can also override this
28 * in a plugin file. The my-hacks.php is deprecated in its usage.
32 if (!defined('CUSTOM_TAGS'))
33 define('CUSTOM_TAGS', false);
37 * Kses global for default allowable HTML tags.
39 * Can be override by using CUSTOM_TAGS constant.
41 * @global array $allowedposttags
44 $allowedposttags = array(
62 'blockquote' => array(
67 'xml:lang' => array()),
71 'disabled' => array (),
88 'charoff' => array (),
95 'datetime' => array ()),
103 'xml:lang' => array()),
107 'fieldset' => array(),
113 'action' => array (),
114 'accept' => array (),
115 'accept-charset' => array (),
116 'enctype' => array (),
117 'method' => array (),
119 'target' => array ()),
124 'style' => array ()),
129 'style' => array ()),
134 'style' => array ()),
139 'style' => array ()),
144 'style' => array ()),
149 'style' => array ()),
153 'noshade' => array (),
155 'width' => array ()),
160 'border' => array (),
162 'height' => array (),
163 'hspace' => array (),
164 'longdesc' => array (),
165 'vspace' => array (),
168 'width' => array ()),
170 'datetime' => array (),
176 'align' => array ()),
179 'class' => array ()),
186 'xml:lang' => array()),
189 'width' => array ()),
200 'xml:lang' => array()),
207 'bgcolor' => array (),
208 'border' => array (),
209 'cellpadding' => array (),
210 'cellspacing' => array (),
216 'summary' => array (),
217 'width' => array ()),
221 'charoff' => array (),
222 'valign' => array ()),
227 'bgcolor' => array (),
229 'charoff' => array (),
231 'colspan' => array (),
233 'headers' => array (),
234 'height' => array (),
235 'nowrap' => array (),
236 'rowspan' => array (),
239 'valign' => array (),
240 'width' => array ()),
244 'disabled' => array (),
246 'readonly' => array ()),
251 'charoff' => array (),
252 'valign' => array ()),
257 'bgcolor' => array (),
259 'charoff' => array (),
261 'colspan' => array (),
262 'headers' => array (),
263 'height' => array (),
264 'nowrap' => array (),
265 'rowspan' => array (),
267 'valign' => array (),
268 'width' => array ()),
272 'charoff' => array (),
274 'valign' => array ()),
278 'bgcolor' => array (),
280 'charoff' => array (),
283 'valign' => array ()),
298 * Kses allowed HTML elements.
300 * @global array $allowedtags
303 $allowedtags = array(
306 'title' => array ()),
308 'title' => array ()),
310 'title' => array ()),
312 'blockquote' => array(
318 'datetime' => array ()),
322 'em' => array (), 'i' => array (),
323 // 'ins' => array('datetime' => array(), 'cite' => array()),
339 * Filters content and keeps only allowable HTML elements.
341 * This function makes sure that only the allowed HTML element names, attribute
342 * names and attribute values plus only sane HTML entities will occur in
343 * $string. You have to remove any slashes from PHP's magic quotes before you
344 * call this function.
346 * The default allowed protocols are 'http', 'https', 'ftp', 'mailto', 'news',
347 * 'irc', 'gopher', 'nntp', 'feed', and finally 'telnet. This covers all common
348 * link protocols, except for 'javascript' which should not be allowed for
353 * @param string $string Content to filter through kses
354 * @param array $allowed_html List of allowed HTML elements
355 * @param array $allowed_protocols Optional. Allowed protocol in links.
356 * @return string Filtered content with only allowed HTML elements
358 function wp_kses($string, $allowed_html, $allowed_protocols = array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet')) {
359 $string = wp_kses_no_null($string);
360 $string = wp_kses_js_entities($string);
361 $string = wp_kses_normalize_entities($string);
362 $allowed_html_fixed = wp_kses_array_lc($allowed_html);
363 $string = wp_kses_hook($string, $allowed_html_fixed, $allowed_protocols); // WP changed the order of these funcs and added args to wp_kses_hook
364 return wp_kses_split($string, $allowed_html_fixed, $allowed_protocols);
368 * You add any kses hooks here.
370 * There is currently only one kses WordPress hook and it is called here. All
371 * parameters are passed to the hooks and expected to recieve a string.
375 * @param string $string Content to filter through kses
376 * @param array $allowed_html List of allowed HTML elements
377 * @param array $allowed_protocols Allowed protocol in links
378 * @return string Filtered content through 'pre_kses' hook
380 function wp_kses_hook($string, $allowed_html, $allowed_protocols) {
381 $string = apply_filters('pre_kses', $string, $allowed_html, $allowed_protocols);
386 * This function returns kses' version number.
390 * @return string KSES Version Number
392 function wp_kses_version() {
397 * Searches for HTML tags, no matter how malformed.
399 * It also matches stray ">" characters.
403 * @param string $string Content to filter
404 * @param array $allowed_html Allowed HTML elements
405 * @param array $allowed_protocols Allowed protocols to keep
406 * @return string Content with fixed HTML tags
408 function wp_kses_split($string, $allowed_html, $allowed_protocols) {
409 global $pass_allowed_html, $pass_allowed_protocols;
410 $pass_allowed_html = $allowed_html;
411 $pass_allowed_protocols = $allowed_protocols;
412 return preg_replace_callback('%((<!--.*?(-->|$))|(<[^>]*(>|$)|>))%',
413 create_function('$match', 'global $pass_allowed_html, $pass_allowed_protocols; return wp_kses_split2($match[1], $pass_allowed_html, $pass_allowed_protocols);'), $string);
417 * Callback for wp_kses_split for fixing malformed HTML tags.
419 * This function does a lot of work. It rejects some very malformed things like
420 * <:::>. It returns an empty string, if the element isn't allowed (look ma, no
421 * strip_tags()!). Otherwise it splits the tag into an element and an attribute
424 * After the tag is split into an element and an attribute list, it is run
425 * through another filter which will remove illegal attributes and once that is
426 * completed, will be returned.
430 * @uses wp_kses_attr()
432 * @param string $string Content to filter
433 * @param array $allowed_html Allowed HTML elements
434 * @param array $allowed_protocols Allowed protocols to keep
435 * @return string Fixed HTML element
437 function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
438 $string = wp_kses_stripslashes($string);
440 if (substr($string, 0, 1) != '<')
442 # It matched a ">" character
444 if (preg_match('%^<!--(.*?)(-->)?$%', $string, $matches)) {
445 $string = str_replace(array('<!--', '-->'), '', $matches[1]);
446 while ( $string != $newstring = wp_kses($string, $allowed_html, $allowed_protocols) )
447 $string = $newstring;
450 // prevent multiple dashes in comments
451 $string = preg_replace('/--+/', '-', $string);
452 // prevent three dashes closing a comment
453 $string = preg_replace('/-$/', '', $string);
454 return "<!--{$string}-->";
456 # Allow HTML comments
458 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
460 # It's seriously malformed
462 $slash = trim($matches[1]);
464 $attrlist = $matches[3];
466 if (!@isset($allowed_html[strtolower($elem)]))
468 # They are using a not allowed HTML element
471 return "<$slash$elem>";
472 # No attributes are allowed for closing elements
474 return wp_kses_attr("$slash$elem", $attrlist, $allowed_html, $allowed_protocols);
478 * Removes all attributes, if none are allowed for this element.
480 * If some are allowed it calls wp_kses_hair() to split them further, and then
481 * it builds up new HTML code from the data that kses_hair() returns. It also
482 * removes "<" and ">" characters, if there are any left. One more thing it does
483 * is to check if the tag has a closing XHTML slash, and if it does, it puts one
484 * in the returned code as well.
488 * @param string $element HTML element/tag
489 * @param string $attr HTML attributes from HTML element to closing HTML element tag
490 * @param array $allowed_html Allowed HTML elements
491 * @param array $allowed_protocols Allowed protocols to keep
492 * @return string Sanitized HTML element
494 function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
495 # Is there a closing XHTML slash at the end of the attributes?
498 if (preg_match('%\s/\s*$%', $attr))
501 # Are any attributes allowed at all for this element?
503 if (@ count($allowed_html[strtolower($element)]) == 0)
504 return "<$element$xhtml_slash>";
508 $attrarr = wp_kses_hair($attr, $allowed_protocols);
510 # Go through $attrarr, and save the allowed attributes for this element
515 foreach ($attrarr as $arreach) {
516 if (!@ isset ($allowed_html[strtolower($element)][strtolower($arreach['name'])]))
517 continue; # the attribute is not allowed
519 $current = $allowed_html[strtolower($element)][strtolower($arreach['name'])];
521 continue; # the attribute is not allowed
523 if (!is_array($current))
524 $attr2 .= ' '.$arreach['whole'];
525 # there are no checks
528 # there are some checks
530 foreach ($current as $currkey => $currval)
531 if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval)) {
536 if ( $arreach['name'] == 'style' ) {
537 $orig_value = $arreach['value'];
539 $value = safecss_filter_attr($orig_value);
544 $arreach['value'] = $value;
546 $arreach['whole'] = str_replace($orig_value, $value, $arreach['whole']);
550 $attr2 .= ' '.$arreach['whole']; # it passed them
551 } # if !is_array($current)
554 # Remove any "<" or ">" characters
556 $attr2 = preg_replace('/[<>]/', '', $attr2);
558 return "<$element$attr2$xhtml_slash>";
562 * Builds an attribute list from string containing attributes.
564 * This function does a lot of work. It parses an attribute list into an array
565 * with attribute data, and tries to do the right thing even if it gets weird
566 * input. It will add quotes around attribute values that don't have any quotes
567 * or apostrophes around them, to make it easier to produce HTML code that will
568 * conform to W3C's HTML specification. It will also remove bad URL protocols
569 * from attribute values. It also reduces duplicate attributes by using the
570 * attribute defined first (foo='bar' foo='baz' will result in foo='bar').
574 * @param string $attr Attribute list from HTML element to closing HTML element tag
575 * @param array $allowed_protocols Allowed protocols to keep
576 * @return array List of attributes after parsing
578 function wp_kses_hair($attr, $allowed_protocols) {
582 $uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
584 # Loop through the whole attribute list
586 while (strlen($attr) != 0) {
587 $working = 0; # Was the last operation successful?
590 case 0 : # attribute name, href for instance
592 if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
593 $attrname = $match[1];
594 $working = $mode = 1;
595 $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
600 case 1 : # equals sign or valueless ("selected")
602 if (preg_match('/^\s*=\s*/', $attr)) # equals sign
606 $attr = preg_replace('/^\s*=\s*/', '', $attr);
610 if (preg_match('/^\s+/', $attr)) # valueless
614 if(FALSE === array_key_exists($attrname, $attrarr)) {
615 $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
617 $attr = preg_replace('/^\s+/', '', $attr);
622 case 2 : # attribute value, a URL after href= for instance
624 if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match))
627 $thisval = $match[1];
628 if ( in_array($attrname, $uris) )
629 $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
631 if(FALSE === array_key_exists($attrname, $attrarr)) {
632 $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
636 $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
640 if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match))
643 $thisval = $match[1];
644 if ( in_array($attrname, $uris) )
645 $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
647 if(FALSE === array_key_exists($attrname, $attrarr)) {
648 $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
652 $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
656 if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match))
659 $thisval = $match[1];
660 if ( in_array($attrname, $uris) )
661 $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
663 if(FALSE === array_key_exists($attrname, $attrarr)) {
664 $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
666 # We add quotes to conform to W3C's HTML spec.
669 $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
675 if ($working == 0) # not well formed, remove and try again
677 $attr = wp_kses_html_error($attr);
682 if ($mode == 1 && FALSE === array_key_exists($attrname, $attrarr))
683 # special case, for when the attribute list ends with a valueless
684 # attribute like "selected"
685 $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
691 * Performs different checks for attribute values.
693 * The currently implemented checks are "maxlen", "minlen", "maxval", "minval"
694 * and "valueless" with even more checks to come soon.
698 * @param string $value Attribute value
699 * @param string $vless Whether the value is valueless or not. Use 'y' or 'n'
700 * @param string $checkname What $checkvalue is checking for.
701 * @param mixed $checkvalue What constraint the value should pass
702 * @return bool Whether check passes (true) or not (false)
704 function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
707 switch (strtolower($checkname)) {
709 # The maxlen check makes sure that the attribute value has a length not
710 # greater than the given value. This can be used to avoid Buffer Overflows
711 # in WWW clients and various Internet servers.
713 if (strlen($value) > $checkvalue)
718 # The minlen check makes sure that the attribute value has a length not
719 # smaller than the given value.
721 if (strlen($value) < $checkvalue)
726 # The maxval check does two things: it checks that the attribute value is
727 # an integer from 0 and up, without an excessive amount of zeroes or
728 # whitespace (to avoid Buffer Overflows). It also checks that the attribute
729 # value is not greater than the given value.
730 # This check can be used to avoid Denial of Service attacks.
732 if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
734 if ($value > $checkvalue)
739 # The minval check checks that the attribute value is a positive integer,
740 # and that it is not smaller than the given value.
742 if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
744 if ($value < $checkvalue)
749 # The valueless check checks if the attribute has a value
750 # (like <a href="blah">) or not (<option selected>). If the given value
751 # is a "y" or a "Y", the attribute must not have a value.
752 # If the given value is an "n" or an "N", the attribute must have one.
754 if (strtolower($checkvalue) != $vless)
763 * Sanitize string from bad protocols.
765 * This function removes all non-allowed protocols from the beginning of
766 * $string. It ignores whitespace and the case of the letters, and it does
767 * understand HTML entities. It does its work in a while loop, so it won't be
768 * fooled by a string like "javascript:javascript:alert(57)".
772 * @param string $string Content to filter bad protocols from
773 * @param array $allowed_protocols Allowed protocols to keep
774 * @return string Filtered content
776 function wp_kses_bad_protocol($string, $allowed_protocols) {
777 $string = wp_kses_no_null($string);
778 $string = preg_replace('/\xad+/', '', $string); # deals with Opera "feature"
779 $string2 = $string.'a';
781 while ($string != $string2) {
783 $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
790 * Removes any NULL characters in $string.
794 * @param string $string
797 function wp_kses_no_null($string) {
798 $string = preg_replace('/\0+/', '', $string);
799 $string = preg_replace('/(\\\\0)+/', '', $string);
805 * Strips slashes from in front of quotes.
807 * This function changes the character sequence \" to just ". It leaves all
808 * other slashes alone. It's really weird, but the quoting from
809 * preg_replace(//e) seems to require this.
813 * @param string $string String to strip slashes
814 * @return string Fixed strings with quoted slashes
816 function wp_kses_stripslashes($string) {
817 return preg_replace('%\\\\"%', '"', $string);
821 * Goes through an array and changes the keys to all lower case.
825 * @param array $inarray Unfiltered array
826 * @return array Fixed array with all lowercase keys
828 function wp_kses_array_lc($inarray) {
829 $outarray = array ();
831 foreach ( (array) $inarray as $inkey => $inval) {
832 $outkey = strtolower($inkey);
833 $outarray[$outkey] = array ();
835 foreach ( (array) $inval as $inkey2 => $inval2) {
836 $outkey2 = strtolower($inkey2);
837 $outarray[$outkey][$outkey2] = $inval2;
845 * Removes the HTML JavaScript entities found in early versions of Netscape 4.
849 * @param string $string
852 function wp_kses_js_entities($string) {
853 return preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
857 * Handles parsing errors in wp_kses_hair().
859 * The general plan is to remove everything to and including some whitespace,
860 * but it deals with quotes and apostrophes as well.
864 * @param string $string
867 function wp_kses_html_error($string) {
868 return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
872 * Sanitizes content from bad protocols and other characters.
874 * This function searches for URL protocols at the beginning of $string, while
875 * handling whitespace and HTML entities.
879 * @param string $string Content to check for bad protocols
880 * @param string $allowed_protocols Allowed protocols
881 * @return string Sanitized content
883 function wp_kses_bad_protocol_once($string, $allowed_protocols) {
884 global $_kses_allowed_protocols;
885 $_kses_allowed_protocols = $allowed_protocols;
887 $string2 = preg_split('/:|:|:/i', $string, 2);
888 if ( isset($string2[1]) && !preg_match('%/\?%', $string2[0]) )
889 $string = wp_kses_bad_protocol_once2($string2[0]) . trim($string2[1]);
891 $string = preg_replace_callback('/^((&[^;]*;|[\sA-Za-z0-9])*)'.'(:|:|&#[Xx]3[Aa];)\s*/', 'wp_kses_bad_protocol_once2', $string);
897 * Callback for wp_kses_bad_protocol_once() regular expression.
899 * This function processes URL protocols, checks to see if they're in the
900 * white-list or not, and returns different data depending on the answer.
905 * @param mixed $matches string or preg_replace_callback() matches array to check for bad protocols
906 * @return string Sanitized content
908 function wp_kses_bad_protocol_once2($matches) {
909 global $_kses_allowed_protocols;
911 if ( is_array($matches) ) {
912 if ( ! isset($matches[1]) || empty($matches[1]) )
915 $string = $matches[1];
920 $string2 = wp_kses_decode_entities($string);
921 $string2 = preg_replace('/\s/', '', $string2);
922 $string2 = wp_kses_no_null($string2);
923 $string2 = preg_replace('/\xad+/', '', $string2);
924 # deals with Opera "feature"
925 $string2 = strtolower($string2);
928 foreach ( (array) $_kses_allowed_protocols as $one_protocol)
929 if (strtolower($one_protocol) == $string2) {
941 * Converts and fixes HTML entities.
943 * This function normalizes HTML entities. It will convert "AT&T" to the correct
944 * "AT&T", ":" to ":", "&#XYZZY;" to "&#XYZZY;" and so on.
948 * @param string $string Content to normalize entities
949 * @return string Content with normalized entities
951 function wp_kses_normalize_entities($string) {
952 # Disarm all entities by converting & to &
954 $string = str_replace('&', '&', $string);
956 # Change back the allowed entities in our entity whitelist
958 $string = preg_replace('/&([A-Za-z][A-Za-z0-9]{0,19});/', '&\\1;', $string);
959 $string = preg_replace_callback('/&#0*([0-9]{1,5});/', 'wp_kses_normalize_entities2', $string);
960 $string = preg_replace_callback('/&#([Xx])0*(([0-9A-Fa-f]{2}){1,2});/', 'wp_kses_normalize_entities3', $string);
966 * Callback for wp_kses_normalize_entities() regular expression.
968 * This function helps wp_kses_normalize_entities() to only accept 16 bit values
969 * and nothing more for &#number; entities.
974 * @param array $matches preg_replace_callback() matches array
975 * @return string Correctly encoded entity
977 function wp_kses_normalize_entities2($matches) {
978 if ( ! isset($matches[1]) || empty($matches[1]) )
982 return ( ( ! valid_unicode($i) ) || ($i > 65535) ? "&#$i;" : "&#$i;" );
986 * Callback for wp_kses_normalize_entities() for regular expression.
988 * This function helps wp_kses_normalize_entities() to only accept valid Unicode
989 * numeric entities in hex form.
993 * @param array $matches preg_replace_callback() matches array
994 * @return string Correctly encoded entity
996 function wp_kses_normalize_entities3($matches) {
997 if ( ! isset($matches[2]) || empty($matches[2]) )
1000 $hexchars = $matches[2];
1001 return ( ( ! valid_unicode(hexdec($hexchars)) ) ? "&#x$hexchars;" : "&#x$hexchars;" );
1005 * Helper function to determine if a Unicode value is valid.
1007 * @param int $i Unicode value
1008 * @return bool true if the value was a valid Unicode number
1010 function valid_unicode($i) {
1011 return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
1012 ($i >= 0x20 && $i <= 0xd7ff) ||
1013 ($i >= 0xe000 && $i <= 0xfffd) ||
1014 ($i >= 0x10000 && $i <= 0x10ffff) );
1018 * Convert all entities to their character counterparts.
1020 * This function decodes numeric HTML entities (A and A). It doesn't do
1021 * anything with other entities like ä, but we don't need them in the URL
1022 * protocol whitelisting system anyway.
1026 * @param string $string Content to change entities
1027 * @return string Content after decoded entities
1029 function wp_kses_decode_entities($string) {
1030 $string = preg_replace_callback('/&#([0-9]+);/', create_function('$match', 'return chr($match[1]);'), $string);
1031 $string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', create_function('$match', 'return chr(hexdec($match[1]));'), $string);
1037 * Sanitize content with allowed HTML Kses rules.
1040 * @uses $allowedtags
1042 * @param string $data Content to filter
1043 * @return string Filtered content
1045 function wp_filter_kses($data) {
1046 global $allowedtags;
1047 return addslashes( wp_kses(stripslashes( $data ), $allowedtags) );
1051 * Sanitize content for allowed HTML tags for post content.
1053 * Post content refers to the page contents of the 'post' type and not $_POST
1057 * @uses $allowedposttags
1059 * @param string $data Post content to filter
1060 * @return string Filtered post content with allowed HTML tags and attributes intact.
1062 function wp_filter_post_kses($data) {
1063 global $allowedposttags;
1064 return addslashes ( wp_kses(stripslashes( $data ), $allowedposttags) );
1068 * Strips all of the HTML in the content.
1072 * @param string $data Content to strip all HTML from
1073 * @return string Filtered content without any HTML
1075 function wp_filter_nohtml_kses($data) {
1076 return addslashes ( wp_kses(stripslashes( $data ), array()) );
1080 * Adds all Kses input form content filters.
1082 * All hooks have default priority. The wp_filter_kses() function is added to
1083 * the 'pre_comment_content' and 'title_save_pre' hooks.
1085 * The wp_filter_post_kses() function is added to the 'content_save_pre',
1086 * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
1089 * @uses add_filter() See description for what functions are added to what hooks.
1091 function kses_init_filters() {
1092 // Normal filtering.
1093 add_filter('pre_comment_content', 'wp_filter_kses');
1094 add_filter('title_save_pre', 'wp_filter_kses');
1097 add_filter('content_save_pre', 'wp_filter_post_kses');
1098 add_filter('excerpt_save_pre', 'wp_filter_post_kses');
1099 add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1103 * Removes all Kses input form content filters.
1105 * A quick procedural method to removing all of the filters that kses uses for
1106 * content in WordPress Loop.
1108 * Does not remove the kses_init() function from 'init' hook (priority is
1109 * default). Also does not remove kses_init() function from 'set_current_user'
1110 * hook (priority is also default).
1114 function kses_remove_filters() {
1115 // Normal filtering.
1116 remove_filter('pre_comment_content', 'wp_filter_kses');
1117 remove_filter('title_save_pre', 'wp_filter_kses');
1120 remove_filter('content_save_pre', 'wp_filter_post_kses');
1121 remove_filter('excerpt_save_pre', 'wp_filter_post_kses');
1122 remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1126 * Sets up most of the Kses filters for input form content.
1128 * If you remove the kses_init() function from 'init' hook and
1129 * 'set_current_user' (priority is default), then none of the Kses filter hooks
1132 * First removes all of the Kses filters in case the current user does not need
1133 * to have Kses filter the content. If the user does not have unfiltered html
1134 * capability, then Kses filters are added.
1136 * @uses kses_remove_filters() Removes the Kses filters
1137 * @uses kses_init_filters() Adds the Kses filters back if the user
1138 * does not have unfiltered HTML capability.
1141 function kses_init() {
1142 kses_remove_filters();
1144 if (current_user_can('unfiltered_html') == false)
1145 kses_init_filters();
1148 add_action('init', 'kses_init');
1149 add_action('set_current_user', 'kses_init');
1151 function safecss_filter_attr( $css, $deprecated = '' ) {
1152 $css = wp_kses_no_null($css);
1153 $css = str_replace(array("\n","\r","\t"), '', $css);
1155 if ( preg_match( '%[\\(&]|/\*%', $css ) ) // remove any inline css containing \ ( & or comments
1158 $css_array = split( ';', trim( $css ) );
1159 $allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
1160 'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
1161 'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
1162 'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
1163 'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
1164 'border-top-color', 'border-top-style', 'border-top-width', 'border-width', 'caption-side',
1165 'clear', 'cursor', 'direction', 'font', 'font-family', 'font-size', 'font-style',
1166 'font-variant', 'font-weight', 'height', 'letter-spacing', 'line-height', 'margin-bottom',
1167 'margin-left', 'margin-right', 'margin-top', 'overflow', 'padding', 'padding-bottom',
1168 'padding-left', 'padding-right', 'padding-top', 'text-decoration', 'text-indent', 'vertical-align',
1171 if ( empty($allowed_attr) )
1175 foreach ( $css_array as $css_item ) {
1176 if ( $css_item == '' )
1178 $css_item = trim( $css_item );
1180 if ( strpos( $css_item, ':' ) === false ) {
1183 $parts = split( ':', $css_item );
1184 if ( in_array( trim( $parts[0] ), $allowed_attr ) )