No Description

class-wp-sitemaps-stylesheet.php 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <?php
  2. /**
  3. * Sitemaps: WP_Sitemaps_Stylesheet class
  4. *
  5. * This class provides the XSL stylesheets to style all sitemaps.
  6. *
  7. * @package WordPress
  8. * @subpackage Sitemaps
  9. * @since 5.5.0
  10. */
  11. /**
  12. * Stylesheet provider class.
  13. *
  14. * @since 5.5.0
  15. */
  16. class WP_Sitemaps_Stylesheet {
  17. /**
  18. * Renders the XSL stylesheet depending on whether it's the sitemap index or not.
  19. *
  20. * @param string $type Stylesheet type. Either 'sitemap' or 'index'.
  21. */
  22. public function render_stylesheet( $type ) {
  23. header( 'Content-type: application/xml; charset=UTF-8' );
  24. if ( 'sitemap' === $type ) {
  25. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All content escaped below.
  26. echo $this->get_sitemap_stylesheet();
  27. }
  28. if ( 'index' === $type ) {
  29. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All content escaped below.
  30. echo $this->get_sitemap_index_stylesheet();
  31. }
  32. exit;
  33. }
  34. /**
  35. * Returns the escaped XSL for all sitemaps, except index.
  36. *
  37. * @since 5.5.0
  38. */
  39. public function get_sitemap_stylesheet() {
  40. $css = $this->get_stylesheet_css();
  41. $title = esc_xml( __( 'XML Sitemap' ) );
  42. $description = esc_xml( __( 'This XML Sitemap is generated by WordPress to make your content more visible for search engines.' ) );
  43. $learn_more = sprintf(
  44. '<a href="%s">%s</a>',
  45. esc_url( __( 'https://www.sitemaps.org/' ) ),
  46. esc_xml( __( 'Learn more about XML sitemaps.' ) )
  47. );
  48. $text = sprintf(
  49. /* translators: %s: Number of URLs. */
  50. esc_xml( __( 'Number of URLs in this XML Sitemap: %s.' ) ),
  51. '<xsl:value-of select="count( sitemap:urlset/sitemap:url )" />'
  52. );
  53. $lang = get_language_attributes( 'html' );
  54. $url = esc_xml( __( 'URL' ) );
  55. $lastmod = esc_xml( __( 'Last Modified' ) );
  56. $changefreq = esc_xml( __( 'Change Frequency' ) );
  57. $priority = esc_xml( __( 'Priority' ) );
  58. $xsl_content = <<<XSL
  59. <?xml version="1.0" encoding="UTF-8"?>
  60. <xsl:stylesheet
  61. version="1.0"
  62. xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  63. xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
  64. exclude-result-prefixes="sitemap"
  65. >
  66. <xsl:output method="html" encoding="UTF-8" indent="yes" />
  67. <!--
  68. Set variables for whether lastmod, changefreq or priority occur for any url in the sitemap.
  69. We do this up front because it can be expensive in a large sitemap.
  70. -->
  71. <xsl:variable name="has-lastmod" select="count( /sitemap:urlset/sitemap:url/sitemap:lastmod )" />
  72. <xsl:variable name="has-changefreq" select="count( /sitemap:urlset/sitemap:url/sitemap:changefreq )" />
  73. <xsl:variable name="has-priority" select="count( /sitemap:urlset/sitemap:url/sitemap:priority )" />
  74. <xsl:template match="/">
  75. <html {$lang}>
  76. <head>
  77. <title>{$title}</title>
  78. <style>
  79. {$css}
  80. </style>
  81. </head>
  82. <body>
  83. <div id="sitemap">
  84. <div id="sitemap__header">
  85. <h1>{$title}</h1>
  86. <p>{$description}</p>
  87. <p>{$learn_more}</p>
  88. </div>
  89. <div id="sitemap__content">
  90. <p class="text">{$text}</p>
  91. <table id="sitemap__table">
  92. <thead>
  93. <tr>
  94. <th class="loc">{$url}</th>
  95. <xsl:if test="\$has-lastmod">
  96. <th class="lastmod">{$lastmod}</th>
  97. </xsl:if>
  98. <xsl:if test="\$has-changefreq">
  99. <th class="changefreq">{$changefreq}</th>
  100. </xsl:if>
  101. <xsl:if test="\$has-priority">
  102. <th class="priority">{$priority}</th>
  103. </xsl:if>
  104. </tr>
  105. </thead>
  106. <tbody>
  107. <xsl:for-each select="sitemap:urlset/sitemap:url">
  108. <tr>
  109. <td class="loc"><a href="{sitemap:loc}"><xsl:value-of select="sitemap:loc" /></a></td>
  110. <xsl:if test="\$has-lastmod">
  111. <td class="lastmod"><xsl:value-of select="sitemap:lastmod" /></td>
  112. </xsl:if>
  113. <xsl:if test="\$has-changefreq">
  114. <td class="changefreq"><xsl:value-of select="sitemap:changefreq" /></td>
  115. </xsl:if>
  116. <xsl:if test="\$has-priority">
  117. <td class="priority"><xsl:value-of select="sitemap:priority" /></td>
  118. </xsl:if>
  119. </tr>
  120. </xsl:for-each>
  121. </tbody>
  122. </table>
  123. </div>
  124. </div>
  125. </body>
  126. </html>
  127. </xsl:template>
  128. </xsl:stylesheet>
  129. XSL;
  130. /**
  131. * Filters the content of the sitemap stylesheet.
  132. *
  133. * @since 5.5.0
  134. *
  135. * @param string $xsl_content Full content for the XML stylesheet.
  136. */
  137. return apply_filters( 'wp_sitemaps_stylesheet_content', $xsl_content );
  138. }
  139. /**
  140. * Returns the escaped XSL for the index sitemaps.
  141. *
  142. * @since 5.5.0
  143. */
  144. public function get_sitemap_index_stylesheet() {
  145. $css = $this->get_stylesheet_css();
  146. $title = esc_xml( __( 'XML Sitemap' ) );
  147. $description = esc_xml( __( 'This XML Sitemap is generated by WordPress to make your content more visible for search engines.' ) );
  148. $learn_more = sprintf(
  149. '<a href="%s">%s</a>',
  150. esc_url( __( 'https://www.sitemaps.org/' ) ),
  151. esc_xml( __( 'Learn more about XML sitemaps.' ) )
  152. );
  153. $text = sprintf(
  154. /* translators: %s: Number of URLs. */
  155. esc_xml( __( 'Number of URLs in this XML Sitemap: %s.' ) ),
  156. '<xsl:value-of select="count( sitemap:sitemapindex/sitemap:sitemap )" />'
  157. );
  158. $lang = get_language_attributes( 'html' );
  159. $url = esc_xml( __( 'URL' ) );
  160. $lastmod = esc_xml( __( 'Last Modified' ) );
  161. $xsl_content = <<<XSL
  162. <?xml version="1.0" encoding="UTF-8"?>
  163. <xsl:stylesheet
  164. version="1.0"
  165. xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  166. xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
  167. exclude-result-prefixes="sitemap"
  168. >
  169. <xsl:output method="html" encoding="UTF-8" indent="yes" />
  170. <!--
  171. Set variables for whether lastmod occurs for any sitemap in the index.
  172. We do this up front because it can be expensive in a large sitemap.
  173. -->
  174. <xsl:variable name="has-lastmod" select="count( /sitemap:sitemapindex/sitemap:sitemap/sitemap:lastmod )" />
  175. <xsl:template match="/">
  176. <html {$lang}>
  177. <head>
  178. <title>{$title}</title>
  179. <style>
  180. {$css}
  181. </style>
  182. </head>
  183. <body>
  184. <div id="sitemap">
  185. <div id="sitemap__header">
  186. <h1>{$title}</h1>
  187. <p>{$description}</p>
  188. <p>{$learn_more}</p>
  189. </div>
  190. <div id="sitemap__content">
  191. <p class="text">{$text}</p>
  192. <table id="sitemap__table">
  193. <thead>
  194. <tr>
  195. <th class="loc">{$url}</th>
  196. <xsl:if test="\$has-lastmod">
  197. <th class="lastmod">{$lastmod}</th>
  198. </xsl:if>
  199. </tr>
  200. </thead>
  201. <tbody>
  202. <xsl:for-each select="sitemap:sitemapindex/sitemap:sitemap">
  203. <tr>
  204. <td class="loc"><a href="{sitemap:loc}"><xsl:value-of select="sitemap:loc" /></a></td>
  205. <xsl:if test="\$has-lastmod">
  206. <td class="lastmod"><xsl:value-of select="sitemap:lastmod" /></td>
  207. </xsl:if>
  208. </tr>
  209. </xsl:for-each>
  210. </tbody>
  211. </table>
  212. </div>
  213. </div>
  214. </body>
  215. </html>
  216. </xsl:template>
  217. </xsl:stylesheet>
  218. XSL;
  219. /**
  220. * Filters the content of the sitemap index stylesheet.
  221. *
  222. * @since 5.5.0
  223. *
  224. * @param string $xsl_content Full content for the XML stylesheet.
  225. */
  226. return apply_filters( 'wp_sitemaps_stylesheet_index_content', $xsl_content );
  227. }
  228. /**
  229. * Gets the CSS to be included in sitemap XSL stylesheets.
  230. *
  231. * @since 5.5.0
  232. *
  233. * @return string The CSS.
  234. */
  235. public function get_stylesheet_css() {
  236. $text_align = is_rtl() ? 'right' : 'left';
  237. $css = <<<EOF
  238. body {
  239. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  240. color: #444;
  241. }
  242. #sitemap {
  243. max-width: 980px;
  244. margin: 0 auto;
  245. }
  246. #sitemap__table {
  247. width: 100%;
  248. border: solid 1px #ccc;
  249. border-collapse: collapse;
  250. }
  251. #sitemap__table tr td.loc {
  252. /*
  253. * URLs should always be LTR.
  254. * See https://core.trac.wordpress.org/ticket/16834
  255. * and https://core.trac.wordpress.org/ticket/49949
  256. */
  257. direction: ltr;
  258. }
  259. #sitemap__table tr th {
  260. text-align: {$text_align};
  261. }
  262. #sitemap__table tr td,
  263. #sitemap__table tr th {
  264. padding: 10px;
  265. }
  266. #sitemap__table tr:nth-child(odd) td {
  267. background-color: #eee;
  268. }
  269. a:hover {
  270. text-decoration: none;
  271. }
  272. EOF;
  273. /**
  274. * Filters the CSS only for the sitemap stylesheet.
  275. *
  276. * @since 5.5.0
  277. *
  278. * @param string $css CSS to be applied to default XSL file.
  279. */
  280. return apply_filters( 'wp_sitemaps_stylesheet_css', $css );
  281. }
  282. }