Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
<element value="WP_Tests_Image_Resize_UnitTestCase"/>
<element value="WP_Import_UnitTestCase"/>
<element value="Tests_Query_Conditionals"/>
<element value="WP_Test_XML_TestCase"/>

<!-- Mock classes. -->
<element value="Spy_REST_Server"/>
Expand Down
10 changes: 10 additions & 0 deletions src/wp-includes/canonical.php
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,11 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) {
$redirect['path'] = trailingslashit( $redirect['path'] ) . $addl_path;
}

// Remove trailing slash for sitemaps requests.
if ( ! empty( get_query_var( 'sitemap' ) ) ) {
$redirect['path'] = untrailingslashit( $redirect['path'] );
}

$redirect_url = $redirect['scheme'] . '://' . $redirect['host'] . $redirect['path'];
}

Expand Down Expand Up @@ -651,6 +656,11 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) {
$redirect['path'] = trailingslashit( $redirect['path'] );
}

// Remove trailing slash for sitemaps requests.
if ( ! empty( get_query_var( 'sitemap' ) ) || ! empty( get_query_var( 'sitemap-stylesheet' ) ) ) {
$redirect['path'] = untrailingslashit( $redirect['path'] );
}

// Strip multiple slashes out of the URL.
if ( strpos( $redirect['path'], '//' ) > -1 ) {
$redirect['path'] = preg_replace( '|/+|', '/', $redirect['path'] );
Expand Down
3 changes: 3 additions & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@
add_action( 'rest_api_init', 'create_initial_rest_routes', 99 );
add_action( 'parse_request', 'rest_api_loaded' );

// Sitemaps actions.
add_action( 'init', 'wp_sitemaps_get_server' );

/**
* Filters formerly mixed into wp-includes.
*/
Expand Down
69 changes: 66 additions & 3 deletions src/wp-includes/formatting.php
Original file line number Diff line number Diff line change
Expand Up @@ -935,14 +935,18 @@ function seems_utf8( $str ) {
* &quot;, or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
*
* @since 1.2.2
* @since 5.5.0 `$quote_style` also accepts '`ENT_XML1`.
* @access private
*
* @staticvar string $_charset
*
* @param string $string The text which is to be encoded.
* @param int|string $quote_style Optional. Converts double quotes if set to ENT_COMPAT,
* both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES.
* Also compatible with old values; converting single quotes if set to 'single',
* Converts single and double quotes, as well as converting HTML
* named entities (that are not also XML named entities) to their
* code points if set to ENT_XML1. Also compatible with old values;
* converting single quotes if set to 'single',
* double if set to 'double' or both if otherwise set.
* Default is ENT_NOQUOTES.
* @param false|string $charset Optional. The character encoding of the string. Default is false.
Expand All @@ -964,7 +968,9 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals
// Account for the previous behaviour of the function when the $quote_style is not an accepted value.
if ( empty( $quote_style ) ) {
$quote_style = ENT_NOQUOTES;
} elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
} elseif ( ENT_XML1 === $quote_style ) {
$quote_style = ENT_QUOTES | ENT_XML1;
} elseif ( ! in_array( $quote_style, array( ENT_NOQUOTES, ENT_COMPAT, ENT_QUOTES, 'single', 'double' ), true ) ) {
$quote_style = ENT_QUOTES;
}

Expand Down Expand Up @@ -994,7 +1000,7 @@ function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = fals
if ( ! $double_encode ) {
// Guarantee every &entity; is valid, convert &garbage; into &amp;garbage;
// This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable.
$string = wp_kses_normalize_entities( $string );
$string = wp_kses_normalize_entities( $string, ( $quote_style & ENT_XML1 ) ? 'xml' : 'html' );
}

$string = htmlspecialchars( $string, $quote_style, $charset, $double_encode );
Expand Down Expand Up @@ -4536,6 +4542,63 @@ function esc_textarea( $text ) {
return apply_filters( 'esc_textarea', $safe_text, $text );
}

/**
* Escaping for XML blocks.
*
* @since 5.5.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love a more detailed description here that explains why someone would use this function rather than esc_attr() or esc_html().

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably update the docblocks of the other functions too while we're at it. Currently they're a bit bleak...

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will see what I can come up with that's a) more helpful that what's there and b) shorter than a dissertation :-)

*
* @param string $text Text to escape.
* @return string Escaped text.
*/
function esc_xml( $text ) {
$safe_text = wp_check_invalid_utf8( $text );

$cdata_regex = '\<\!\[CDATA\[.*?\]\]\>';
$regex = <<<EOF
/
(?=.*?{$cdata_regex}) # lookahead that will match anything followed by a CDATA Section
(?<non_cdata_followed_by_cdata>(.*?)) # the "anything" matched by the lookahead
(?<cdata>({$cdata_regex})) # the CDATA Section matched by the lookahead

| # alternative

(?<non_cdata>(.*)) # non-CDATA Section
/sx
EOF;

$safe_text = (string) preg_replace_callback(
$regex,
static function( $matches ) {
if ( ! $matches[0] ) {
return '';
}

if ( ! empty( $matches['non_cdata'] ) ) {
// escape HTML entities in the non-CDATA Section.
return _wp_specialchars( $matches['non_cdata'], ENT_XML1 );
}

// Return the CDATA Section unchanged, escape HTML entities in the rest.
return _wp_specialchars( $matches['non_cdata_followed_by_cdata'], ENT_XML1 ) . $matches['cdata'];
},
$safe_text
);

/**
* Filters a string cleaned and escaped for output in XML.
*
* Text passed to esc_xml() is stripped of invalid or special characters
* before output. HTML named character references are converted to their
* equivalent code points.
*
* @since 5.5.0
*
* @param string $safe_text The text after it has been escaped.
* @param string $text The text prior to being escaped.
*/
return apply_filters( 'esc_xml', $safe_text, $text );
}

/**
* Escape an HTML tag name.
*
Expand Down
63 changes: 59 additions & 4 deletions src/wp-includes/kses.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

// Ensure that these variables are added to the global namespace
// (e.g. if using namespaces / autoload in the current PHP environment).
global $allowedposttags, $allowedtags, $allowedentitynames;
global $allowedposttags, $allowedtags, $allowedentitynames, $allowedxmlentitynames;

if ( ! CUSTOM_TAGS ) {
/**
Expand Down Expand Up @@ -704,6 +704,18 @@
'there4',
);

/**
* @var string[] $allowedxmlentitynames Array of KSES allowed XML entitity names.
* @since 5.5.0
*/
$allowedxmlnamedentities = array(
'amp',
'lt',
'gt',
'apos',
'quot',
);

$allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags );
} else {
$allowedtags = wp_kses_array_lc( $allowedtags );
Expand Down Expand Up @@ -1745,17 +1757,27 @@ function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
* This function normalizes HTML entities. It will convert `AT&T` to the correct
* `AT&amp;T`, `&#00058;` to `&#58;`, `&#XYZZY;` to `&amp;#XYZZY;` and so on.
*
* When `$context` is set to 'xml', HTML entities are converted to their code points. For
* example, `AT&T&hellip;&#XYZZY;` is converted to `AT&amp;T…&amp;#XYZZY;`.
*
* @since 1.0.0
* @since 5.5.0 Added `$context` parameter.
*
* @param string $string Content to normalize entities.
* @param string $string Content to normalize entities.
* @param string $context Context for normalization. Can be either 'html' or 'xml'.
* Default 'html'.
* @return string Content with normalized entities.
*/
function wp_kses_normalize_entities( $string ) {
function wp_kses_normalize_entities( $string, $context = 'html' ) {
// Disarm all entities by converting & to &amp;
$string = str_replace( '&', '&amp;', $string );

// Change back the allowed entities in our entity whitelist.
$string = preg_replace_callback( '/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string );
if ( 'xml' === $context ) {
$string = preg_replace_callback( '/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_xml_named_entities', $string );
} else {
$string = preg_replace_callback( '/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string );
}
$string = preg_replace_callback( '/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string );
$string = preg_replace_callback( '/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string );

Expand Down Expand Up @@ -1786,6 +1808,39 @@ function wp_kses_named_entities( $matches ) {
return ( ! in_array( $i, $allowedentitynames, true ) ) ? "&amp;$i;" : "&$i;";
}

/**
* Callback for `wp_kses_normalize_entities()` regular expression.
*
* This function only accepts valid named entity references, which are finite,
* case-sensitive, and highly scrutinized by XML validators. HTML named entity
* references are converted to their code points.
*
* @since 5.5.0
*
* @global array $allowedentitynames
* @global array $allowedxmlnamedentities
*
* @param array $matches preg_replace_callback() matches array.
* @return string Correctly encoded entity.
*/
function wp_kses_xml_named_entities( $matches ) {
global $allowedentitynames, $allowedxmlnamedentities;

if ( empty( $matches[1] ) ) {
return '';
}

$i = $matches[1];

if ( in_array( $i, $allowedxmlnamedentities, true ) ) {
return "&$i;";
} elseif ( in_array( $i, $allowedentitynames, true ) ) {
return html_entity_decode( "&$i;", ENT_HTML5 );
}

return "&amp;$i;";
}

/**
* Callback for `wp_kses_normalize_entities()` regular expression.
*
Expand Down
119 changes: 119 additions & 0 deletions src/wp-includes/sitemaps.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
/**
* Sitemaps: Public functions
*
* This file contains a variety of public functions developers can use to interact with
* the XML Sitemaps API.
*
* @package WordPress
* @subpackage Sitemaps
Comment thread
swissspidy marked this conversation as resolved.
* @since 5.5.0
*/

/**
* Retrieves the current Sitemaps server instance.
*
* @since 5.5.0
*
* @return WP_Sitemaps|null Sitemaps instance, or null if sitemaps are disabled.
*/
function wp_sitemaps_get_server() {
/**
* Global Core Sitemaps instance.
*
* @since 5.5.0
*
* @var WP_Sitemaps $wp_sitemaps
*/
global $wp_sitemaps;

$is_enabled = (bool) get_option( 'blog_public' );

/**
* Filters whether XML Sitemaps are enabled or not.
*
* @since 5.5.0
*
* @param bool $is_enabled Whether XML Sitemaps are enabled or not. Defaults to true for public sites.
*/
$is_enabled = (bool) apply_filters( 'wp_sitemaps_is_enabled', $is_enabled );
Comment thread
swissspidy marked this conversation as resolved.

if ( ! $is_enabled ) {
return null;
}

// If there isn't a global instance, set and bootstrap the sitemaps system.
if ( empty( $wp_sitemaps ) ) {
$wp_sitemaps = new WP_Sitemaps();
$wp_sitemaps->init();

/**
* Fires when initializing the Sitemaps object.
*
* Additional sitemaps should be registered on this hook.
*
* @since 5.5.0
*
* @param WP_Sitemaps $sitemaps Server object.
*/
do_action( 'wp_sitemaps_init', $wp_sitemaps );
}

return $wp_sitemaps;
}

/**
* Gets a list of sitemap providers.
*
* @since 5.5.0
*
* @return array $sitemaps A list of registered sitemap providers.
*/
function wp_get_sitemaps() {
$sitemaps = wp_sitemaps_get_server();

if ( ! $sitemaps ) {
return array();
}

return $sitemaps->registry->get_sitemaps();
}

/**
* Registers a new sitemap provider.
*
* @since 5.5.0
*
* @param string $name Unique name for the sitemap provider.
* @param WP_Sitemaps_Provider $provider The `Sitemaps_Provider` instance implementing the sitemap.
* @return bool Returns true if the sitemap was added. False on failure.
*/
function wp_register_sitemap( $name, WP_Sitemaps_Provider $provider ) {
$sitemaps = wp_sitemaps_get_server();

if ( ! $sitemaps ) {
return false;
}

return $sitemaps->registry->add_sitemap( $name, $provider );
}

/**
* Gets the maximum number of URLs for a sitemap.
*
* @since 5.5.0
*
* @param string $object_type Object type for sitemap to be filtered (e.g. 'post', 'term', 'user').
* @return int The maximum number of URLs.
*/
function wp_sitemaps_get_max_urls( $object_type ) {
/**
* Filters the maximum number of URLs displayed on a sitemap.
*
* @since 5.5.0
*
* @param int $max_urls The maximum number of URLs included in a sitemap. Default 2000.
* @param string $object_type Object type for sitemap to be filtered (e.g. 'post', 'term', 'user').
*/
return apply_filters( 'wp_sitemaps_max_urls', 2000, $object_type );
}
Loading