PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/wpseo-functions.php

https://github.com/elimendonca/WordPress-SEO-by-Yoast
PHP | 737 lines | 488 code | 110 blank | 139 comment | 117 complexity | f106fb880f9f71ab371854c2ad030c46 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /**
  3. * @package Internals
  4. */
  5. if ( !defined( 'WPSEO_VERSION' ) ) {
  6. header( 'HTTP/1.0 403 Forbidden' );
  7. die;
  8. }
  9. /**
  10. * Get the value from the post custom values
  11. *
  12. * @param string $val name of the value to get
  13. * @param int $postid post ID of the post to get the value for
  14. * @return bool|mixed
  15. */
  16. function wpseo_get_value( $val, $postid = 0 ) {
  17. $postid = absint( $postid );
  18. if ( $postid === 0 ) {
  19. global $post;
  20. if ( isset( $post ) && isset( $post->post_status ) && $post->post_status != 'auto-draft')
  21. $postid = $post->ID;
  22. else
  23. return false;
  24. }
  25. $custom = get_post_custom( $postid );
  26. if ( !empty( $custom['_yoast_wpseo_' . $val][0] ) )
  27. return maybe_unserialize( $custom['_yoast_wpseo_' . $val][0] );
  28. else
  29. return false;
  30. }
  31. /**
  32. * @param string $meta the meta to change
  33. * @param mixed $val the value to set the meta to
  34. * @param int $postid the ID of the post to change the meta for.
  35. */
  36. function wpseo_set_value( $meta, $val, $postid ) {
  37. update_post_meta( $postid, '_yoast_wpseo_' . $meta, $val );
  38. }
  39. /**
  40. * Retrieve an array of all the options the plugin uses. It can't use only one due to limitations of the options API.
  41. *
  42. * @return array of options.
  43. */
  44. function get_wpseo_options_arr() {
  45. $optarr = array( 'wpseo', 'wpseo_permalinks', 'wpseo_titles', 'wpseo_rss', 'wpseo_internallinks', 'wpseo_xml', 'wpseo_social' );
  46. return apply_filters( 'wpseo_options', $optarr );
  47. }
  48. /**
  49. * Retrieve all the options for the SEO plugin in one go.
  50. *
  51. * @return array of options
  52. */
  53. function get_wpseo_options() {
  54. static $options;
  55. if ( !isset( $options ) ) {
  56. $options = array();
  57. foreach ( get_wpseo_options_arr() as $opt ) {
  58. $options = array_merge( $options, (array) get_option( $opt ) );
  59. }
  60. }
  61. return $options;
  62. }
  63. /**
  64. * @param string $string the string to replace the variables in.
  65. * @param array $args the object some of the replacement values might come from, could be a post, taxonomy or term.
  66. * @param array $omit variables that should not be replaced by this function.
  67. * @return string
  68. */
  69. function wpseo_replace_vars( $string, $args, $omit = array() ) {
  70. $args = (array) $args;
  71. $string = strip_tags( $string );
  72. // Let's see if we can bail super early.
  73. if ( strpos( $string, '%%' ) === false )
  74. return trim( preg_replace( '`\s+`u', ' ', $string ) );
  75. global $sep;
  76. if ( !isset( $sep ) || empty( $sep ) )
  77. $sep = '-';
  78. $simple_replacements = array(
  79. '%%sep%%' => $sep,
  80. '%%sitename%%' => get_bloginfo( 'name' ),
  81. '%%sitedesc%%' => get_bloginfo( 'description' ),
  82. '%%currenttime%%' => date( get_option( 'time_format' ) ),
  83. '%%currentdate%%' => date( get_option( 'date_format' ) ),
  84. '%%currentday%%' => date( 'j' ),
  85. '%%currentmonth%%' => date( 'F' ),
  86. '%%currentyear%%' => date( 'Y' ),
  87. );
  88. foreach ( $simple_replacements as $var => $repl ) {
  89. $string = str_replace( $var, $repl, $string );
  90. }
  91. // Let's see if we can bail early.
  92. if ( strpos( $string, '%%' ) === false )
  93. return trim( preg_replace( '`\s+`u', ' ', $string ) );
  94. global $wp_query;
  95. $defaults = array(
  96. 'ID' => '',
  97. 'name' => '',
  98. 'post_author' => '',
  99. 'post_content' => '',
  100. 'post_date' => '',
  101. 'post_excerpt' => '',
  102. 'post_modified' => '',
  103. 'post_title' => '',
  104. 'taxonomy' => '',
  105. 'term_id' => '',
  106. 'term404' => '',
  107. );
  108. if ( isset( $args['post_content'] ) )
  109. $args['post_content'] = wpseo_strip_shortcode( $args['post_content'] );
  110. if ( isset( $args['post_excerpt'] ) )
  111. $args['post_excerpt'] = wpseo_strip_shortcode( $args['post_excerpt'] );
  112. $r = (object) wp_parse_args( $args, $defaults );
  113. $max_num_pages = 1;
  114. if ( !is_single() ) {
  115. $pagenum = get_query_var( 'paged' );
  116. if ( $pagenum === 0 )
  117. $pagenum = 1;
  118. if ( isset( $wp_query->max_num_pages ) && $wp_query->max_num_pages != '' && $wp_query->max_num_pages != 0 )
  119. $max_num_pages = $wp_query->max_num_pages;
  120. } else {
  121. global $post;
  122. $pagenum = get_query_var( 'page' );
  123. $max_num_pages = ( isset( $post->post_content ) ) ? substr_count( $post->post_content, '<!--nextpage-->' ) : 1;
  124. if ( $max_num_pages >= 1 )
  125. $max_num_pages++;
  126. }
  127. // Let's do date first as it's a bit more work to get right.
  128. if ( $r->post_date != '' ) {
  129. $date = mysql2date( get_option( 'date_format' ), $r->post_date );
  130. } else {
  131. if ( get_query_var( 'day' ) && get_query_var( 'day' ) != '' ) {
  132. $date = get_the_date();
  133. } else {
  134. if ( single_month_title( ' ', false ) && single_month_title( ' ', false ) != '' ) {
  135. $date = single_month_title( ' ', false );
  136. } else if ( get_query_var( 'year' ) != '' ) {
  137. $date = get_query_var( 'year' );
  138. } else {
  139. $date = '';
  140. }
  141. }
  142. }
  143. $replacements = array(
  144. '%%date%%' => $date,
  145. '%%searchphrase%%' => esc_html( get_query_var( 's' ) ),
  146. '%%page%%' => ( $max_num_pages > 1 && $pagenum > 1 ) ? sprintf( $sep . ' ' . __( 'Page %d of %d', 'wordpress-seo' ), $pagenum, $max_num_pages ) : '',
  147. '%%pagetotal%%' => $max_num_pages,
  148. '%%pagenumber%%' => $pagenum,
  149. '%%term404%%' => sanitize_text_field ( str_replace( '-', ' ', $r->term404 ) ),
  150. );
  151. if ( isset( $r->ID ) ) {
  152. $replacements = array_merge( $replacements, array(
  153. '%%caption%%' => $r->post_excerpt,
  154. '%%category%%' => wpseo_get_terms( $r->ID, 'category' ),
  155. '%%excerpt%%' => ( !empty( $r->post_excerpt ) ) ? strip_tags( $r->post_excerpt ) : wp_html_excerpt( strip_shortcodes( $r->post_content ),155 ),
  156. '%%excerpt_only%%' => strip_tags( $r->post_excerpt ),
  157. '%%focuskw%%' => wpseo_get_value( 'focuskw', $r->ID ),
  158. '%%id%%' => $r->ID,
  159. '%%modified%%' => mysql2date( get_option( 'date_format' ), $r->post_modified ),
  160. '%%name%%' => get_the_author_meta( 'display_name', !empty( $r->post_author ) ? $r->post_author : get_query_var( 'author' ) ),
  161. '%%tag%%' => wpseo_get_terms( $r->ID, 'post_tag' ),
  162. '%%title%%' => stripslashes( $r->post_title ),
  163. '%%userid%%' => !empty( $r->post_author ) ? $r->post_author : get_query_var( 'author' ),
  164. ) );
  165. }
  166. if ( !empty( $r->taxonomy ) ) {
  167. $replacements = array_merge( $replacements, array(
  168. '%%category_description%%' => trim( strip_tags( get_term_field( 'description', $r->term_id, $r->taxonomy ) ) ),
  169. '%%tag_description%%' => trim( strip_tags( get_term_field( 'description', $r->term_id, $r->taxonomy ) ) ),
  170. '%%term_description%%' => trim( strip_tags( get_term_field( 'description', $r->term_id, $r->taxonomy ) ) ),
  171. '%%term_title%%' => $r->name,
  172. ) );
  173. }
  174. foreach ( $replacements as $var => $repl ) {
  175. if ( !in_array( $var, $omit ) )
  176. $string = str_replace( $var, $repl, $string );
  177. }
  178. if ( strpos( $string, '%%' ) === false ) {
  179. $string = preg_replace( '`\s+`u', ' ', $string );
  180. return trim( $string );
  181. }
  182. if ( isset( $wp_query->query_vars['post_type'] ) && preg_match_all( '`%%pt_([^%]+)%%`u', $string, $matches, PREG_SET_ORDER ) ) {
  183. $pt = get_post_type_object( $wp_query->query_vars['post_type'] );
  184. $pt_plural = $pt_singular = $pt->name;
  185. if ( isset( $pt->labels->singular_name ) )
  186. $pt_singular = $pt->labels->singular_name;
  187. if ( isset( $pt->labels->name ) )
  188. $pt_plural = $pt->labels->name;
  189. $string = str_replace( '%%pt_single%%', $pt_singular, $string );
  190. $string = str_replace( '%%pt_plural%%', $pt_plural, $string );
  191. }
  192. if ( preg_match_all( '`%%cf_([^%]+)%%`u', $string, $matches, PREG_SET_ORDER ) ) {
  193. global $post;
  194. foreach ( $matches as $match ) {
  195. $string = str_replace( $match[0], get_post_meta( $post->ID, $match[1], true ), $string );
  196. }
  197. }
  198. if ( preg_match_all( '`%%ct_desc_([^%]+)?%%`u', $string, $matches, PREG_SET_ORDER ) ) {
  199. global $post;
  200. foreach ( $matches as $match ) {
  201. $terms = get_the_terms( $post->ID, $match[1] );
  202. if( is_array( $terms ) && count( $terms ) > 0 ) {
  203. $term = current( $terms );
  204. $string = str_replace( $match[0], get_term_field( 'description', $term->term_id, $match[1] ), $string );
  205. }
  206. else {
  207. // Make sure that the variable is removed ?
  208. $string = str_replace( $match[0], '', $string );
  209. /* Check for WP_Error object (=invalid taxonomy entered) and if it's an error,
  210. notify in admin dashboard */
  211. if( is_wp_error( $terms ) && is_admin() ) {
  212. add_action( 'admin_notices', 'wpseo_invalid_custom_taxonomy' );
  213. }
  214. }
  215. }
  216. }
  217. if ( preg_match_all( '`%%ct_([^%]+)%%(single%%)?`u', $string, $matches, PREG_SET_ORDER ) ) {
  218. foreach ( $matches as $match ) {
  219. $single = false;
  220. if ( isset( $match[2] ) && $match[2] == 'single%%' )
  221. $single = true;
  222. $ct_terms = wpseo_get_terms( $r->ID, $match[1], $single );
  223. $string = str_replace( $match[0], $ct_terms, $string );
  224. }
  225. }
  226. $string = preg_replace( '`\s+`u', ' ', $string );
  227. return trim( $string );
  228. }
  229. /**
  230. * Throw a notice about an invalid custom taxonomy used
  231. *
  232. * @since 1.4.14
  233. */
  234. function wpseo_invalid_custom_taxonomy() {
  235. echo '<div class="error"><p>' . sprintf( __( 'The taxonomy you used in (one of your) %s variables is <strong>invalid</strong>. Please %sadjust your settings%s.' ), '%%ct_desc_<custom-tax-name>%%', '<a href="' . admin_url( 'admin.php?page=wpseo_titles#top#taxonomies' ) . '">', '</a>' ) . '</p></div>';
  236. }
  237. /**
  238. * Retrieve a post's terms, comma delimited.
  239. *
  240. * @param int $id ID of the post to get the terms for.
  241. * @param string $taxonomy The taxonomy to get the terms for this post from.
  242. * @param bool $return_single If true, return the first term.
  243. * @return string either a single term or a comma delimited string of terms.
  244. */
  245. function wpseo_get_terms( $id, $taxonomy, $return_single = false ) {
  246. $output = '';
  247. // If we're on a specific tag, category or taxonomy page, use that.
  248. if ( is_category() || is_tag() || is_tax() ) {
  249. global $wp_query;
  250. $term = $wp_query->get_queried_object();
  251. $output = $term->name;
  252. }
  253. else if ( !empty( $id ) && !empty( $taxonomy ) ) {
  254. $terms = get_the_terms( $id, $taxonomy );
  255. if ( $terms ) {
  256. foreach ( $terms as $term ) {
  257. if ( $return_single ) {
  258. $output = $term->name;
  259. break;
  260. }
  261. else {
  262. $output .= $term->name . ', ';
  263. }
  264. }
  265. $output = rtrim( trim( $output ), ',' );
  266. }
  267. }
  268. /**
  269. * Allows filtering of the terms list used to replace %%category%%, %%tag%% and %%ct_<custom-tax-name>%% variables
  270. * @api string $output Comma-delimited string containing the terms
  271. */
  272. return apply_filters( 'wpseo_terms', $output );
  273. }
  274. /**
  275. * Retrieve a taxonomy term's meta value.
  276. *
  277. * @param string|object $term term to get the meta value for
  278. * @param string $taxonomy name of the taxonomy to which the term is attached
  279. * @param string $meta meta value to get
  280. * @return bool|mixed value when the meta exists, false when it does not
  281. */
  282. function wpseo_get_term_meta( $term, $taxonomy, $meta ) {
  283. if ( is_string( $term ) )
  284. $term = get_term_by( 'slug', $term, $taxonomy );
  285. if ( is_object( $term ) )
  286. $term = $term->term_id;
  287. else
  288. return false;
  289. $tax_meta = get_option( 'wpseo_taxonomy_meta' );
  290. if ( isset( $tax_meta[$taxonomy][$term] ) )
  291. $tax_meta = $tax_meta[$taxonomy][$term];
  292. else
  293. return false;
  294. return ( isset( $tax_meta['wpseo_' . $meta] ) ) ? $tax_meta['wpseo_' . $meta] : false;
  295. }
  296. /**
  297. * Strip out the shortcodes with a filthy regex, because people don't properly register their shortcodes.
  298. *
  299. * @param string $text input string that might contain shortcodes
  300. * @return string $text string without shortcodes
  301. */
  302. function wpseo_strip_shortcode( $text ) {
  303. return preg_replace( '`\[[^\]]+\]`s', '', $text );
  304. }
  305. /**
  306. * Redirect /sitemap.xml to /sitemap_index.xml
  307. */
  308. function wpseo_xml_redirect_sitemap() {
  309. global $wp_query;
  310. $current_url =( isset($_SERVER["HTTPS"] ) && $_SERVER["HTTPS"]=='on' ) ? 'https://' : 'http://';
  311. $current_url .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
  312. // must be 'sitemap.xml' and must be 404
  313. if ( home_url( '/sitemap.xml' ) == $current_url && $wp_query->is_404) {
  314. wp_redirect( home_url( '/sitemap_index.xml' ) );
  315. }
  316. }
  317. /**
  318. * Initialize sitemaps. Add sitemap rewrite rules and query var
  319. */
  320. function wpseo_xml_sitemaps_init() {
  321. $options = get_option( 'wpseo_xml' );
  322. if ( !isset( $options['enablexmlsitemap'] ) || !$options['enablexmlsitemap'] )
  323. return;
  324. // redirects sitemap.xml to sitemap_index.xml
  325. add_action( 'template_redirect', 'wpseo_xml_redirect_sitemap', 0 );
  326. if ( !is_object( $GLOBALS['wp'] ) ) {
  327. return;
  328. }
  329. $GLOBALS['wp']->add_query_var( 'sitemap' );
  330. $GLOBALS['wp']->add_query_var( 'sitemap_n' );
  331. $GLOBALS['wp']->add_query_var( 'xslt' );
  332. add_rewrite_rule( 'sitemap_index\.xml$', 'index.php?sitemap=1', 'top' );
  333. add_rewrite_rule( '([^/]+?)-sitemap([0-9]+)?\.xml$', 'index.php?sitemap=$matches[1]&sitemap_n=$matches[2]', 'top' );
  334. add_rewrite_rule( 'sitemap\.xslt$', 'index.php?xslt=1', 'top' );
  335. }
  336. add_action( 'init', 'wpseo_xml_sitemaps_init', 1 );
  337. /**
  338. * Notify search engines of the updated sitemap.
  339. */
  340. function wpseo_ping_search_engines( $sitemapurl = null ) {
  341. $options = get_option( 'wpseo_xml' );
  342. $base = $GLOBALS['wp_rewrite']->using_index_permalinks() ? 'index.php/' : '';
  343. if ( $sitemapurl == null )
  344. $sitemapurl = urlencode( home_url( $base . 'sitemap_index.xml' ) );
  345. // Always ping Google and Bing, optionally ping Ask and Yahoo!
  346. wp_remote_get( 'http://www.google.com/webmasters/tools/ping?sitemap=' . $sitemapurl );
  347. wp_remote_get( 'http://www.bing.com/webmaster/ping.aspx?sitemap=' . $sitemapurl );
  348. if ( isset( $options['xml_ping_yahoo'] ) && $options['xml_ping_yahoo'] )
  349. wp_remote_get( 'http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=3usdTDLV34HbjQpIBuzMM1UkECFl5KDN7fogidABihmHBfqaebDuZk1vpLDR64I-&url=' . $sitemapurl );
  350. if ( isset( $options['xml_ping_ask'] ) && $options['xml_ping_ask'] )
  351. wp_remote_get( 'http://submissions.ask.com/ping?sitemap=' . $sitemapurl );
  352. }
  353. add_action( 'wpseo_ping_search_engines', 'wpseo_ping_search_engines' );
  354. function wpseo_store_tracking_response() {
  355. if ( ! wp_verify_nonce( $_POST['nonce'], 'wpseo_activate_tracking' ) )
  356. die();
  357. $options = get_option( 'wpseo' );
  358. $options['tracking_popup'] = 'done';
  359. if ( $_POST['allow_tracking'] == 'yes' )
  360. $options['yoast_tracking'] = 'on';
  361. update_option( 'wpseo', $options );
  362. }
  363. add_action('wp_ajax_wpseo_allow_tracking', 'wpseo_store_tracking_response');
  364. /**
  365. * WPML plugin support: Set titles for custom types / taxonomies as translatable.
  366. * It adds new keys to a wpml-config.xml file for a custom post type title, metadesc, title-ptarchive and metadesc-ptarchive fields translation.
  367. * Documentation: http://wpml.org/documentation/support/language-configuration-files/
  368. *
  369. * @global $sitepress
  370. * @param array $config
  371. * @return array
  372. */
  373. function wpseo_wpml_config( $config ) {
  374. global $sitepress;
  375. if ( ( is_array( $config ) && isset( $config['wpml-config']['admin-texts']['key'] ) ) && ( is_array( $config['wpml-config']['admin-texts']['key'] ) && $config['wpml-config']['admin-texts']['key'] !== array() ) ) {
  376. $admin_texts = $config['wpml-config']['admin-texts']['key'];
  377. foreach ( $admin_texts as $k => $val ) {
  378. if ( $val['attr']['name'] === 'wpseo_titles' ) {
  379. $translate_cp = array_keys( $sitepress->get_translatable_documents() );
  380. if ( is_array( $translate_cp ) && $translate_cp !== array() ) {
  381. foreach ( $translate_cp as $post_type ) {
  382. $admin_texts[$k]['key'][]['attr']['name'] = 'title-'. $post_type;
  383. $admin_texts[$k]['key'][]['attr']['name'] = 'metadesc-'. $post_type;
  384. $admin_texts[$k]['key'][]['attr']['name'] = 'title-ptarchive-'. $post_type;
  385. $admin_texts[$k]['key'][]['attr']['name'] = 'metadesc-ptarchive-'. $post_type;
  386. $translate_tax = $sitepress->get_translatable_taxonomies(false, $post_type);
  387. if ( is_array( $translate_tax ) && $translate_tax !== array() ) {
  388. foreach ( $translate_tax as $taxonomy ) {
  389. $admin_texts[$k]['key'][]['attr']['name'] = 'title-'. $taxonomy;
  390. $admin_texts[$k]['key'][]['attr']['name'] = 'metadesc-'. $taxonomy;
  391. }
  392. }
  393. }
  394. }
  395. break;
  396. }
  397. }
  398. $config['wpml-config']['admin-texts']['key'] = $admin_texts;
  399. }
  400. return $config;
  401. }
  402. add_filter( 'icl_wpml_config_array', 'wpseo_wpml_config' );
  403. /**
  404. * (Un-)schedule the yoast tracking cronjob if the tracking option has changed
  405. *
  406. * Needs to be done here, rather than in the Yoast_Tracking class as class-tracking.php may not be loaded
  407. *
  408. * @todo - check if this has any impact on other Yoast plugins which may use the same tracking schedule
  409. * hook. If so, may be get any other yoast plugin options, check for the tracking status and
  410. * unschedule based on the combined status
  411. *
  412. * @param mixed $disregard Not needed - Option name if option was added, old value if option was updated
  413. * @param array $value The new value of the option after add/update
  414. * @return void
  415. */
  416. function schedule_yoast_tracking( $disregard, $value ) {
  417. $current_schedule = wp_next_scheduled( 'yoast_tracking' );
  418. $tracking = ( isset( $value['yoast_tracking'] ) && $value['yoast_tracking'] ) ? true : false;
  419. if( $tracking === true && $current_schedule === false ) {
  420. // The tracking checks daily, but only sends new data every 7 days.
  421. wp_schedule_event( time(), 'daily', 'yoast_tracking' );
  422. }
  423. else if( $tracking === false && $current_schedule !== false ){
  424. wp_clear_scheduled_hook( 'yoast_tracking' );
  425. }
  426. }
  427. add_action( 'add_option_wpseo', 'schedule_yoast_tracking', 10, 2 );
  428. add_action( 'update_option_wpseo', 'schedule_yoast_tracking', 10, 2 );
  429. /**
  430. * Generate an HTML sitemap
  431. *
  432. * @param array $atts The attributes passed to the shortcode.
  433. *
  434. * @return string
  435. */
  436. function wpseo_sitemap_handler( $atts ) {
  437. global $wpdb;
  438. $atts = shortcode_atts( array(
  439. 'authors' => true,
  440. 'pages' => true,
  441. 'posts' => true,
  442. 'archives' => true
  443. ), $atts );
  444. $display_authors = ( $atts['authors'] === 'no' ) ? false : true;
  445. $display_pages = ( $atts['pages'] === 'no' ) ? false : true;
  446. $display_posts = ( $atts['posts'] === 'no' ) ? false : true;
  447. $display_archives = ( $atts['archives'] === 'no' ) ? false : true;
  448. $options = get_wpseo_options();
  449. // Delete the transient if any of these are no
  450. if ( $display_authors === 'no' || $display_pages === 'no' || $display_posts === 'no' ) {
  451. delete_transient( 'html-sitemap' );
  452. }
  453. // Get any existing copy of our transient data
  454. if ( false !== ( $output = get_transient( 'html-sitemap' ) ) ) {
  455. // $output .= 'CACHE'; // debug
  456. // return $output;
  457. }
  458. $output = '';
  459. // create author list
  460. if ( $display_authors ) {
  461. $output .= '<h2 id="authors">' . __( 'Authors', 'wordpress-seo' ) . '</h2><ul>';
  462. // use echo => false b/c shortcode format screws up
  463. $author_list = wp_list_authors(
  464. array(
  465. 'exclude_admin' => false,
  466. 'echo' => false,
  467. )
  468. );
  469. $output .= $author_list;
  470. $output .= '</ul>';
  471. }
  472. // create page list
  473. if ( $display_pages ) {
  474. $output .= '<h2 id="pages">' . __( 'Pages', 'wordpress-seo' ) . '</h2><ul>';
  475. // Some query magic to retrieve all pages that should be excluded, while preventing noindex pages that are set to
  476. // "always" include in HTML sitemap from being excluded.
  477. $exclude_query = "SELECT DISTINCT( post_id ) FROM $wpdb->postmeta
  478. WHERE ( ( meta_key = '_yoast_wpseo_sitemap-html-include' AND meta_value = 'never' )
  479. OR ( meta_key = '_yoast_wpseo_meta-robots-noindex' AND meta_value = 1 ) )
  480. AND post_id NOT IN
  481. ( SELECT pm2.post_id FROM $wpdb->postmeta pm2
  482. WHERE pm2.meta_key = '_yoast_wpseo_sitemap-html-include' AND pm2.meta_value = 'always')
  483. ORDER BY post_id ASC";
  484. $excluded_pages = $wpdb->get_results( $exclude_query );
  485. $exclude = array();
  486. foreach ( $excluded_pages as $page ) {
  487. $exclude[] = $page->post_id;
  488. }
  489. unset( $excluded_pages, $page );
  490. /**
  491. * This filter allows excluding more pages should you wish to from the HTML sitemap.
  492. */
  493. $exclude = implode( ',', apply_filters( 'wpseo_html_sitemap_page_exclude', $exclude ) );
  494. $page_list = wp_list_pages(
  495. array(
  496. 'exclude' => $exclude,
  497. 'title_li' => '',
  498. 'echo' => false,
  499. )
  500. );
  501. $output .= $page_list;
  502. $output .= '</ul>';
  503. }
  504. // create post list
  505. if ( $display_posts ) {
  506. $output .= '<h2 id="posts">' . __( 'Posts', 'wordpress-seo' ) . '</h2><ul>';
  507. // Add categories you'd like to exclude in the exclude here
  508. // possibly have this controlled by shortcode params
  509. $cats = get_categories( 'exclude=' );
  510. foreach ( $cats as $cat ) {
  511. $output .= "<li><h3>" . $cat->cat_name . "</h3>";
  512. $output .= "<ul>";
  513. $args = array(
  514. 'post_type' => 'post',
  515. 'post_status' => 'publish',
  516. 'posts_per_page' => -1,
  517. 'cat' => $cat->cat_ID,
  518. 'meta_query' => array(
  519. 'relation' => 'OR',
  520. // include if this key doesn't exists
  521. array(
  522. 'key' => '_yoast_wpseo_meta-robots-noindex',
  523. 'value' => '', // This is ignored, but is necessary...
  524. 'compare' => 'NOT EXISTS'
  525. ),
  526. // OR if key does exists include if it is not 1
  527. array(
  528. 'key' => '_yoast_wpseo_meta-robots-noindex',
  529. 'value' => 1,
  530. 'compare' => '!='
  531. ),
  532. // OR this key overrides it
  533. array(
  534. 'key' => '_yoast_wpseo_sitemap-html-include',
  535. 'value' => 'always',
  536. 'compare' => '='
  537. )
  538. )
  539. );
  540. $posts = get_posts( $args );
  541. foreach ( $posts as $post ) {
  542. $category = get_the_category( $post->ID );
  543. // Only display a post link once, even if it's in multiple categories
  544. if ( $category[0]->cat_ID == $cat->cat_ID ) {
  545. $output .= '<li><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></li>';
  546. }
  547. }
  548. $output .= "</ul>";
  549. $output .= "</li>";
  550. }
  551. }
  552. $output .= '</ul>';
  553. // get all public non-builtin post types
  554. $args = array(
  555. 'public' => true,
  556. '_builtin' => false
  557. );
  558. $post_types = get_post_types( $args, 'object' );
  559. // create an noindex array of post types and taxonomies
  560. $noindex = array();
  561. foreach ( $options as $key => $value ) {
  562. if ( strpos( $key, 'noindex-' ) === 0 && $value == 'on' )
  563. $noindex[] = $key;
  564. }
  565. // create custom post type list
  566. foreach ( $post_types as $post_type ) {
  567. if ( ! in_array( 'noindex-' . $post_type->name, $noindex ) ) {
  568. $output .= '<h2 id="' . $post_type->name . '">' . __( $post_type->label, 'wordpress-seo' ) . '</h2><ul>';
  569. $output .= create_type_sitemap_template( $post_type );
  570. $output .= '</ul>';
  571. }
  572. }
  573. // $output = '';
  574. // create archives list
  575. if ( $display_archives ) {
  576. $output .= '<h2 id="archives">' . __( 'Archives', 'wordpress-seo' ) . '</h2><ul>';
  577. foreach ( $post_types as $post_type ) {
  578. if ( $post_type->has_archive && ! in_array( 'noindex-ptarchive-' . $post_type->name, $noindex ) ) {
  579. $output .= '<a href="' . get_post_type_archive_link( $post_type->name ) . '">' . $post_type->labels->name . '</a>';
  580. $output .= create_type_sitemap_template( $post_type );
  581. }
  582. }
  583. $output .= '</ul>';
  584. }
  585. set_transient( 'html-sitemap', $output, 60 );
  586. return $output;
  587. }
  588. add_shortcode( 'wpseo_sitemap', 'wpseo_sitemap_handler' );
  589. function create_type_sitemap_template( $post_type ) {
  590. // $output = '<h2 id="' . $post_type->name . '">' . __( $post_type->label, 'wordpress-seo' ) . '</h2><ul>';
  591. $output = '';
  592. // Get all registered taxonomy of this post type
  593. $taxs = get_object_taxonomies( $post_type->name, 'object' );
  594. // Build the taxonomy tree
  595. require_once( WPSEO_PATH . 'inc/class-sitemap-walker.php' );
  596. $walker = new Sitemap_Walker();
  597. foreach ( $taxs as $key => $tax ) {
  598. if ( $tax->public !== 1 )
  599. continue;
  600. $args = array(
  601. 'post_type' => $post_type->name,
  602. 'tax_query' => array(
  603. array(
  604. 'taxonomy' => $key,
  605. 'field' => 'id',
  606. 'terms' => -1,
  607. 'operator' => 'NOT',
  608. )
  609. )
  610. );
  611. $query = new WP_Query( $args );
  612. $title_li = $query->have_posts() ? $tax->labels->name : '';
  613. $output .= wp_list_categories(
  614. array(
  615. 'title_li' => $title_li,
  616. 'echo' => false,
  617. 'taxonomy' => $key,
  618. 'show_option_none' => '',
  619. // 'hierarchical' => 0, // uncomment this for a flat list
  620. 'walker' => $walker,
  621. 'post_type' => $post_type->name // arg used by the Walker class
  622. )
  623. );
  624. }
  625. $output .= '<br />';
  626. return $output;
  627. }