Add upstream

This commit is contained in:
root
2019-10-24 00:12:05 +02:00
parent 85d41e4216
commit ac980f592c
3504 changed files with 1049983 additions and 29971 deletions

View File

@@ -0,0 +1,146 @@
<?php
/**
* The fallback buffer for users with no XML support.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing master sitemap xml files.
*
* @since 5.1.0
*/
abstract class Jetpack_Sitemap_Buffer_Fallback extends Jetpack_Sitemap_Buffer {
/**
* The buffer contents.
*
* @access protected
* @since 5.3.0
* @var string The buffer contents.
*/
protected $buffer;
public function __construct( $item_limit, $byte_limit, $time = '1970-01-01 00:00:00' ) {
$this->is_full_flag = false;
$this->is_empty_flag = true;
$this->timestamp = $time;
$this->finder = new Jetpack_Sitemap_Finder();
$this->item_capacity = max( 1, intval( $item_limit ) );
$this->byte_capacity = max( 1, intval( $byte_limit ) ) - strlen( $this->contents() );
}
/**
* Append an item to the buffer, if there is room for it,
* and set is_empty_flag to false. If there is no room,
* we set is_full_flag to true. If $item is null,
* don't do anything and report success.
*
* @since 5.3.0
*
* @param array $array The item to be added.
*
* @return bool True if the append succeeded, False if not.
*/
public function append( $array ) {
if ( is_null( $array ) ) {
return true;
}
if ( $this->is_full_flag ) {
return false;
}
if ( 0 >= $this->item_capacity || 0 >= $this->byte_capacity ) {
$this->is_full_flag = true;
return false;
} else {
$this->item_capacity -= 1;
$added_string = $this->array_to_xml_string( $array );
$this->buffer .= $added_string;
$this->is_empty_flag = false;
mbstring_binary_safe_encoding(); // So we can safely use strlen().
$this->byte_capacity -= strlen( $added_string );
reset_mbstring_encoding();
return true;
}
}
/**
* Detect whether the buffer is empty.
*
* @since 5.3.0
*
* @return bool True if the buffer is empty, false otherwise.
*/
public function is_empty() {
return $this->is_empty_flag;
}
/**
* Retrieve the contents of the buffer.
*
* @since 5.3.0
*
* @return string The contents of the buffer (with the footer included).
*/
public function contents() {
$root = $this->get_root_element();
return '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . $root[0] . $this->buffer . $root[1] . PHP_EOL;
}
/**
* Legacy implementation of array to XML conversion without using DOMDocument.
*
* @param Array $array
* @return String $result
*/
public function array_to_xml_string( $array, $parent = null, $root = null ) {
$string = '';
foreach ( $array as $key => $value ) {
// Only allow a-z, A-Z, colon, underscore, and hyphen.
$tag = preg_replace( '/[^a-zA-Z:_-]/', '_', $key );
if ( is_array( $value ) ) {
$string .= "<$tag>";
$string .= $this->array_to_xml_string( $value );
$string .= "</$tag>";
} elseif ( is_null( $value ) ) {
$string .= "<$tag />";
} else {
$string .= "<$tag>" . htmlspecialchars( $value ) . "</$tag>";
}
}
return $string;
}
/**
* Render an associative array of XML attribute key/value pairs.
*
* @access public
* @since 5.3.0
*
* @param array $array Key/value array of attributes.
*
* @return string The rendered attribute string.
*/
public static function array_to_xml_attr_string( $array ) {
$string = '';
foreach ( $array as $key => $value ) {
$key = preg_replace( '/[^a-zA-Z:_-]/', '_', $key );
$string .= ' ' . $key . '="' . esc_attr( $value ) . '"';
}
return $string;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Image
* extends the Jetpack_Sitemap_Buffer class to represent the single image sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap image xml files for users that have no libxml support.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Image extends Jetpack_Sitemap_Buffer_Fallback {
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the XML namespaces included in image sitemaps.
*
* @module sitemaps
*
* @since 4.8.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_image_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1',
)
);
$sitemap_xsl_url = $this->finder->construct_sitemap_url( 'sitemap.xsl' );
$jetpack_version = JETPACK__VERSION;
$this->root = array(
"<!-- generator='jetpack-{$jetpack_version}' -->" . PHP_EOL
. "<?xml-stylesheet type='text/xsl' href='{$sitemap_xsl_url}'?>" . PHP_EOL
. '<urlset ' . $this->array_to_xml_attr_string( $namespaces ) . '>' . PHP_EOL,
'</urlset>',
);
$this->byte_capacity -= strlen( join( '', $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Image
* extends the Jetpack_Sitemap_Buffer class to represent the single image sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap image xml files.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Image extends Jetpack_Sitemap_Buffer {
public function __construct( $item_limit, $byte_limit, $time = '1970-01-01 00:00:00' ) {
parent::__construct( $item_limit, $byte_limit, $time );
$this->doc->appendChild(
$this->doc->createComment( "generator='jetpack-" . JETPACK__VERSION . "'" )
);
$this->doc->appendChild(
$this->doc->createProcessingInstruction(
'xml-stylesheet',
'type="text/xsl" href="' . $this->finder->construct_sitemap_url( 'image-sitemap.xsl' ) . '"'
)
);
}
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the XML namespaces included in image sitemaps.
*
* @module sitemaps
*
* @since 4.8.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_image_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1',
)
);
$this->root = $this->doc->createElement( 'urlset' );
foreach ( $namespaces as $name => $value ) {
$this->root->setAttribute( $name, $value );
}
$this->doc->appendChild( $this->root );
$this->byte_capacity -= strlen( $this->doc->saveXML( $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Master
* extends the Jetpack_Sitemap_Buffer class to represent the master sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing master sitemap xml files for users without libxml support.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Master extends Jetpack_Sitemap_Buffer_Fallback {
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
$sitemap_index_xsl_url = $this->finder->construct_sitemap_url( 'sitemap-index.xsl' );
$jetpack_version = JETPACK__VERSION;
$this->root = array(
"<!-- generator='jetpack-{$jetpack_version}' -->" . PHP_EOL
. "<?xml-stylesheet type='text/xsl' href='{$sitemap_index_xsl_url}'?>" . PHP_EOL
. "<sitemapindex xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>" . PHP_EOL,
'</sitemapindex>',
);
$this->byte_capacity -= strlen( join( '', $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Master
* extends the Jetpack_Sitemap_Buffer class to represent the master sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing master sitemap xml files.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Master extends Jetpack_Sitemap_Buffer {
public function __construct( $item_limit, $byte_limit, $time = '1970-01-01 00:00:00' ) {
parent::__construct( $item_limit, $byte_limit, $time );
$this->doc->appendChild(
$this->doc->createComment( "generator='jetpack-" . JETPACK__VERSION . "'" )
);
$this->doc->appendChild(
$this->doc->createProcessingInstruction(
'xml-stylesheet',
'type="text/xsl" href="' . $this->finder->construct_sitemap_url( 'sitemap-index.xsl' ) . '"'
)
);
}
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
$this->root = $this->doc->createElement( 'sitemapindex' );
$this->root->setAttribute( 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9' );
$this->doc->appendChild( $this->root );
$this->byte_capacity -= strlen( $this->doc->saveXML( $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_News
* extends the Jetpack_Sitemap_Buffer class to represent the single news sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap image xml files for users without libxml support.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_News extends Jetpack_Sitemap_Buffer_Fallback {
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the attribute value pairs used for namespace and namespace URI mappings.
*
* @module sitemaps
*
* @since 4.8.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_news_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'xmlns:news' => 'http://www.google.com/schemas/sitemap-news/0.9',
)
);
$jetpack_version = JETPACK__VERSION;
$news_sitemap_xsl_url = $this->finder->construct_sitemap_url( 'news-sitemap.xsl' );
$this->root = array(
"<!-- generator='jetpack-{$jetpack_version}' -->" . PHP_EOL
. "<?xml-stylesheet type='text/xsl' href='{$news_sitemap_xsl_url}'?>" . PHP_EOL
. '<urlset ' . $this->array_to_xml_attr_string( $namespaces ) . '>',
'</urlset>',
);
$this->byte_capacity -= strlen( join( '', $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_News
* extends the Jetpack_Sitemap_Buffer class to represent the single news sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap image xml files.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_News extends Jetpack_Sitemap_Buffer {
public function __construct( $item_limit, $byte_limit, $time = '1970-01-01 00:00:00' ) {
parent::__construct( $item_limit, $byte_limit, $time );
$this->doc->appendChild(
$this->doc->createComment( "generator='jetpack-" . JETPACK__VERSION . "'" )
);
$this->doc->appendChild(
$this->doc->createProcessingInstruction(
'xml-stylesheet',
'type="text/xsl" href="' . $this->finder->construct_sitemap_url( 'news-sitemap.xsl' ) . '"'
)
);
}
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the attribute value pairs used for namespace and namespace URI mappings.
*
* @module sitemaps
*
* @since 4.8.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_news_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'xmlns:news' => 'http://www.google.com/schemas/sitemap-news/0.9',
)
);
$this->root = $this->doc->createElement( 'urlset' );
foreach ( $namespaces as $name => $value ) {
$this->root->setAttribute( $name, $value );
}
$this->doc->appendChild( $this->root );
$this->byte_capacity -= strlen( $this->doc->saveXML( $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Page
* extends the Jetpack_Sitemap_Buffer class to represent the single page sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap page xml files for users with no libxml support.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Page extends Jetpack_Sitemap_Buffer_Fallback {
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the attribute value pairs used for namespace and namespace URI mappings.
*
* @module sitemaps
*
* @since 3.9.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
)
);
$jetpack_version = JETPACK__VERSION;
$sitemap_xsl_url = $this->finder->construct_sitemap_url( 'sitemap.xsl' );
$this->root = array(
"<!-- generator='jetpack-{$jetpack_version}' -->" . PHP_EOL
. "<?xml-stylesheet type='text/xsl' href='{$sitemap_xsl_url}'?>" . PHP_EOL
. '<urlset ' . $this->array_to_xml_attr_string( $namespaces ) . '>',
'</urlset>',
);
$this->byte_capacity -= strlen( join( '', $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Page
* extends the Jetpack_Sitemap_Buffer class to represent the single page sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap page xml files.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Page extends Jetpack_Sitemap_Buffer {
public function __construct( $item_limit, $byte_limit, $time = '1970-01-01 00:00:00' ) {
parent::__construct( $item_limit, $byte_limit, $time );
$this->doc->appendChild(
$this->doc->createComment( "generator='jetpack-" . JETPACK__VERSION . "'" )
);
$this->doc->appendChild(
$this->doc->createProcessingInstruction(
'xml-stylesheet',
'type="text/xsl" href="' . $this->finder->construct_sitemap_url( 'sitemap.xsl' ) . '"'
)
);
}
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the attribute value pairs used for namespace and namespace URI mappings.
*
* @module sitemaps
*
* @since 3.9.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
)
);
$this->root = $this->doc->createElement( 'urlset' );
foreach ( $namespaces as $name => $value ) {
$this->root->setAttribute( $name, $value );
}
$this->doc->appendChild( $this->root );
$this->byte_capacity -= strlen( $this->doc->saveXML( $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Video
* extends the Jetpack_Sitemap_Buffer class to represent the single video sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap video xml files for users without libxml support.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Video extends Jetpack_Sitemap_Buffer_Fallback {
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the XML namespaces included in video sitemaps.
*
* @module sitemaps
*
* @since 4.8.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_video_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'xmlns:video' => 'http://www.google.com/schemas/sitemap-video/1.1',
)
);
$video_sitemap_xsl_url = $this->finder->construct_sitemap_url( 'video-sitemap.xsl' );
$jetpack_version = JETPACK__VERSION;
$this->root = array(
"<!-- generator='jetpack-{$jetpack_version}' -->" . PHP_EOL
. "<?xml-stylesheet type='text/xsl' href='{$video_sitemap_xsl_url}'?>" . PHP_EOL
. '<urlset ' . $this->array_to_xml_attr_string( $namespaces ) . '>',
'</urlset>',
);
$this->byte_capacity -= strlen( join( '', $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer_Video
* extends the Jetpack_Sitemap_Buffer class to represent the single video sitemap
* buffer.
*
* @since 5.3.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap video xml files.
*
* @since 5.3.0
*/
class Jetpack_Sitemap_Buffer_Video extends Jetpack_Sitemap_Buffer {
public function __construct( $item_limit, $byte_limit, $time = '1970-01-01 00:00:00' ) {
parent::__construct( $item_limit, $byte_limit, $time );
$this->doc->appendChild(
$this->doc->createComment( "generator='jetpack-" . JETPACK__VERSION . "'" )
);
$this->doc->appendChild(
$this->doc->createProcessingInstruction(
'xml-stylesheet',
'type="text/xsl" href="' . $this->finder->construct_sitemap_url( 'video-sitemap.xsl' ) . '"'
)
);
}
protected function get_root_element() {
if ( ! isset( $this->root ) ) {
/**
* Filter the XML namespaces included in video sitemaps.
*
* @module sitemaps
*
* @since 4.8.0
*
* @param array $namespaces Associative array with namespaces and namespace URIs.
*/
$namespaces = apply_filters(
'jetpack_sitemap_video_ns',
array(
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd',
'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
'xmlns:video' => 'http://www.google.com/schemas/sitemap-video/1.1',
)
);
$this->root = $this->doc->createElement( 'urlset' );
foreach ( $namespaces as $name => $value ) {
$this->root->setAttribute( $name, $value );
}
$this->doc->appendChild( $this->root );
$this->byte_capacity -= strlen( $this->doc->saveXML( $this->root ) );
}
return $this->root;
}
}

View File

@@ -0,0 +1,325 @@
<?php
/**
* Sitemaps (per the protocol) are essentially lists of XML fragments;
* lists which are subject to size constraints. The Jetpack_Sitemap_Buffer
* class abstracts the details of constructing these lists while
* maintaining the constraints.
*
* @since 4.8.0
* @package Jetpack
*/
/**
* A buffer for constructing sitemap xml files.
*
* Models a list of strings such that
*
* 1. the list must have a bounded number of entries,
* 2. the concatenation of the strings must have bounded
* length (including some header and footer strings), and
* 3. each item has a timestamp, and we need to keep track
* of the most recent timestamp of the items in the list.
*
* @since 4.8.0
*/
abstract class Jetpack_Sitemap_Buffer {
/**
* Largest number of items the buffer can hold.
*
* @access protected
* @since 4.8.0
* @var int $item_capacity The item capacity.
*/
protected $item_capacity;
/**
* Largest number of bytes the buffer can hold.
*
* @access protected
* @since 4.8.0
* @var int $byte_capacity The byte capacity.
*/
protected $byte_capacity;
/**
* Flag which detects when the buffer is full.
*
* @access protected
* @since 4.8.0
* @var bool $is_full_flag The flag value. This flag is set to false on construction and only flipped to true if we've tried to add something and failed.
*/
protected $is_full_flag;
/**
* Flag which detects when the buffer is empty.
*
* @access protected
* @since 4.8.0
* @var bool $is_empty_flag The flag value. This flag is set to true on construction and only flipped to false if we've tried to add something and succeeded.
*/
protected $is_empty_flag;
/**
* The most recent timestamp seen by the buffer.
*
* @access protected
* @since 4.8.0
* @var string $timestamp Must be in 'YYYY-MM-DD hh:mm:ss' format.
*/
protected $timestamp;
/**
* The DOM document object that is currently being used to construct the XML doc.
*
* @access protected
* @since 5.3.0
* @var DOMDocument $doc
*/
protected $doc = null;
/**
* The root DOM element object that holds everything inside. Do not use directly, call
* the get_root_element getter method instead.
*
* @access protected
* @since 5.3.0
* @var DOMElement $doc
*/
protected $root = null;
/**
* Helper class to construct sitemap paths.
*
* @since 5.3.0
* @protected
* @var Jetpack_Sitemap_Finder
*/
protected $finder;
/**
* Construct a new Jetpack_Sitemap_Buffer.
*
* @since 4.8.0
*
* @param int $item_limit The maximum size of the buffer in items.
* @param int $byte_limit The maximum size of the buffer in bytes.
* @param string $time The initial datetime of the buffer. Must be in 'YYYY-MM-DD hh:mm:ss' format.
*/
public function __construct( $item_limit, $byte_limit, $time ) {
$this->is_full_flag = false;
$this->timestamp = $time;
$this->finder = new Jetpack_Sitemap_Finder();
$this->doc = new DOMDocument( '1.0', 'UTF-8' );
$this->item_capacity = max( 1, intval( $item_limit ) );
$this->byte_capacity = max( 1, intval( $byte_limit ) ) - strlen( $this->doc->saveXML() );
}
/**
* Returns a DOM element that contains all sitemap elements.
*
* @access protected
* @since 5.3.0
* @return DOMElement $root
*/
abstract protected function get_root_element();
/**
* Append an item to the buffer, if there is room for it,
* and set is_empty_flag to false. If there is no room,
* we set is_full_flag to true. If $item is null,
* don't do anything and report success.
*
* @since 4.8.0
* @deprecated 5.3.0 Use Jetpack_Sitemap_Buffer::append.
*
* @param string $item The item to be added.
*/
public function try_to_add_item( $item ) {
_deprecated_function(
'Jetpack_Sitemap_Buffer::try_to_add_item',
'5.3.0',
'Jetpack_Sitemap_Buffer::append'
);
$this->append( $item );
}
/**
* Append an item to the buffer, if there is room for it,
* and set is_empty_flag to false. If there is no room,
* we set is_full_flag to true. If $item is null,
* don't do anything and report success.
*
* @since 5.3.0
*
* @param array $array The item to be added.
*
* @return bool True if the append succeeded, False if not.
*/
public function append( $array ) {
if ( is_null( $array ) ) {
return true;
}
if ( $this->is_full_flag ) {
return false;
}
if ( 0 >= $this->item_capacity || 0 >= $this->byte_capacity ) {
$this->is_full_flag = true;
return false;
} else {
$this->item_capacity -= 1;
$added_element = $this->array_to_xml_string( $array, $this->get_root_element(), $this->doc );
$this->byte_capacity -= strlen( $this->doc->saveXML( $added_element ) );
return true;
}
}
/**
* Retrieve the contents of the buffer.
*
* @since 4.8.0
*
* @return string The contents of the buffer (with the footer included).
*/
public function contents() {
if ( $this->is_empty() ) {
// The sitemap should have at least the root element added to the DOM.
$this->get_root_element();
}
return $this->doc->saveXML();
}
/**
* Retrieve the document object.
*
* @since 5.3.0
* @return DOMDocument $doc
*/
public function get_document() {
return $this->doc;
}
/**
* Detect whether the buffer is full.
*
* @since 4.8.0
*
* @return bool True if the buffer is full, false otherwise.
*/
public function is_full() {
return $this->is_full_flag;
}
/**
* Detect whether the buffer is empty.
*
* @since 4.8.0
*
* @return bool True if the buffer is empty, false otherwise.
*/
public function is_empty() {
return (
! isset( $this->root )
|| ! $this->root->hasChildNodes()
);
}
/**
* Update the timestamp of the buffer.
*
* @since 4.8.0
*
* @param string $new_time A datetime string in 'YYYY-MM-DD hh:mm:ss' format.
*/
public function view_time( $new_time ) {
$this->timestamp = max( $this->timestamp, $new_time );
}
/**
* Retrieve the timestamp of the buffer.
*
* @since 4.8.0
*
* @return string A datetime string in 'YYYY-MM-DD hh:mm:ss' format.
*/
public function last_modified() {
return $this->timestamp;
}
/**
* Render an associative array as an XML string. This is needed because
* SimpleXMLElement only handles valid XML, but we sometimes want to
* pass around (possibly invalid) fragments. Note that 'null' values make
* a tag self-closing; this is only sometimes correct (depending on the
* version of HTML/XML); see the list of 'void tags'.
*
* Example:
*
* array(
* 'html' => array( |<html xmlns="foo">
* 'head' => array( | <head>
* 'title' => 'Woo!', | <title>Woo!</title>
* ), | </head>
* 'body' => array( ==> | <body>
* 'h2' => 'Some thing', | <h2>Some thing</h2>
* 'p' => 'it's all up ons', | <p>it's all up ons</p>
* 'br' => null, | <br />
* ), | </body>
* ), |</html>
* )
*
* @access protected
* @since 3.9.0
* @since 4.8.0 Rename, add $depth parameter, and change return type.
* @since 5.3.0 Refactor, remove $depth parameter, add $parent and $root, make access protected.
*
* @param array $array A recursive associative array of tag/child relationships.
* @param DOMElement $parent (optional) an element to which new children should be added.
* @param DOMDocument $root (optional) the parent document.
*
* @return string|DOMDocument The rendered XML string or an object if root element is specified.
*/
protected function array_to_xml_string( $array, $parent = null, $root = null ) {
$return_string = false;
if ( null === $parent ) {
$return_string = true;
$parent = $root = new DOMDocument();
}
if ( is_array( $array ) ) {
foreach ( $array as $key => $value ) {
$element = $root->createElement( $key );
$parent->appendChild( $element );
if ( is_array( $value ) ) {
foreach ( $value as $child_key => $child_value ) {
$child = $root->createElement( $child_key );
$element->appendChild( $child );
$child->appendChild( self::array_to_xml_string( $child_value, $child, $root ) );
}
} else {
$element->appendChild(
$root->createTextNode( $value )
);
}
}
} else {
$element = $root->createTextNode( $array );
$parent->appendChild( $element );
}
if ( $return_string ) {
return $root->saveHTML();
} else {
return $element;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
<?php
/**
* Sitemap-related constants.
*
* @package Jetpack
* @since 4.8.0
* @author Automattic
*/
/**
* Maximum size (in bytes) of a sitemap xml file.
* Max is 716800 = 700kb to avoid potential failures for default memcached limits (1MB)
*
* @link http://www.sitemaps.org/
* @since 4.8.0
*/
if ( ! defined( 'JP_SITEMAP_MAX_BYTES' ) ) {
define( 'JP_SITEMAP_MAX_BYTES', 716800 );
}
/**
* Maximum size (in url nodes) of a sitemap xml file.
* Per the spec, max value is 50000.
*
* @link http://www.sitemaps.org/
* @since 4.8.0
*/
if ( ! defined( 'JP_SITEMAP_MAX_ITEMS' ) ) {
define( 'JP_SITEMAP_MAX_ITEMS', 2000 );
}
/**
* Maximum size (in url nodes) of a news sitemap xml file.
* Per the spec, max value is 1000.
*
* @link https://support.google.com/news/publisher/answer/74288?hl=en
* @since 4.8.0
*/
if ( ! defined( 'JP_NEWS_SITEMAP_MAX_ITEMS' ) ) {
define( 'JP_NEWS_SITEMAP_MAX_ITEMS', 1000 );
}
/**
* Batch size for database queries.
*
* @since 4.8.0
*/
if ( ! defined( 'JP_SITEMAP_BATCH_SIZE' ) ) {
define( 'JP_SITEMAP_BATCH_SIZE', 50 );
}
/**
* Number of sitemap files to update on each run.
*
* @since 4.8.0
*/
if ( ! defined( 'JP_SITEMAP_UPDATE_SIZE' ) ) {
define( 'JP_SITEMAP_UPDATE_SIZE', 100 );
}
/**
* Number of seconds between sitemap updates.
*
* @since 4.8.0
*/
if ( ! defined( 'JP_SITEMAP_INTERVAL' ) ) {
define( 'JP_SITEMAP_INTERVAL', 12 * HOUR_IN_SECONDS );
}
/**
* Number of seconds to lock the sitemap state.
*
* @since 4.8.0
*/
if ( ! defined( 'JP_SITEMAP_LOCK_INTERVAL' ) ) {
define( 'JP_SITEMAP_LOCK_INTERVAL', 15 * MINUTE_IN_SECONDS );
}
/**
* Cache lifetime of news sitemap (in seconds).
*
* @since 4.8.0
*/
if ( ! defined( 'JP_NEWS_SITEMAP_INTERVAL' ) ) {
define( 'JP_NEWS_SITEMAP_INTERVAL', 12 * HOUR_IN_SECONDS );
}
/*
* These constants represent the types of various kinds of sitemaps.
* Note: these strings are used as 'post_types' in the database, and
* so must be at most 20 characters long.
*/
if ( ! defined( 'JP_MASTER_SITEMAP_TYPE' ) ) {
define( 'JP_MASTER_SITEMAP_TYPE', 'jp_sitemap_master' );
}
if ( ! defined( 'JP_PAGE_SITEMAP_TYPE' ) ) {
define( 'JP_PAGE_SITEMAP_TYPE', 'jp_sitemap' );
}
if ( ! defined( 'JP_PAGE_SITEMAP_INDEX_TYPE' ) ) {
define( 'JP_PAGE_SITEMAP_INDEX_TYPE', 'jp_sitemap_index' );
}
if ( ! defined( 'JP_IMAGE_SITEMAP_TYPE' ) ) {
define( 'JP_IMAGE_SITEMAP_TYPE', 'jp_img_sitemap' );
}
if ( ! defined( 'JP_IMAGE_SITEMAP_INDEX_TYPE' ) ) {
define( 'JP_IMAGE_SITEMAP_INDEX_TYPE', 'jp_img_sitemap_index' );
}
if ( ! defined( 'JP_VIDEO_SITEMAP_TYPE' ) ) {
define( 'JP_VIDEO_SITEMAP_TYPE', 'jp_vid_sitemap' );
}
if ( ! defined( 'JP_VIDEO_SITEMAP_INDEX_TYPE' ) ) {
define( 'JP_VIDEO_SITEMAP_INDEX_TYPE', 'jp_vid_sitemap_index' );
}
/**
* The name (with extension) of a sitemap file of the given
* type and number.
*
* @since 4.8.0
*
* @param string $type The sitemap type.
* @param string $number The sitemap number.
*
* @return string The filename.
*/
function jp_sitemap_filename( $type, $number = null ) {
if ( is_null( $number ) ) {
return "error-not-int-$type-$number.xml";
} elseif ( JP_MASTER_SITEMAP_TYPE === $type ) {
return 'sitemap.xml';
} elseif ( JP_PAGE_SITEMAP_TYPE === $type ) {
return "sitemap-$number.xml";
} elseif ( JP_PAGE_SITEMAP_INDEX_TYPE === $type ) {
return "sitemap-index-$number.xml";
} elseif ( JP_IMAGE_SITEMAP_TYPE === $type ) {
return "image-sitemap-$number.xml";
} elseif ( JP_IMAGE_SITEMAP_INDEX_TYPE === $type ) {
return "image-sitemap-index-$number.xml";
} elseif ( JP_VIDEO_SITEMAP_TYPE === $type ) {
return "video-sitemap-$number.xml";
} elseif ( JP_VIDEO_SITEMAP_INDEX_TYPE === $type ) {
return "video-sitemap-index-$number.xml";
} else {
return "error-bad-type-$type-$number.xml";
}
}
/**
* The index type corresponding to a sitemap type.
*
* @since 4.8.0
*
* @param string $type The sitemap type.
*
* @return string The index type.
*/
function jp_sitemap_index_type_of( $type ) {
if ( JP_PAGE_SITEMAP_TYPE === $type ) {
return JP_PAGE_SITEMAP_INDEX_TYPE;
} elseif ( JP_IMAGE_SITEMAP_TYPE === $type ) {
return JP_IMAGE_SITEMAP_INDEX_TYPE;
} elseif ( JP_VIDEO_SITEMAP_TYPE === $type ) {
return JP_VIDEO_SITEMAP_INDEX_TYPE;
} else {
return "error-bad-type-$type";
}
}
/**
* The sitemap type corresponding to an index type.
*
* @since 4.8.0
*
* @param string $type The index type.
*
* @return string The sitemap type.
*/
function jp_sitemap_child_type_of( $type ) {
if ( JP_PAGE_SITEMAP_INDEX_TYPE === $type ) {
return JP_PAGE_SITEMAP_TYPE;
} elseif ( JP_IMAGE_SITEMAP_INDEX_TYPE === $type ) {
return JP_IMAGE_SITEMAP_TYPE;
} elseif ( JP_VIDEO_SITEMAP_INDEX_TYPE === $type ) {
return JP_VIDEO_SITEMAP_TYPE;
} else {
return "error-bad-type-$type";
}
}
/**
* Convert '0000-00-00 00:00:00' to '0000-00-00T00:00:00Z'.
* Note that the input is assumed to be in UTC (a.k.a. GMT).
*
* @link https://www.w3.org/TR/NOTE-datetime
* @since 4.8.0
*
* @param string $datetime The timestamp to convert.
*
* @return string The converted timestamp.
*/
function jp_sitemap_datetime( $datetime ) {
$regex = '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/';
if ( preg_match( $regex, $datetime ) ) {
return str_replace( ' ', 'T', $datetime ) . 'Z';
} else {
return $datetime;
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* The functions in this class provide an API for handling
* sitemap related URIs.
*
* @package Jetpack
* @since 4.8.0
* @author Automattic
*/
/**
* The Jetpack_Sitemap_Finder object deals with constructing
* sitemap URIs.
*
* @since 4.8.0
*/
class Jetpack_Sitemap_Finder {
/**
* Construct the complete URL of a sitemap file. Depends on
* permalink settings.
*
* @access public
* @since 4.8.0
* @since 4.8.1 Call jetpack_sitemap_uri()
*
* @param string $filename The filename of the sitemap.
*
* @return string Complete URI of the given sitemap file.
*/
public function construct_sitemap_url( $filename ) {
$url = jetpack_sitemap_uri( $filename );
if ( pathinfo( $filename, PATHINFO_EXTENSION ) === 'xsl' ) {
// strip scheme for sites where sitemap could be access via http or https
$url = preg_replace( '/^https?:/', '', $url );
}
return $url;
}
/**
* Path and query prefix of sitemap files. Depends on permalink
* settings.
*
* @access public
* @since 4.8.0
*
* @return string The path+query prefix.
*/
public function the_jetpack_sitemap_path_and_query_prefix() {
global $wp_rewrite;
// Get path fragment from home_url().
$home = wp_parse_url( home_url() );
if ( isset( $home['path'] ) ) {
$home_path = $home['path'];
} else {
$home_path = '';
}
// Get additional path fragment from filter.
$location = Jetpack_Options::get_option_and_ensure_autoload(
'jetpack_sitemap_location',
''
);
if ( $wp_rewrite->using_index_permalinks() ) {
return $home_path . '/index.php' . $location . '/';
} elseif ( $wp_rewrite->using_permalinks() ) {
return $home_path . $location . '/';
} else {
return $home_path . $location . '/?jetpack-sitemap=';
}
}
/**
* Examine a path+query URI fragment looking for a sitemap request.
*
* @access public
* @since 4.8.0
*
* @param string $raw_uri A URI (path+query only) to test for sitemap-ness.
*
* @return array @args {
* @type string $sitemap_name The recognized sitemap name (or null).
* }
*/
public function recognize_sitemap_uri( $raw_uri ) {
// The path+query where sitemaps are served.
$sitemap_path = $this->the_jetpack_sitemap_path_and_query_prefix();
// A regex which detects $sitemap_path at the beginning of a string.
$path_regex = '/^' . preg_quote( $sitemap_path, '/' ) . '/';
// Check that the request URI begins with the sitemap path.
if ( preg_match( $path_regex, $raw_uri ) ) {
// Strip off the $sitemap_path and any trailing slash.
$stripped_uri = preg_replace( $path_regex, '', rtrim( $raw_uri, '/' ) );
} else {
$stripped_uri = '';
}
// Check that the stripped uri begins with one of the sitemap prefixes.
if ( preg_match( '/^sitemap|^image-s|^news-s|^video-s/', $stripped_uri ) ) {
$filename = $stripped_uri;
} else {
$filename = null;
}
return array(
'sitemap_name' => $filename,
);
}
}

View File

@@ -0,0 +1,431 @@
<?php
/**
* Sitemaps are stored in the database using a custom table. This class
* provides a small API for storing and retrieving sitemap data so we can
* avoid lots of explicit SQL juggling while building sitemaps. This file
* also includes the SQL used to retrieve posts and images to be included
* in the sitemaps.
*
* @since 4.8.0
* @package Jetpack
*/
/* Ensure sitemap constants are available. */
require_once dirname( __FILE__ ) . '/sitemap-constants.php';
/**
* This object handles any database interaction required
* for sitemap generation.
*
* @since 4.8.0
*/
class Jetpack_Sitemap_Librarian {
/**
* Retrieve a single sitemap with given name and type.
* Returns null if no such sitemap exists.
*
* @access public
* @since 4.8.0
*
* @param string $name Name of the sitemap to be retrieved.
* @param string $type Type of the sitemap to be retrieved.
*
* @return array $args {
* @type int $id ID number of the sitemap in the database.
* @type string $timestamp Most recent timestamp of the resources pointed to.
* @type string $name Name of the sitemap in the database.
* @type string $type Type of the sitemap in the database.
* @type string $text The content of the sitemap.
* }
*/
public function read_sitemap_data( $name, $type ) {
$post_array = get_posts(
array(
'numberposts' => 1,
'title' => $name,
'post_type' => $type,
'post_status' => 'draft',
)
);
$the_post = array_shift( $post_array );
if ( null === $the_post ) {
return null;
} else {
return array(
'id' => $the_post->ID,
'timestamp' => $the_post->post_date,
'name' => $the_post->post_title,
'type' => $the_post->post_type,
'text' => base64_decode( $the_post->post_content ),
);
}
}
/**
* Store a sitemap of given type and index in the database.
* Note that the timestamp is reencoded as 'Y-m-d H:i:s'.
*
* If a sitemap with that type and name does not exist, create it.
* If a sitemap with that type and name does exist, update it.
*
* @access public
* @since 4.8.0
*
* @param string $index Index of the sitemap to be stored.
* @param string $type Type of the sitemap to be stored.
* @param string $contents Contents of the sitemap to be stored.
* @param string $timestamp Timestamp of the sitemap to be stored, in 'YYYY-MM-DD hh:mm:ss' format.
*/
public function store_sitemap_data( $index, $type, $contents, $timestamp ) {
$name = jp_sitemap_filename( $type, $index );
$the_post = $this->read_sitemap_data( $name, $type );
if ( null === $the_post ) {
// Post does not exist.
wp_insert_post(
array(
'post_title' => $name,
'post_content' => base64_encode( $contents ),
'post_type' => $type,
'post_date' => date( 'Y-m-d H:i:s', strtotime( $timestamp ) ),
)
);
} else {
// Post does exist.
wp_insert_post(
array(
'ID' => $the_post['id'],
'post_title' => $name,
'post_content' => base64_encode( $contents ),
'post_type' => $type,
'post_date' => date( 'Y-m-d H:i:s', strtotime( $timestamp ) ),
)
);
}
}
/**
* Delete a sitemap by name and type.
*
* @access public
* @since 4.8.0
*
* @param string $name Row name.
* @param string $type Row type.
*
* @return bool 'true' if a row was deleted, 'false' otherwise.
*/
public function delete_sitemap_data( $name, $type ) {
$the_post = $this->read_sitemap_data( $name, $type );
if ( null === $the_post ) {
return false;
} else {
wp_delete_post( $the_post['id'] );
return true;
}
}
/**
* Retrieve the contents of a sitemap with given name and type.
* If no such sitemap exists, return the empty string. Note that the
* returned string is run through wp_specialchars_decode.
*
* @access public
* @since 4.8.0
*
* @param string $name Row name.
* @param string $type Row type.
*
* @return string Text of the specified sitemap, or the empty string.
*/
public function get_sitemap_text( $name, $type ) {
$row = $this->read_sitemap_data( $name, $type );
if ( null === $row ) {
return '';
} else {
return $row['text'];
}
}
/**
* Delete numbered sitemaps named prefix-(p+1), prefix-(p+2), ...
* until the first nonexistent sitemap is found.
*
* @access public
* @since 4.8.0
*
* @param int $position Number before the first sitemap to be deleted.
* @param string $type Sitemap type.
*/
public function delete_numbered_sitemap_rows_after( $position, $type ) {
$any_left = true;
while ( true === $any_left ) {
$position++;
$name = jp_sitemap_filename( $type, $position );
$any_left = $this->delete_sitemap_data( $name, $type );
}
}
/**
* Deletes all stored sitemap data.
*
* @access public
* @since 4.8.0
*/
public function delete_all_stored_sitemap_data() {
$this->delete_sitemap_type_data( JP_MASTER_SITEMAP_TYPE );
$this->delete_sitemap_type_data( JP_PAGE_SITEMAP_TYPE );
$this->delete_sitemap_type_data( JP_PAGE_SITEMAP_INDEX_TYPE );
$this->delete_sitemap_type_data( JP_IMAGE_SITEMAP_TYPE );
$this->delete_sitemap_type_data( JP_IMAGE_SITEMAP_INDEX_TYPE );
$this->delete_sitemap_type_data( JP_VIDEO_SITEMAP_TYPE );
$this->delete_sitemap_type_data( JP_VIDEO_SITEMAP_INDEX_TYPE );
}
/**
* Deletes all sitemap data of specific type
*
* @access protected
* @since 5.3.0
*
* @param String $type Type of sitemap.
*/
protected function delete_sitemap_type_data( $type ) {
$ids = get_posts(
array(
'post_type' => $type,
'post_status' => 'draft',
'fields' => 'ids',
)
);
foreach ( $ids as $id ) {
wp_trash_post( $id );
}
}
/**
* Retrieve an array of sitemap rows (of a given type) sorted by ID.
*
* Returns the smallest $num_posts sitemap rows (measured by ID)
* of the given type which are larger than $from_id.
*
* @access public
* @since 4.8.0
*
* @param string $type Type of the sitemap rows to retrieve.
* @param int $from_id Greatest lower bound of retrieved sitemap post IDs.
* @param int $num_posts Largest number of sitemap posts to retrieve.
*
* @return array The sitemaps, as an array of associative arrays.
*/
public function query_sitemaps_after_id( $type, $from_id, $num_posts ) {
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT *
FROM $wpdb->posts
WHERE post_type=%s
AND post_status=%s
AND ID>%d
ORDER BY ID ASC
LIMIT %d;",
$type,
'draft',
$from_id,
$num_posts
),
ARRAY_A
); // WPCS: db call ok; no-cache ok.
}
/**
* Retrieve an array of posts sorted by ID.
*
* More precisely, returns the smallest $num_posts posts
* (measured by ID) which are larger than $from_id.
*
* @access public
* @since 4.8.0
*
* @param int $from_id Greatest lower bound of retrieved post IDs.
* @param int $num_posts Largest number of posts to retrieve.
*
* @return array The posts.
*/
public function query_posts_after_id( $from_id, $num_posts ) {
global $wpdb;
// Get the list of post types to include and prepare for query.
$post_types = Jetpack_Options::get_option_and_ensure_autoload(
'jetpack_sitemap_post_types',
array( 'page', 'post' )
);
foreach ( (array) $post_types as $i => $post_type ) {
$post_types[ $i ] = $wpdb->prepare( '%s', $post_type );
}
$post_types_list = join( ',', $post_types );
return $wpdb->get_results(
$wpdb->prepare(
"SELECT *
FROM $wpdb->posts
WHERE post_status='publish'
AND post_type IN ($post_types_list)
AND ID>%d
ORDER BY ID ASC
LIMIT %d;",
$from_id,
$num_posts
)
); // WPCS: db call ok; no-cache ok.
}
/**
* Get the most recent timestamp among approved comments for the given post_id.
*
* @access public
* @since 4.8.0
*
* @param int $post_id Post identifier.
*
* @return int Timestamp in 'Y-m-d h:i:s' format (UTC) of the most recent comment on the given post, or null if no such comments exist.
*/
public function query_latest_approved_comment_time_on_post( $post_id ) {
global $wpdb;
return $wpdb->get_var(
$wpdb->prepare(
"SELECT MAX(comment_date_gmt)
FROM $wpdb->comments
WHERE comment_post_ID = %d AND comment_approved = '1' AND comment_type=''",
$post_id
)
);
}
/**
* Retrieve an array of image posts sorted by ID.
*
* More precisely, returns the smallest $num_posts image posts
* (measured by ID) which are larger than $from_id.
*
* @access public
* @since 4.8.0
*
* @param int $from_id Greatest lower bound of retrieved image post IDs.
* @param int $num_posts Largest number of image posts to retrieve.
*
* @return array The posts.
*/
public function query_images_after_id( $from_id, $num_posts ) {
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT *
FROM $wpdb->posts
WHERE post_type='attachment'
AND post_mime_type LIKE %s
AND ID>%d
ORDER BY ID ASC
LIMIT %d;",
'image/%',
$from_id,
$num_posts
)
); // WPCS: db call ok; no-cache ok.
}
/**
* Retrieve an array of video posts sorted by ID.
*
* More precisely, returns the smallest $num_posts video posts
* (measured by ID) which are larger than $from_id.
*
* @access public
* @since 4.8.0
*
* @param int $from_id Greatest lower bound of retrieved video post IDs.
* @param int $num_posts Largest number of video posts to retrieve.
*
* @return array The posts.
*/
public function query_videos_after_id( $from_id, $num_posts ) {
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT *
FROM $wpdb->posts
WHERE post_type='attachment'
AND post_mime_type LIKE %s
AND ID>%d
ORDER BY ID ASC
LIMIT %d;",
'video/%',
$from_id,
$num_posts
)
); // WPCS: db call ok; no-cache ok.
}
/**
* Retrieve an array of published posts from the last 2 days.
*
* @access public
* @since 4.8.0
*
* @param int $num_posts Largest number of posts to retrieve.
*
* @return array The posts.
*/
public function query_most_recent_posts( $num_posts ) {
global $wpdb;
$two_days_ago = date( 'Y-m-d', strtotime( '-2 days' ) );
/**
* Filter post types to be included in news sitemap.
*
* @module sitemaps
*
* @since 3.9.0
*
* @param array $post_types Array with post types to include in news sitemap.
*/
$post_types = apply_filters(
'jetpack_sitemap_news_sitemap_post_types',
array( 'page', 'post' )
);
foreach ( (array) $post_types as $i => $post_type ) {
$post_types[ $i ] = $wpdb->prepare( '%s', $post_type );
}
$post_types_list = join( ',', $post_types );
return $wpdb->get_results(
$wpdb->prepare(
"SELECT *
FROM $wpdb->posts
WHERE post_status='publish'
AND post_date >= '%s'
AND post_type IN ($post_types_list)
ORDER BY post_date DESC
LIMIT %d;",
$two_days_ago,
$num_posts
)
); // WPCS: db call ok; no-cache ok.
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* A message logger for the Jetpack Sitemap module.
*
* @package Jetpack
* @since 4.8.0
*/
/**
* Handles logging errors and debug messages for sitemap generator.
*
* A Jetpack_Sitemap_Logger object keeps track of its birth time as well
* as a "unique" ID string. Calling the report() method writes a message
* to the PHP error log as well as the ID string for easier grepping.
*
* @since 4.8.0
*/
class Jetpack_Sitemap_Logger {
/**
* A unique-ish string for each logger, enabling us to grep
* for the messages written by an individual generation phase.
*
* @access private
* @since 4.8.0
* @var string $key The key string.
*/
private $key;
/**
* The birth time of this object in microseconds.
*
* @access private
* @since 4.8.0
* @var int $starttime The birth time.
*/
private $starttime;
/**
* Initializes a new logger object.
*
* @access public
* @since 4.8.0
*
* @param string $message An optional message string to be written to the debug log on initialization.
*/
public function __construct( $message = null ) {
$this->key = wp_generate_password( 5, false );
$this->starttime = microtime( true );
if ( ! is_null( $message ) ) {
$this->report( $message );
}
}
/**
* Writes a string to the debug log, including the logger's ID string.
*
* @access public
* @since 4.8.0
*
* @param string $message The string to be written to the log.
* @param boolean $is_error If true, $message will be logged even if JETPACK_DEV_DEBUG is not enabled.
*/
public function report( $message, $is_error = false ) {
$message = 'jp-sitemap-' . $this->key . ': ' . $message;
if ( ! ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
return;
}
if ( ! $is_error && ! ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) ) {
return;
}
error_log( $message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
/**
* Writes the elapsed lifetime of the logger to the debug log, with an optional message.
*
* @access public
* @since 4.8.0
*
* @param string $message The optional message string. Default is the empty string.
*/
public function time( $message = '' ) {
$time = round( microtime( true ) - $this->starttime, 3 );
$this->report( $message . ' ' . $time . ' seconds elapsed.' );
}
}

View File

@@ -0,0 +1,144 @@
<?php
/**
* Abstract sitemap generation state class.
*
* @package Jetpack
* @since 4.8.0
* @author Automattic
*/
/* Include standard constants and librarian. */
require_once dirname( __FILE__ ) . '/sitemap-constants.php';
require_once dirname( __FILE__ ) . '/sitemap-librarian.php';
if ( defined( 'WP_DEBUG' ) && ( true === WP_DEBUG ) ) {
require_once dirname( __FILE__ ) . '/sitemap-logger.php';
}
/**
* This class provides an interface for storing and retrieving
* the state of a sitemap generation phase. Whenever the builder
* wants to build a new sitemap page, it uses this class to see
* what the current state of the sitemap is. The lock is stored
* as a transient with max lifetime of 15 minutes; this way if our
* builder times out before unlocking the state, the lock will expire
* before the builder tries again.
*
* @since 4.8.0
*/
class Jetpack_Sitemap_State {
/**
* Initial state for the sitemap generator.
*
* @access public
* @since 4.8.0
*
* @param string $type The initial sitemap type.
*
* @return array $args {
* @type string sitemap-type The type of sitemap to be generated.
* @type int last-added The largest index to be added to a generated sitemap page.
* @type int number The index of the last sitemap to be generated.
* @type string last-modified The latest timestamp seen.
* @type array max The latest index of each sitemap type seen.
* }
*/
private static function initial( $type = JP_PAGE_SITEMAP_TYPE ) {
return array(
'sitemap-type' => $type,
'last-added' => 0,
'number' => 0,
'last-modified' => '1970-01-01 00:00:00',
'max' => array(),
);
}
/**
* Reset the sitemap state.
*
* @param string $type The initial sitemap type.
*
* @access public
* @since 4.8.0
*/
public static function reset( $type ) {
delete_transient( 'jetpack-sitemap-state-lock' );
update_option(
'jetpack-sitemap-state',
self::initial( $type )
);
}
/**
* Store a sitemap state, and unlock it.
*
* @access public
* @since 4.8.0
*
* @param array $state Array of the Sitemap state details.
* @type string sitemap-type The type of sitemap to be generated.
* @type int last-added The largest index to be added to a generated sitemap page.
* @type int number The index of the last sitemap to be generated.
* @type string last-modified The latest timestamp seen.
*/
public static function check_in( $state ) {
// Get the old max value.
$sitemap_old = get_option( 'jetpack-sitemap-state', self::initial() );
$state['max'] = $sitemap_old['max'];
// Update the max value of the current type.
$state['max'][ $state['sitemap-type'] ]['number'] = $state['number'];
$state['max'][ $state['sitemap-type'] ]['lastmod'] = $state['last-modified'];
update_option( 'jetpack-sitemap-state', $state );
}
/**
* Unlock the sitemap state.
*
* @access public
* @since 4.8.0
*/
public static function unlock() {
delete_transient( 'jetpack-sitemap-state-lock' );
}
/**
* Read the stored sitemap state. Returns false if the state is locked.
*
* @access public
* @since 4.8.0
*
* @return bool|array $args {
* @type string sitemap-type The type of sitemap to be generated.
* @type int last-added The largest index to be added to a generated sitemap page.
* @type int number The index of the last sitemap to be generated.
* @type string last-modified The latest timestamp seen.
* @type array max The latest index of each sitemap type seen.
* }
*/
public static function check_out() {
// See if the state is locked.
if ( true === get_transient( 'jetpack-sitemap-state-lock' ) ) {
// If it is, return false.
return false;
} else {
// Otherwise, lock the state for 15 minutes and then return it.
set_transient( 'jetpack-sitemap-state-lock', true, JP_SITEMAP_LOCK_INTERVAL );
return get_option( 'jetpack-sitemap-state', self::initial() );
}
}
/**
* Delete the stored state and lock.
*
* @access public
* @since 4.8.0
*/
public static function delete() {
delete_transient( 'jetpack-sitemap-state-lock' );
delete_option( 'jetpack-sitemap-state' );
}
}

View File

@@ -0,0 +1,783 @@
<?php
/**
* The XSL used to style sitemaps is essentially a bunch of
* static strings. This class handles the construction of
* those strings.
*
* @package Jetpack
* @since 4.8.0
*/
/**
* Builds the XSL files required by Jetpack_Sitemap_Manager.
*
* @since 4.8.0
*/
class Jetpack_Sitemap_Stylist {
/**
* Convert named entities, strip all HTML except anchor tags,
* and interpolate with vsprintf. This is a helper function
* for all the internationalized UI strings in this class
* which have to include URLs.
*
* Note that $url_array should be indexed by integers like so:
*
* array(
* 1 => 'example.com',
* 2 => 'example.org',
* );
*
* Then '%1$s' in the format string will substitute 'example.com'
* and '%2$s' will substitute 'example.org'.
*
* @access private
* @since 4.8.0
* @link http://php.net/manual/en/function.vsprintf.php Format string documentation.
*
* @param string $format A vsprintf-style format string to be sanitized.
* @param array $url_array The string substitution array to be passed to vsprintf.
*
* @return string The sanitized string.
*/
private static function sanitize_with_links( $format, $url_array ) {
return vsprintf(
wp_kses(
ent2ncr( $format ),
array(
'a' => array(
'href' => true,
'title' => true,
),
)
),
$url_array
);
}
/**
* Returns the xsl of a sitemap xml file as a string.
*
* @access public
* @since 4.8.0
*
* @return string The contents of the xsl file.
*/
public static function sitemap_xsl() {
$title = esc_html( ent2ncr( __( 'XML Sitemap', 'jetpack' ) ) );
$header_url = esc_html( ent2ncr( __( 'URL', 'jetpack' ) ) );
$header_lastmod = esc_html( ent2ncr( __( 'Last Modified', 'jetpack' ) ) );
$description = self::sanitize_with_links(
__(
'This is an XML Sitemap generated by <a href="%1$s" rel="noopener noreferrer" target="_blank">Jetpack</a>, meant to be consumed by search engines like <a href="%2$s" rel="noopener noreferrer" target="_blank">Google</a> or <a href="%3$s" rel="noopener noreferrer" target="_blank">Bing</a>.',
'jetpack'
),
array(
1 => 'http://jetpack.com/',
2 => 'https://www.google.com/',
3 => 'https://www.bing.com/',
)
);
$more_info = self::sanitize_with_links(
__(
'You can find more information on XML sitemaps at <a href="%1$s" rel="noopener noreferrer" target="_blank">sitemaps.org</a>',
'jetpack'
),
array(
1 => 'http://sitemaps.org',
)
);
$generated_by = self::sanitize_with_links(
__(
'Generated by <a href="%s" rel="noopener noreferrer" target="_blank">Jetpack for WordPress</a>',
'jetpack'
),
array(
1 => 'https://jetpack.com',
)
);
$css = self::sitemap_xsl_css();
return <<<XSL
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='2.0'
xmlns:html='http://www.w3.org/TR/REC-html40'
xmlns:sitemap='http://www.sitemaps.org/schemas/sitemap/0.9'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
<style type='text/css'>
$css
</style>
</head>
<body>
<div id='description'>
<h1>$title</h1>
<p>$description</p>
<p>$more_info</p>
</div>
<div id='content'>
<!-- <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> -->
<table>
<tr>
<th>#</th>
<th>$header_url</th>
<th>$header_lastmod</th>
</tr>
<xsl:for-each select="sitemap:urlset/sitemap:url">
<tr>
<xsl:choose>
<xsl:when test='position() mod 2 != 1'>
<xsl:attribute name="class">odd</xsl:attribute>
</xsl:when>
</xsl:choose>
<td>
<xsl:value-of select = "position()" />
</td>
<td>
<xsl:variable name='itemURL'>
<xsl:value-of select='sitemap:loc'/>
</xsl:variable>
<a href='{\$itemURL}'>
<xsl:value-of select='sitemap:loc'/>
</a>
</td>
<td>
<xsl:value-of select='sitemap:lastmod'/>
</td>
</tr>
</xsl:for-each>
</table>
</div>
<div id='footer'>
<p>$generated_by</p>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>\n
XSL;
}
/**
* Returns the xsl of a sitemap index xml file as a string.
*
* @access public
* @since 4.8.0
*
* @return string The contents of the xsl file.
*/
public static function sitemap_index_xsl() {
$title = esc_html( ent2ncr( __( 'XML Sitemap Index', 'jetpack' ) ) );
$header_url = esc_html( ent2ncr( __( 'Sitemap URL', 'jetpack' ) ) );
$header_lastmod = esc_html( ent2ncr( __( 'Last Modified', 'jetpack' ) ) );
$description = self::sanitize_with_links(
__(
'This is an XML Sitemap Index generated by <a href="%1$s" rel="noopener noreferrer" target="_blank">Jetpack</a>, meant to be consumed by search engines like <a href="%2$s" rel="noopener noreferrer" target="_blank">Google</a> or <a href="%3$s" rel="noopener noreferrer" target="_blank">Bing</a>.',
'jetpack'
),
array(
1 => 'http://jetpack.com/',
2 => 'https://www.google.com/',
3 => 'https://www.bing.com/',
)
);
if ( current_user_can( 'manage_options' ) ) {
$next = human_time_diff( wp_next_scheduled( 'jp_sitemap_cron_hook' ) );
/* translators: %s is a human_time_diff until next sitemap generation. */
$no_nodes_warning = sprintf( __( 'No sitemap found. The system will try to build it again in %s.', 'jetpack' ), $next );
} else {
$no_nodes_warning = '';
}
$more_info = self::sanitize_with_links(
__(
'You can find more information on XML sitemaps at <a href="%1$s" rel="noopener noreferrer" target="_blank">sitemaps.org</a>',
'jetpack'
),
array(
1 => 'http://sitemaps.org',
)
);
$generated_by = self::sanitize_with_links(
__(
'Generated by <a href="%s" rel="noopener noreferrer" target="_blank">Jetpack for WordPress</a>',
'jetpack'
),
array(
1 => 'https://jetpack.com',
)
);
$css = self::sitemap_xsl_css();
return <<<XSL
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='2.0'
xmlns:html='http://www.w3.org/TR/REC-html40'
xmlns:sitemap='http://www.sitemaps.org/schemas/sitemap/0.9'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
<style type='text/css'>
$css
</style>
</head>
<body>
<div id='description'>
<h1>$title</h1>
<xsl:choose>
<xsl:when test='not(sitemap:sitemapindex/sitemap:sitemap)'>
<p><strong>$no_nodes_warning</strong></p>
</xsl:when>
</xsl:choose>
<p>$description</p>
<p>$more_info</p>
</div>
<div id='content'>
<table>
<tr>
<th>#</th>
<th>$header_url</th>
<th>$header_lastmod</th>
</tr>
<xsl:for-each select='sitemap:sitemapindex/sitemap:sitemap'>
<tr>
<xsl:choose>
<xsl:when test='position() mod 2 != 1'>
<xsl:attribute name="class">odd</xsl:attribute>
</xsl:when>
</xsl:choose>
<td>
<xsl:value-of select = "position()" />
</td>
<td>
<xsl:variable name='itemURL'>
<xsl:value-of select='sitemap:loc'/>
</xsl:variable>
<a href='{\$itemURL}'>
<xsl:value-of select='sitemap:loc'/>
</a>
</td>
<td>
<xsl:value-of select='sitemap:lastmod'/>
</td>
</tr>
</xsl:for-each>
</table>
</div>
<div id='footer'>
<p>$generated_by</p>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>\n
XSL;
}
/**
* Returns the xsl of an image sitemap xml file as a string.
*
* @access public
* @since 4.8.0
*
* @return string The contents of the xsl file.
*/
public static function image_sitemap_xsl() {
$title = esc_html( ent2ncr( __( 'XML Image Sitemap', 'jetpack' ) ) );
$header_url = esc_html( ent2ncr( __( 'Page URL', 'jetpack' ) ) );
$header_image_url = esc_html( ent2ncr( __( 'Image URL', 'jetpack' ) ) );
$header_thumbnail = esc_html( ent2ncr( __( 'Thumbnail', 'jetpack' ) ) );
$header_title = esc_html( ent2ncr( __( 'Title', 'jetpack' ) ) );
$header_lastmod = esc_html( ent2ncr( __( 'Last Modified', 'jetpack' ) ) );
$header_caption = esc_html( ent2ncr( __( 'Caption', 'jetpack' ) ) );
$description = self::sanitize_with_links(
__(
'This is an XML Image Sitemap generated by <a href="%1$s" rel="noopener noreferrer" target="_blank">Jetpack</a>, meant to be consumed by search engines like <a href="%2$s" rel="noopener noreferrer" target="_blank">Google</a> or <a href="%3$s" rel="noopener noreferrer" target="_blank">Bing</a>.',
'jetpack'
),
array(
1 => 'http://jetpack.com/',
2 => 'https://www.google.com/',
3 => 'https://www.bing.com/',
)
);
$more_info = self::sanitize_with_links(
__(
'You can find more information on XML sitemaps at <a href="%1$s" rel="noopener noreferrer" target="_blank">sitemaps.org</a>',
'jetpack'
),
array(
1 => 'http://sitemaps.org',
)
);
$generated_by = self::sanitize_with_links(
__(
'Generated by <a href="%s" rel="noopener noreferrer" target="_blank">Jetpack for WordPress</a>',
'jetpack'
),
array(
1 => 'https://jetpack.com',
)
);
$css = self::sitemap_xsl_css();
return <<<XSL
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='2.0'
xmlns:html='http://www.w3.org/TR/REC-html40'
xmlns:sitemap='http://www.sitemaps.org/schemas/sitemap/0.9'
xmlns:image='http://www.google.com/schemas/sitemap-image/1.1'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
<style type='text/css'>
$css
</style>
</head>
<body>
<div id='description'>
<h1>$title</h1>
<p>$description</p>
<p>$more_info</p>
</div>
<div id='content'>
<!-- <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> -->
<table>
<tr>
<th>#</th>
<th>$header_url</th>
<th>$header_image_url</th>
<th>$header_title</th>
<th>$header_caption</th>
<th>$header_lastmod</th>
<th>$header_thumbnail</th>
</tr>
<xsl:for-each select="sitemap:urlset/sitemap:url">
<tr>
<xsl:choose>
<xsl:when test='position() mod 2 != 1'>
<xsl:attribute name="class">odd</xsl:attribute>
</xsl:when>
</xsl:choose>
<td>
<xsl:value-of select = "position()" />
</td>
<td>
<xsl:variable name='pageURL'>
<xsl:value-of select='sitemap:loc'/>
</xsl:variable>
<a href='{\$pageURL}'>
<xsl:value-of select='sitemap:loc'/>
</a>
</td>
<xsl:variable name='itemURL'>
<xsl:value-of select='image:image/image:loc'/>
</xsl:variable>
<td>
<a href='{\$itemURL}'>
<xsl:value-of select='image:image/image:loc'/>
</a>
</td>
<td>
<xsl:value-of select='image:image/image:title'/>
</td>
<td>
<xsl:value-of select='image:image/image:caption'/>
</td>
<td>
<xsl:value-of select='sitemap:lastmod'/>
</td>
<td>
<a href='{\$itemURL}'>
<img class='thumbnail' src='{\$itemURL}'/>
</a>
</td>
</tr>
</xsl:for-each>
</table>
</div>
<div id='footer'>
<p>$generated_by</p>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>\n
XSL;
}
/**
* Returns the xsl of a video sitemap xml file as a string.
*
* @access public
* @since 4.8.0
*
* @return string The contents of the xsl file.
*/
public static function video_sitemap_xsl() {
$title = esc_html( ent2ncr( __( 'XML Video Sitemap', 'jetpack' ) ) );
$header_url = esc_html( ent2ncr( __( 'Page URL', 'jetpack' ) ) );
$header_image_url = esc_html( ent2ncr( __( 'Video URL', 'jetpack' ) ) );
$header_thumbnail = esc_html( ent2ncr( __( 'Thumbnail', 'jetpack' ) ) );
$header_title = esc_html( ent2ncr( __( 'Title', 'jetpack' ) ) );
$header_lastmod = esc_html( ent2ncr( __( 'Last Modified', 'jetpack' ) ) );
$header_description = esc_html( ent2ncr( __( 'Description', 'jetpack' ) ) );
$description = self::sanitize_with_links(
__(
'This is an XML Video Sitemap generated by <a href="%1$s" rel="noopener noreferrer" target="_blank">Jetpack</a>, meant to be consumed by search engines like <a href="%2$s" rel="noopener noreferrer" target="_blank">Google</a> or <a href="%3$s" rel="noopener noreferrer" target="_blank">Bing</a>.',
'jetpack'
),
array(
1 => 'http://jetpack.com/',
2 => 'https://www.google.com/',
3 => 'https://www.bing.com/',
)
);
$more_info = self::sanitize_with_links(
__(
'You can find more information on XML sitemaps at <a href="%1$s" rel="noopener noreferrer" target="_blank">sitemaps.org</a>',
'jetpack'
),
array(
1 => 'http://sitemaps.org',
)
);
$generated_by = self::sanitize_with_links(
__(
'Generated by <a href="%s" rel="noopener noreferrer" target="_blank">Jetpack for WordPress</a>',
'jetpack'
),
array(
1 => 'https://jetpack.com',
)
);
$css = self::sitemap_xsl_css();
return <<<XSL
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='2.0'
xmlns:html='http://www.w3.org/TR/REC-html40'
xmlns:sitemap='http://www.sitemaps.org/schemas/sitemap/0.9'
xmlns:video='http://www.google.com/schemas/sitemap-video/1.1'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
<style type='text/css'>
$css
</style>
</head>
<body>
<div id='description'>
<h1>$title</h1>
<p>$description</p>
<p>$more_info</p>
</div>
<div id='content'>
<!-- <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> -->
<table>
<tr>
<th>#</th>
<th>$header_url</th>
<th>$header_image_url</th>
<th>$header_title</th>
<th>$header_description</th>
<th>$header_lastmod</th>
<th>$header_thumbnail</th>
</tr>
<xsl:for-each select="sitemap:urlset/sitemap:url">
<tr>
<xsl:choose>
<xsl:when test='position() mod 2 != 1'>
<xsl:attribute name="class">odd</xsl:attribute>
</xsl:when>
</xsl:choose>
<td>
<xsl:value-of select = "position()" />
</td>
<td>
<xsl:variable name='pageURL'>
<xsl:value-of select='sitemap:loc'/>
</xsl:variable>
<a href='{\$pageURL}'>
<xsl:value-of select='sitemap:loc'/>
</a>
</td>
<xsl:variable name='itemURL'>
<xsl:value-of select='video:video/video:content_loc'/>
</xsl:variable>
<td>
<a href='{\$itemURL}'>
<xsl:value-of select='video:video/video:content_loc'/>
</a>
</td>
<td>
<xsl:value-of select='video:video/video:title'/>
</td>
<td>
<xsl:value-of select='video:video/video:description'/>
</td>
<td>
<xsl:value-of select='sitemap:lastmod'/>
</td>
<td>
<xsl:variable name='thumbURL'>
<xsl:value-of select='video:video/video:thumbnail_loc'/>
</xsl:variable>
<a href='{\$thumbURL}'>
<img class='thumbnail' src='{\$thumbURL}'/>
</a>
</td>
</tr>
</xsl:for-each>
</table>
</div>
<div id='footer'>
<p>$generated_by</p>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>\n
XSL;
}
/**
* Returns the xsl of a news sitemap xml file as a string.
*
* @access public
* @since 4.8.0
*
* @return string The contents of the xsl file.
*/
public static function news_sitemap_xsl() {
$title = esc_html( ent2ncr( __( 'XML News Sitemap', 'jetpack' ) ) );
$header_url = esc_html( ent2ncr( __( 'Page URL', 'jetpack' ) ) );
$header_title = esc_html( ent2ncr( __( 'Title', 'jetpack' ) ) );
$header_pubdate = esc_html( ent2ncr( __( 'Publication Date', 'jetpack' ) ) );
$description = self::sanitize_with_links(
__(
'This is an XML News Sitemap generated by <a href="%1$s" rel="noopener noreferrer" target="_blank">Jetpack</a>, meant to be consumed by search engines like <a href="%2$s" rel="noopener noreferrer" target="_blank">Google</a> or <a href="%3$s" rel="noopener noreferrer" target="_blank">Bing</a>.',
'jetpack'
),
array(
1 => 'http://jetpack.com/',
2 => 'https://www.google.com/',
3 => 'https://www.bing.com/',
)
);
$more_info = self::sanitize_with_links(
__(
'You can find more information on XML sitemaps at <a href="%1$s" rel="noopener noreferrer" target="_blank">sitemaps.org</a>',
'jetpack'
),
array(
1 => 'http://sitemaps.org',
)
);
$generated_by = self::sanitize_with_links(
__(
'Generated by <a href="%s" rel="noopener noreferrer" target="_blank">Jetpack for WordPress</a>',
'jetpack'
),
array(
1 => 'https://jetpack.com',
)
);
$css = self::sitemap_xsl_css();
return <<<XSL
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='2.0'
xmlns:html='http://www.w3.org/TR/REC-html40'
xmlns:sitemap='http://www.sitemaps.org/schemas/sitemap/0.9'
xmlns:news='http://www.google.com/schemas/sitemap-news/0.9'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
<style type='text/css'>
$css
</style>
</head>
<body>
<div id='description'>
<h1>$title</h1>
<p>$description</p>
<p>$more_info</p>
</div>
<div id='content'>
<!-- <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> -->
<table>
<tr>
<th>#</th>
<th>$header_url</th>
<th>$header_title</th>
<th>$header_pubdate</th>
</tr>
<xsl:for-each select="sitemap:urlset/sitemap:url">
<tr>
<xsl:choose>
<xsl:when test='position() mod 2 != 1'>
<xsl:attribute name="class">odd</xsl:attribute>
</xsl:when>
</xsl:choose>
<td>
<xsl:value-of select = "position()" />
</td>
<xsl:variable name='pageURL'>
<xsl:value-of select='sitemap:loc'/>
</xsl:variable>
<td>
<a href='{\$pageURL}'>
<xsl:value-of select='sitemap:loc'/>
</a>
</td>
<td>
<a href='{\$pageURL}'>
<xsl:value-of select='news:news/news:title'/>
</a>
</td>
<td>
<xsl:value-of select='news:news/news:publication_date'/>
</td>
</tr>
</xsl:for-each>
</table>
</div>
<div id='footer'>
<p>$generated_by</p>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>\n
XSL;
}
/**
* The CSS to be included in sitemap xsl stylesheets;
* factored out for uniformity.
*
* @access public
* @since 4.8.0
*
* @return string The CSS.
*/
public static function sitemap_xsl_css() {
return <<<CSS
body {
font: 14px 'Open Sans', Helvetica, Arial, sans-serif;
margin: 0;
}
a {
color: #3498db;
text-decoration: none;
}
h1 {
margin: 0;
}
#description {
background-color: #81a844;
color: #FFF;
padding: 30px 30px 20px;
}
#description a {
color: #fff;
}
#content {
padding: 10px 30px 30px;
background: #fff;
}
a:hover {
border-bottom: 1px solid;
}
th, td {
font-size: 12px;
}
th {
text-align: left;
border-bottom: 1px solid #ccc;
}
th, td {
padding: 10px 15px;
}
.odd {
background-color: #E7F1D4;
}
#footer {
margin: 20px 30px;
font-size: 12px;
color: #999;
}
#footer a {
color: inherit;
}
#description a, #footer a {
border-bottom: 1px solid;
}
#description a:hover, #footer a:hover {
border-bottom: none;
}
img.thumbnail {
max-height: 100px;
max-width: 100px;
}
CSS;
}
}

View File

@@ -0,0 +1,600 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Generate sitemap files in base XML as well as some namespace extensions.
*
* This module generates two different base sitemaps.
*
* 1. sitemap.xml
* The basic sitemap is updated regularly by wp-cron. It is stored in the
* database and retrieved when requested. This sitemap aims to include canonical
* URLs for all published content and abide by the sitemap spec. This is the root
* of a tree of sitemap and sitemap index xml files, depending on the number of URLs.
*
* By default the sitemap contains published posts of type 'post' and 'page', as
* well as the home url. To include other post types use the 'jetpack_sitemap_post_types'
* filter.
*
* @link http://sitemaps.org/protocol.php Base sitemaps protocol.
* @link https://support.google.com/webmasters/answer/178636 Image sitemap extension.
* @link https://developers.google.com/webmasters/videosearch/sitemaps Video sitemap extension.
*
* 2. news-sitemap.xml
* The news sitemap is generated on the fly when requested. It does not aim for
* completeness, instead including at most 1000 of the most recent published posts
* from the previous 2 days, per the news-sitemap spec.
*
* @link http://www.google.com/support/webmasters/bin/answer.py?answer=74288 News sitemap extension.
*
* @package Jetpack
* @since 3.9.0
* @since 4.8.0 Remove 1000 post limit.
* @author Automattic
*/
/* Include all of the sitemap subclasses. */
require_once dirname( __FILE__ ) . '/sitemap-constants.php';
require_once dirname( __FILE__ ) . '/sitemap-buffer.php';
require_once dirname( __FILE__ ) . '/sitemap-stylist.php';
require_once dirname( __FILE__ ) . '/sitemap-librarian.php';
require_once dirname( __FILE__ ) . '/sitemap-finder.php';
require_once dirname( __FILE__ ) . '/sitemap-builder.php';
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
require_once dirname( __FILE__ ) . '/sitemap-logger.php';
}
/**
* Governs the generation, storage, and serving of sitemaps.
*
* @since 4.8.0
*/
class Jetpack_Sitemap_Manager {
/**
* Librarian object for storing and retrieving sitemap data.
*
* @see Jetpack_Sitemap_Librarian
* @since 4.8.0
* @var Jetpack_Sitemap_Librarian $librarian Librarian object for storing and retrieving sitemap data.
*/
private $librarian;
/**
* Logger object for reporting debug messages.
*
* @see Jetpack_Sitemap_Logger
* @since 4.8.0
* @var Jetpack_Sitemap_Logger $logger Logger object for reporting debug messages.
*/
private $logger;
/**
* Finder object for handling sitemap URIs.
*
* @see Jetpack_Sitemap_Finder
* @since 4.8.0
* @var Jetpack_Sitemap_Finder $finder Finder object for handling with sitemap URIs.
*/
private $finder;
/**
* Construct a new Jetpack_Sitemap_Manager.
*
* @access public
* @since 4.8.0
*/
public function __construct() {
$this->librarian = new Jetpack_Sitemap_Librarian();
$this->finder = new Jetpack_Sitemap_Finder();
if ( defined( 'WP_DEBUG' ) && ( true === WP_DEBUG ) ) {
$this->logger = new Jetpack_Sitemap_Logger();
}
// Add callback for sitemap URL handler.
add_action(
'init',
array( $this, 'callback_action_catch_sitemap_urls' ),
defined( 'IS_WPCOM' ) && IS_WPCOM ? 100 : 10
);
// Add generator to wp_cron task list.
$this->schedule_sitemap_generation();
// Add sitemap to robots.txt.
add_action(
'do_robotstxt',
array( $this, 'callback_action_do_robotstxt' ),
20
);
// The news sitemap is cached; here we add a callback to
// flush the cached news sitemap when a post is published.
add_action(
'publish_post',
array( $this, 'callback_action_flush_news_sitemap_cache' ),
10
);
// In case we need to purge all sitemaps, we do this.
add_action(
'jetpack_sitemaps_purge_data',
array( $this, 'callback_action_purge_data' )
);
/*
* Module parameters are stored as options in the database.
* This allows us to avoid having to process all of init
* before serving the sitemap data. The following actions
* process and store these filters.
*/
// Process filters and store location string for sitemap.
add_action(
'init',
array( $this, 'callback_action_filter_sitemap_location' ),
999
);
}
/**
* Echo a raw string of given content-type.
*
* @access private
* @since 4.8.0
*
* @param string $the_content_type The content type to be served.
* @param string $the_content The string to be echoed.
*/
private function serve_raw_and_die( $the_content_type, $the_content ) {
header( 'Content-Type: ' . $the_content_type . '; charset=UTF-8' );
global $wp_query;
$wp_query->is_feed = true;
set_query_var( 'feed', 'sitemap' );
if ( '' === $the_content ) {
$error = __( 'No sitemap found. Please try again later.', 'jetpack' );
if ( current_user_can( 'manage_options' ) ) {
$next = human_time_diff( wp_next_scheduled( 'jp_sitemap_cron_hook' ) );
/* translators: %s is a human_time_diff until next sitemap generation. */
$error = sprintf( __( 'No sitemap found. The system will try to build it again in %s.', 'jetpack' ), $next );
}
wp_die(
esc_html( $error ),
esc_html__( 'Sitemaps', 'jetpack' ),
array(
'response' => 404,
)
);
}
echo $the_content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All content created by Jetpack.
die();
}
/**
* Callback to intercept sitemap url requests and serve sitemap files.
*
* @access public
* @since 4.8.0
*/
public function callback_action_catch_sitemap_urls() {
// Regular expressions for sitemap URL routing.
$regex = array(
'master' => '/^sitemap\.xml$/',
'sitemap' => '/^sitemap-[1-9][0-9]*\.xml$/',
'index' => '/^sitemap-index-[1-9][0-9]*\.xml$/',
'sitemap-style' => '/^sitemap\.xsl$/',
'index-style' => '/^sitemap-index\.xsl$/',
'image' => '/^image-sitemap-[1-9][0-9]*\.xml$/',
'image-index' => '/^image-sitemap-index-[1-9][0-9]*\.xml$/',
'image-style' => '/^image-sitemap\.xsl$/',
'video' => '/^video-sitemap-[1-9][0-9]*\.xml$/',
'video-index' => '/^video-sitemap-index-[1-9][0-9]*\.xml$/',
'video-style' => '/^video-sitemap\.xsl$/',
'news' => '/^news-sitemap\.xml$/',
'news-style' => '/^news-sitemap\.xsl$/',
);
// The raw path(+query) of the requested URI.
if ( isset( $_SERVER['REQUEST_URI'] ) ) { // WPCS: Input var okay.
$raw_uri = sanitize_text_field(
wp_unslash( $_SERVER['REQUEST_URI'] ) // WPCS: Input var okay.
);
} else {
$raw_uri = '';
}
$request = $this->finder->recognize_sitemap_uri( $raw_uri );
if ( isset( $request['sitemap_name'] ) ) {
/**
* Filter the content type used to serve the sitemap XML files.
*
* @module sitemaps
*
* @since 3.9.0
*
* @param string $xml_content_type By default, it's 'text/xml'.
*/
$xml_content_type = apply_filters( 'jetpack_sitemap_content_type', 'text/xml' );
// Catch master sitemap xml.
if ( preg_match( $regex['master'], $request['sitemap_name'] ) ) {
$sitemap_content = $this->librarian->get_sitemap_text(
jp_sitemap_filename( JP_MASTER_SITEMAP_TYPE, 0 ),
JP_MASTER_SITEMAP_TYPE
);
// if there is no master sitemap yet, let's just return an empty sitemap with a short TTL instead of a 404.
if ( empty( $sitemap_content ) ) {
$builder = new Jetpack_Sitemap_Builder();
$sitemap_content = $builder->empty_sitemap_xml();
}
$this->serve_raw_and_die(
$xml_content_type,
$sitemap_content
);
}
// Catch sitemap xml.
if ( preg_match( $regex['sitemap'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
$xml_content_type,
$this->librarian->get_sitemap_text(
$request['sitemap_name'],
JP_PAGE_SITEMAP_TYPE
)
);
}
// Catch sitemap index xml.
if ( preg_match( $regex['index'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
$xml_content_type,
$this->librarian->get_sitemap_text(
$request['sitemap_name'],
JP_PAGE_SITEMAP_INDEX_TYPE
)
);
}
// Catch sitemap xsl.
if ( preg_match( $regex['sitemap-style'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
'application/xml',
Jetpack_Sitemap_Stylist::sitemap_xsl()
);
}
// Catch sitemap index xsl.
if ( preg_match( $regex['index-style'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
'application/xml',
Jetpack_Sitemap_Stylist::sitemap_index_xsl()
);
}
// Catch image sitemap xml.
if ( preg_match( $regex['image'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
$xml_content_type,
$this->librarian->get_sitemap_text(
$request['sitemap_name'],
JP_IMAGE_SITEMAP_TYPE
)
);
}
// Catch image sitemap index xml.
if ( preg_match( $regex['image-index'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
$xml_content_type,
$this->librarian->get_sitemap_text(
$request['sitemap_name'],
JP_IMAGE_SITEMAP_INDEX_TYPE
)
);
}
// Catch image sitemap xsl.
if ( preg_match( $regex['image-style'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
'application/xml',
Jetpack_Sitemap_Stylist::image_sitemap_xsl()
);
}
// Catch video sitemap xml.
if ( preg_match( $regex['video'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
$xml_content_type,
$this->librarian->get_sitemap_text(
$request['sitemap_name'],
JP_VIDEO_SITEMAP_TYPE
)
);
}
// Catch video sitemap index xml.
if ( preg_match( $regex['video-index'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
$xml_content_type,
$this->librarian->get_sitemap_text(
$request['sitemap_name'],
JP_VIDEO_SITEMAP_INDEX_TYPE
)
);
}
// Catch video sitemap xsl.
if ( preg_match( $regex['video-style'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
'application/xml',
Jetpack_Sitemap_Stylist::video_sitemap_xsl()
);
}
// Catch news sitemap xml.
if ( preg_match( $regex['news'], $request['sitemap_name'] ) ) {
$sitemap_builder = new Jetpack_Sitemap_Builder();
$this->serve_raw_and_die(
$xml_content_type,
$sitemap_builder->news_sitemap_xml()
);
}
// Catch news sitemap xsl.
if ( preg_match( $regex['news-style'], $request['sitemap_name'] ) ) {
$this->serve_raw_and_die(
'application/xml',
Jetpack_Sitemap_Stylist::news_sitemap_xsl()
);
}
}
}
/**
* Callback for adding sitemap-interval to the list of schedules.
*
* @access public
* @since 4.8.0
*
* @param array $schedules The array of WP_Cron schedules.
*
* @return array The updated array of WP_Cron schedules.
*/
public function callback_add_sitemap_schedule( $schedules ) {
$schedules['sitemap-interval'] = array(
'interval' => JP_SITEMAP_INTERVAL,
'display' => __( 'Sitemap Interval', 'jetpack' ),
);
return $schedules;
}
/**
* Callback handler for sitemap cron hook
*
* @access public
*/
public function callback_sitemap_cron_hook() {
$sitemap_builder = new Jetpack_Sitemap_Builder();
$sitemap_builder->update_sitemap();
}
/**
* Add actions to schedule sitemap generation.
* Should only be called once, in the constructor.
*
* @access private
* @since 4.8.0
*/
private function schedule_sitemap_generation() {
// Add cron schedule.
add_filter( 'cron_schedules', array( $this, 'callback_add_sitemap_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
add_action(
'jp_sitemap_cron_hook',
array( $this, 'callback_sitemap_cron_hook' )
);
if ( ! wp_next_scheduled( 'jp_sitemap_cron_hook' ) ) {
/**
* Filter the delay in seconds until sitemap generation cron job is started.
*
* This filter allows a site operator or hosting provider to potentialy spread out sitemap generation for a
* lot of sites over time. By default, it will be randomly done over 15 minutes.
*
* @module sitemaps
* @since 6.6.1
*
* @param int $delay Time to delay in seconds.
*/
$delay = apply_filters( 'jetpack_sitemap_generation_delay', MINUTE_IN_SECONDS * wp_rand( 1, 15 ) ); // Randomly space it out to start within next fifteen minutes.
wp_schedule_event(
time() + $delay,
'sitemap-interval',
'jp_sitemap_cron_hook'
);
}
}
/**
* Callback to add sitemap to robots.txt.
*
* @access public
* @since 4.8.0
*/
public function callback_action_do_robotstxt() {
/**
* Filter whether to make the default sitemap discoverable to robots or not. Default true.
*
* @module sitemaps
* @since 3.9.0
* @deprecated 7.4.0
*
* @param bool $discover_sitemap Make default sitemap discoverable to robots.
*/
$discover_sitemap = apply_filters_deprecated( 'jetpack_sitemap_generate', array( true ), 'jetpack-7.4.0', 'jetpack_sitemap_include_in_robotstxt' );
/**
* Filter whether to make the default sitemap discoverable to robots or not. Default true.
*
* @module sitemaps
* @since 7.4.0
*
* @param bool $discover_sitemap Make default sitemap discoverable to robots.
*/
$discover_sitemap = apply_filters( 'jetpack_sitemap_include_in_robotstxt', $discover_sitemap );
if ( true === $discover_sitemap ) {
$sitemap_url = $this->finder->construct_sitemap_url( 'sitemap.xml' );
echo 'Sitemap: ' . esc_url( $sitemap_url ) . "\n";
}
/**
* Filter whether to make the news sitemap discoverable to robots or not. Default true.
*
* @module sitemaps
* @since 3.9.0
* @deprecated 7.4.0
*
* @param bool $discover_news_sitemap Make default news sitemap discoverable to robots.
*/
$discover_news_sitemap = apply_filters_deprecated( 'jetpack_news_sitemap_generate', array( true ), 'jetpack-7.4.0', 'jetpack_news_sitemap_include_in_robotstxt' );
/**
* Filter whether to make the news sitemap discoverable to robots or not. Default true.
*
* @module sitemaps
* @since 7.4.0
*
* @param bool $discover_news_sitemap Make default news sitemap discoverable to robots.
*/
$discover_news_sitemap = apply_filters( 'jetpack_news_sitemap_include_in_robotstxt', $discover_news_sitemap );
if ( true === $discover_news_sitemap ) {
$news_sitemap_url = $this->finder->construct_sitemap_url( 'news-sitemap.xml' );
echo 'Sitemap: ' . esc_url( $news_sitemap_url ) . "\n";
}
}
/**
* Callback to delete the news sitemap cache.
*
* @access public
* @since 4.8.0
*/
public function callback_action_flush_news_sitemap_cache() {
delete_transient( 'jetpack_news_sitemap_xml' );
}
/**
* Callback for resetting stored sitemap data.
*
* @access public
* @since 5.3.0
* @since 6.7.0 Schedules a regeneration.
*/
public function callback_action_purge_data() {
$this->callback_action_flush_news_sitemap_cache();
$this->librarian->delete_all_stored_sitemap_data();
/** This filter is documented in modules/sitemaps/sitemaps.php */
$delay = apply_filters( 'jetpack_sitemap_generation_delay', MINUTE_IN_SECONDS * wp_rand( 1, 15 ) ); // Randomly space it out to start within next fifteen minutes.
wp_schedule_single_event( time() + $delay, 'jp_sitemap_cron_hook' );
}
/**
* Callback to set the sitemap location.
*
* @access public
* @since 4.8.0
*/
public function callback_action_filter_sitemap_location() {
update_option(
'jetpack_sitemap_location',
/**
* Additional path for sitemap URIs. Default value is empty.
*
* This string is any additional path fragment you want included between
* the home URL and the sitemap filenames. Exactly how this fragment is
* interpreted depends on your permalink settings. For example:
*
* Pretty permalinks:
* home_url() . jetpack_sitemap_location . '/sitemap.xml'
*
* Plain ("ugly") permalinks:
* home_url() . jetpack_sitemap_location . '/?jetpack-sitemap=sitemap.xml'
*
* PATHINFO permalinks:
* home_url() . '/index.php' . jetpack_sitemap_location . '/sitemap.xml'
*
* where 'sitemap.xml' is the name of a specific sitemap file.
* The value of this filter must be a valid path fragment per RFC 3986;
* in particular it must either be empty or begin with a '/'.
* Also take care that any restrictions on sitemap location imposed by
* the sitemap protocol are satisfied.
*
* The result of this filter is stored in an option, 'jetpack_sitemap_location';
* that option is what gets read when the sitemap location is needed.
* This way we don't have to wait for init to finish before building sitemaps.
*
* @link https://tools.ietf.org/html/rfc3986#section-3.3 RFC 3986
* @link http://www.sitemaps.org/ The sitemap protocol
*
* @since 4.8.0
*/
apply_filters(
'jetpack_sitemap_location',
''
)
);
}
} // End Jetpack_Sitemap_Manager class.
new Jetpack_Sitemap_Manager();
/**
* Absolute URL of the current blog's sitemap.
*
* @module sitemaps
*
* @since 3.9.0
* @since 4.8.1 Code uses method found in Jetpack_Sitemap_Finder::construct_sitemap_url in 4.8.0.
* It has been moved here to avoid fatal errors with other plugins that were expecting to find this function.
*
* @param string $filename Sitemap file name. Defaults to 'sitemap.xml', the initial sitemaps page.
*
* @return string Sitemap URL.
*/
function jetpack_sitemap_uri( $filename = 'sitemap.xml' ) {
global $wp_rewrite;
$location = Jetpack_Options::get_option_and_ensure_autoload( 'jetpack_sitemap_location', '' );
if ( $wp_rewrite->using_index_permalinks() ) {
$sitemap_url = home_url( '/index.php' . $location . '/' . $filename );
} elseif ( $wp_rewrite->using_permalinks() ) {
$sitemap_url = home_url( $location . '/' . $filename );
} else {
$sitemap_url = home_url( $location . '/?jetpack-sitemap=' . $filename );
}
/**
* Filter sitemap URL relative to home URL.
*
* @module sitemaps
*
* @since 3.9.0
*
* @param string $sitemap_url Sitemap URL.
*/
return apply_filters( 'jetpack_sitemap_location', $sitemap_url );
}