/frontend/class-frontend.php

  1. <?php 
  2. /** 
  3. * @package WPSEO\Frontend 
  4. */ 
  5.  
  6. /** 
  7. * Main frontend class for Yoast SEO, responsible for the SEO output as well as removing 
  8. * default WordPress output. 
  9. */ 
  10. class WPSEO_Frontend { 
  11.  
  12. /** 
  13. * @var object Instance of this class. 
  14. */ 
  15. public static $instance; 
  16.  
  17. /** 
  18. * @var array Holds the plugins options. 
  19. */ 
  20. public $options = array(); 
  21.  
  22. /** 
  23. * @var boolean Boolean indicating wether output buffering has been started. 
  24. */ 
  25. private $ob_started = false; 
  26.  
  27. /** 
  28. * Holds the canonical URL for the current page. 
  29. * 
  30. * @var string 
  31. */ 
  32. private $canonical = null; 
  33.  
  34. /** 
  35. * Holds the canonical URL for the current page that cannot be overriden by a manual canonical input. 
  36. * 
  37. * @var string 
  38. */ 
  39. private $canonical_no_override = null; 
  40.  
  41. /** 
  42. * Holds the canonical URL for the current page without pagination. 
  43. * 
  44. * @var string 
  45. */ 
  46. private $canonical_unpaged = null; 
  47.  
  48. /** 
  49. * Holds the pages meta description. 
  50. * 
  51. * @var string 
  52. */ 
  53. private $metadesc = null; 
  54.  
  55. /** 
  56. * Holds the generated title for the page. 
  57. * 
  58. * @var string 
  59. */ 
  60. private $title = null; 
  61.  
  62. /** 
  63. * Holds the names of the required options. 
  64. * 
  65. * @var array 
  66. */ 
  67. private $required_options = array( 'wpseo', 'wpseo_rss', 'wpseo_social', 'wpseo_permalinks', 'wpseo_titles' ); 
  68.  
  69. /** 
  70. * @var array 
  71. */ 
  72. private $hooks; 
  73.  
  74. /** 
  75. * Class constructor 
  76. * 
  77. * Adds and removes a lot of filters. 
  78. */ 
  79. protected function __construct() { 
  80.  
  81. $this->options = WPSEO_Options::get_options( $this->required_options ); 
  82.  
  83. add_action( 'wp_head', array( $this, 'front_page_specific_init' ), 0 ); 
  84. add_action( 'wp_head', array( $this, 'head' ), 1 ); 
  85.  
  86. // The head function here calls action wpseo_head, to which we hook all our functionality. 
  87. add_action( 'wpseo_head', array( $this, 'debug_mark' ), 2 ); 
  88. add_action( 'wpseo_head', array( $this, 'metadesc' ), 6 ); 
  89. add_action( 'wpseo_head', array( $this, 'robots' ), 10 ); 
  90. add_action( 'wpseo_head', array( $this, 'metakeywords' ), 11 ); 
  91. add_action( 'wpseo_head', array( $this, 'canonical' ), 20 ); 
  92. add_action( 'wpseo_head', array( $this, 'adjacent_rel_links' ), 21 ); 
  93. add_action( 'wpseo_head', array( $this, 'publisher' ), 22 ); 
  94.  
  95. // Remove actions that we will handle through our wpseo_head call, and probably change the output of. 
  96. remove_action( 'wp_head', 'rel_canonical' ); 
  97. remove_action( 'wp_head', 'index_rel_link' ); 
  98. remove_action( 'wp_head', 'start_post_rel_link' ); 
  99. remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' ); 
  100. remove_action( 'wp_head', 'noindex', 1 ); 
  101.  
  102. // When using WP 4.4, just use the new hook. 
  103. add_filter( 'pre_get_document_title', array( $this, 'title' ), 15 ); 
  104. add_filter( 'wp_title', array( $this, 'title' ), 15, 3 ); 
  105.  
  106. add_filter( 'thematic_doctitle', array( $this, 'title' ), 15 ); 
  107.  
  108. add_action( 'wp', array( $this, 'page_redirect' ), 99 ); 
  109.  
  110. add_action( 'template_redirect', array( $this, 'noindex_feed' ) ); 
  111.  
  112. add_filter( 'loginout', array( $this, 'nofollow_link' ) ); 
  113. add_filter( 'register', array( $this, 'nofollow_link' ) ); 
  114.  
  115. // Fix the WooThemes woo_title() output. 
  116. add_filter( 'woo_title', array( $this, 'fix_woo_title' ), 99 ); 
  117.  
  118. if ( $this->options['disable-date'] === true || 
  119. $this->options['disable-author'] === true || 
  120. $this->options['disable-post_format'] === true 
  121. ) { 
  122. add_action( 'wp', array( $this, 'archive_redirect' ) ); 
  123. if ( $this->options['redirectattachment'] === true ) { 
  124. add_action( 'template_redirect', array( $this, 'attachment_redirect' ), 1 ); 
  125.  
  126. /** 
  127. * The setting to get here has been deprecated, but don't remove the code as that would break 
  128. * the functionality for those that still have it! 
  129. */ 
  130. if ( $this->options['trailingslash'] === true ) { 
  131. add_filter( 'user_trailingslashit', array( $this, 'add_trailingslash' ), 10, 2 ); 
  132. if ( $this->options['cleanpermalinks'] === true ) { 
  133. add_action( 'template_redirect', array( $this, 'clean_permalink' ), 1 ); 
  134. if ( $this->options['cleanreplytocom'] === true ) { 
  135. add_filter( 'comment_reply_link', array( $this, 'remove_reply_to_com' ) ); 
  136. add_action( 'template_redirect', array( $this, 'replytocom_redirect' ), 1 ); 
  137. add_filter( 'the_content_feed', array( $this, 'embed_rssfooter' ) ); 
  138. add_filter( 'the_excerpt_rss', array( $this, 'embed_rssfooter_excerpt' ) ); 
  139.  
  140. // For WordPress functions below 4.4. 
  141. if ( ! current_theme_supports( 'title-tag' ) && $this->options['forcerewritetitle'] === true ) { 
  142. add_action( 'template_redirect', array( $this, 'force_rewrite_output_buffer' ), 99999 ); 
  143. add_action( 'wp_footer', array( $this, 'flush_cache' ), - 1 ); 
  144.  
  145. if ( $this->options['title_test'] > 0 ) { 
  146. add_filter( 'wpseo_title', array( $this, 'title_test_helper' ) ); 
  147.  
  148. $primary_category = new WPSEO_Frontend_Primary_Category(); 
  149. $primary_category->register_hooks(); 
  150.  
  151. $this->hooks = array( $primary_category ); 
  152.  
  153. /** 
  154. * Initialize the functions that only need to run on the frontpage 
  155. */ 
  156. public function front_page_specific_init() { 
  157. if ( ! is_front_page() ) { 
  158. return; 
  159.  
  160. new WPSEO_JSON_LD; 
  161. add_action( 'wpseo_head', array( $this, 'webmaster_tools_authentication' ), 90 ); 
  162.  
  163. /** 
  164. * Resets the entire class so canonicals, titles etc can be regenerated. 
  165. */ 
  166. public function reset() { 
  167. foreach ( get_class_vars( __CLASS__ ) as $name => $default ) { 
  168. if ( $name == 'instance' ) { 
  169. self::$instance = null; 
  170. else { 
  171. $this->$name = $default; 
  172. $this->options = WPSEO_Options::get_options( $this->required_options ); 
  173.  
  174. /** 
  175. * Get the singleton instance of this class 
  176. * 
  177. * @return WPSEO_Frontend 
  178. */ 
  179. public static function get_instance() { 
  180. if ( ! ( self::$instance instanceof self ) ) { 
  181. self::$instance = new self(); 
  182.  
  183. return self::$instance; 
  184.  
  185. /** 
  186. * Override Woo's title with our own. 
  187. * 
  188. * @param string $title Title string. 
  189. * 
  190. * @return string 
  191. */ 
  192. public function fix_woo_title( $title ) { 
  193. return $this->title( $title ); 
  194.  
  195. /** 
  196. * Determine whether this is the homepage and shows posts. 
  197. * 
  198. * @return bool 
  199. */ 
  200. public function is_home_posts_page() { 
  201. return ( is_home() && 'posts' == get_option( 'show_on_front' ) ); 
  202.  
  203. /** 
  204. * Determine whether the this is the static frontpage. 
  205. * 
  206. * @return bool 
  207. */ 
  208. public function is_home_static_page() { 
  209. return ( is_front_page() && 'page' == get_option( 'show_on_front' ) && is_page( get_option( 'page_on_front' ) ) ); 
  210.  
  211. /** 
  212. * Determine whether this is the posts page, when it's not the frontpage. 
  213. * 
  214. * @return bool 
  215. */ 
  216. public function is_posts_page() { 
  217. return ( is_home() && 'page' == get_option( 'show_on_front' ) ); 
  218.  
  219. /** 
  220. * Used for static home and posts pages as well as singular titles. 
  221. * 
  222. * @param object|null $object If filled, object to get the title for. 
  223. * 
  224. * @return string 
  225. */ 
  226. public function get_content_title( $object = null ) { 
  227. if ( is_null( $object ) ) { 
  228. $object = $GLOBALS['wp_query']->get_queried_object(); 
  229.  
  230. if ( is_object( $object ) ) { 
  231. $title = WPSEO_Meta::get_value( 'title', $object->ID ); 
  232.  
  233. if ( $title !== '' ) { 
  234. return wpseo_replace_vars( $title, $object ); 
  235.  
  236. $post_type = ( isset( $object->post_type ) ? $object->post_type : $object->query_var ); 
  237.  
  238. return $this->get_title_from_options( 'title-' . $post_type, $object ); 
  239.  
  240. return $this->get_title_from_options( 'title-404-wpseo' ); 
  241.  
  242. /** 
  243. * Used for category, tag, and tax titles. 
  244. * 
  245. * @return string 
  246. */ 
  247. public function get_taxonomy_title() { 
  248. $object = $GLOBALS['wp_query']->get_queried_object(); 
  249.  
  250. $title = WPSEO_Taxonomy_Meta::get_term_meta( $object, $object->taxonomy, 'title' ); 
  251.  
  252. if ( is_string( $title ) && $title !== '' ) { 
  253. return wpseo_replace_vars( $title, $object ); 
  254. else { 
  255. return $this->get_title_from_options( 'title-tax-' . $object->taxonomy, $object ); 
  256.  
  257. /** 
  258. * Used for author titles. 
  259. * 
  260. * @return string 
  261. */ 
  262. public function get_author_title() { 
  263. $author_id = get_query_var( 'author' ); 
  264. $title = trim( get_the_author_meta( 'wpseo_title', $author_id ) ); 
  265.  
  266. if ( $title !== '' ) { 
  267. return wpseo_replace_vars( $title, array() ); 
  268.  
  269. return $this->get_title_from_options( 'title-author-wpseo' ); 
  270.  
  271. /** 
  272. * Simple function to use to pull data from $options. 
  273. * 
  274. * All titles pulled from options will be run through the wpseo_replace_vars function. 
  275. * 
  276. * @param string $index name of the page to get the title from the settings for. 
  277. * @param object|array $var_source possible object to pull variables from. 
  278. * 
  279. * @return string 
  280. */ 
  281. public function get_title_from_options( $index, $var_source = array() ) { 
  282. if ( ! isset( $this->options[ $index ] ) || $this->options[ $index ] === '' ) { 
  283. if ( is_singular() ) { 
  284. return wpseo_replace_vars( '%%title%% %%sep%% %%sitename%%', $var_source ); 
  285. else { 
  286. return ''; 
  287. else { 
  288. return wpseo_replace_vars( $this->options[ $index ], $var_source ); 
  289.  
  290. /** 
  291. * Get the default title for the current page. 
  292. * 
  293. * This is the fallback title generator used when a title hasn't been set for the specific content, taxonomy, author 
  294. * details, or in the options. It scrubs off any present prefix before or after the title (based on $seplocation) in 
  295. * order to prevent duplicate seperations from appearing in the title (this happens when a prefix is supplied to the 
  296. * wp_title call on singular pages). 
  297. * 
  298. * @param string $sep The separator used between variables. 
  299. * @param string $seplocation Whether the separator should be left or right. 
  300. * @param string $title Possible title that's already set. 
  301. * 
  302. * @return string 
  303. */ 
  304. public function get_default_title( $sep, $seplocation, $title = '' ) { 
  305. if ( 'right' == $seplocation ) { 
  306. $regex = '`\s*' . preg_quote( trim( $sep ), '`' ) . '\s*`u'; 
  307. else { 
  308. $regex = '`^\s*' . preg_quote( trim( $sep ), '`' ) . '\s*`u'; 
  309. $title = preg_replace( $regex, '', $title ); 
  310.  
  311. if ( ! is_string( $title ) || ( is_string( $title ) && $title === '' ) ) { 
  312. $title = get_bloginfo( 'name' ); 
  313. $title = $this->add_paging_to_title( $sep, $seplocation, $title ); 
  314. $title = $this->add_to_title( $sep, $seplocation, $title, strip_tags( get_bloginfo( 'description' ) ) ); 
  315.  
  316. return $title; 
  317.  
  318. $title = $this->add_paging_to_title( $sep, $seplocation, $title ); 
  319. $title = $this->add_to_title( $sep, $seplocation, $title, strip_tags( get_bloginfo( 'name' ) ) ); 
  320.  
  321. return $title; 
  322.  
  323. /** 
  324. * This function adds paging details to the title. 
  325. * 
  326. * @param string $sep Separator used in the title. 
  327. * @param string $seplocation Whether the separator should be left or right. 
  328. * @param string $title The title to append the paging info to. 
  329. * 
  330. * @return string 
  331. */ 
  332. public function add_paging_to_title( $sep, $seplocation, $title ) { 
  333. global $wp_query; 
  334.  
  335. if ( ! empty( $wp_query->query_vars['paged'] ) && $wp_query->query_vars['paged'] > 1 ) { 
  336. return $this->add_to_title( $sep, $seplocation, $title, $wp_query->query_vars['paged'] . '/' . $wp_query->max_num_pages ); 
  337.  
  338. return $title; 
  339.  
  340. /** 
  341. * Add part to title, while ensuring that the $seplocation variable is respected. 
  342. * 
  343. * @param string $sep Separator used in the title. 
  344. * @param string $seplocation Whether the separator should be left or right. 
  345. * @param string $title The title to append the title_part to. 
  346. * @param string $title_part The part to append to the title. 
  347. * 
  348. * @return string 
  349. */ 
  350. public function add_to_title( $sep, $seplocation, $title, $title_part ) { 
  351. if ( 'right' === $seplocation ) { 
  352. return $title . $sep . $title_part; 
  353.  
  354. return $title_part . $sep . $title; 
  355.  
  356. /** 
  357. * Main title function. 
  358. * 
  359. * @param string $title Title that might have already been set. 
  360. * @param string $separator Separator determined in theme (unused). 
  361. * @param string $separator_location Whether the separator should be left or right. 
  362. * 
  363. * @return string 
  364. */ 
  365. public function title( $title, $separator = '', $separator_location = '' ) { 
  366. if ( is_null( $this->title ) ) { 
  367. $this->title = $this->generate_title( $title, $separator_location ); 
  368.  
  369. return $this->title; 
  370.  
  371. /** 
  372. * Main title generation function. 
  373. * 
  374. * @param string $title Title that might have already been set. 
  375. * @param string $separator_location Whether the separator should be left or right. 
  376. * 
  377. * @return string 
  378. */ 
  379. private function generate_title( $title, $separator_location ) { 
  380.  
  381. if ( is_feed() ) { 
  382. return $title; 
  383.  
  384. $separator = wpseo_replace_vars( '%%sep%%', array() ); 
  385. $separator = ' ' . trim( $separator ) . ' '; 
  386.  
  387. if ( '' === trim( $separator_location ) ) { 
  388. $separator_location = ( is_rtl() ) ? 'left' : 'right'; 
  389.  
  390. // This needs to be kept track of in order to generate 
  391. // default titles for singular pages. 
  392. $original_title = $title; 
  393.  
  394. // This flag is used to determine if any additional 
  395. // processing should be done to the title after the 
  396. // main section of title generation completes. 
  397. $modified_title = true; 
  398.  
  399. // This variable holds the page-specific title part 
  400. // that is used to generate default titles. 
  401. $title_part = ''; 
  402.  
  403. if ( $this->is_home_static_page() ) { 
  404. $title = $this->get_content_title(); 
  405. elseif ( $this->is_home_posts_page() ) { 
  406. $title = $this->get_title_from_options( 'title-home-wpseo' ); 
  407. elseif ( $this->is_posts_page() ) { 
  408. $title = $this->get_content_title( get_post( get_option( 'page_for_posts' ) ) ); 
  409. elseif ( is_singular() ) { 
  410. $title = $this->get_content_title(); 
  411.  
  412. if ( ! is_string( $title ) || '' === $title ) { 
  413. $title_part = $original_title; 
  414. elseif ( is_search() ) { 
  415. $title = $this->get_title_from_options( 'title-search-wpseo' ); 
  416.  
  417. if ( ! is_string( $title ) || '' === $title ) { 
  418. $title_part = sprintf( __( 'Search for "%s"', 'wordpress-seo' ), esc_html( get_search_query() ) ); 
  419. elseif ( is_category() || is_tag() || is_tax() ) { 
  420. $title = $this->get_taxonomy_title(); 
  421.  
  422. if ( ! is_string( $title ) || '' === $title ) { 
  423. if ( is_category() ) { 
  424. $title_part = single_cat_title( '', false ); 
  425. elseif ( is_tag() ) { 
  426. $title_part = single_tag_title( '', false ); 
  427. else { 
  428. $title_part = single_term_title( '', false ); 
  429. if ( $title_part === '' ) { 
  430. $term = $GLOBALS['wp_query']->get_queried_object(); 
  431. $title_part = $term->name; 
  432. elseif ( is_author() ) { 
  433. $title = $this->get_author_title(); 
  434.  
  435. if ( ! is_string( $title ) || '' === $title ) { 
  436. $title_part = get_the_author_meta( 'display_name', get_query_var( 'author' ) ); 
  437. elseif ( is_post_type_archive() ) { 
  438. $post_type = get_query_var( 'post_type' ); 
  439.  
  440. if ( is_array( $post_type ) ) { 
  441. $post_type = reset( $post_type ); 
  442.  
  443. $title = $this->get_title_from_options( 'title-ptarchive-' . $post_type ); 
  444.  
  445. if ( ! is_string( $title ) || '' === $title ) { 
  446. $post_type_obj = get_post_type_object( $post_type ); 
  447. if ( isset( $post_type_obj->labels->menu_name ) ) { 
  448. $title_part = $post_type_obj->labels->menu_name; 
  449. elseif ( isset( $post_type_obj->name ) ) { 
  450. $title_part = $post_type_obj->name; 
  451. else { 
  452. $title_part = ''; // To be determined what this should be. 
  453. elseif ( is_archive() ) { 
  454. $title = $this->get_title_from_options( 'title-archive-wpseo' ); 
  455.  
  456. // @todo [JRF => Yoast] Should these not use the archive default if no title found ? 
  457. // WPSEO_Options::get_default( 'wpseo_titles', 'title-archive-wpseo' ) 
  458. // Replacement would be needed! 
  459. if ( empty( $title ) ) { 
  460. if ( is_month() ) { 
  461. $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), single_month_title( ' ', false ) ); 
  462. elseif ( is_year() ) { 
  463. $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), get_query_var( 'year' ) ); 
  464. elseif ( is_day() ) { 
  465. $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), get_the_date() ); 
  466. else { 
  467. $title_part = __( 'Archives', 'wordpress-seo' ); 
  468. elseif ( is_404() ) { 
  469.  
  470. $title = $this->get_title_from_options( 'title-404-wpseo' ); 
  471.  
  472. // @todo [JRF => Yoast] Should these not use the 404 default if no title found ? 
  473. // WPSEO_Options::get_default( 'wpseo_titles', 'title-404-wpseo' ) 
  474. // Replacement would be needed! 
  475. if ( empty( $title ) ) { 
  476. $title_part = __( 'Page not found', 'wordpress-seo' ); 
  477. else { 
  478. // In case the page type is unknown, leave the title alone. 
  479. $modified_title = false; 
  480.  
  481. // If you would like to generate a default title instead,  
  482. // the following code could be used 
  483. // $title_part = $title; 
  484. // instead of the line above. 
  485.  
  486. if ( ( $modified_title && empty( $title ) ) || ! empty( $title_part ) ) { 
  487. $title = $this->get_default_title( $separator, $separator_location, $title_part ); 
  488.  
  489. if ( defined( 'ICL_LANGUAGE_CODE' ) && false !== strpos( $title, ICL_LANGUAGE_CODE ) ) { 
  490. $title = str_replace( ' @' . ICL_LANGUAGE_CODE, '', $title ); 
  491.  
  492. /** 
  493. * Filter: 'wpseo_title' - Allow changing the Yoast SEO <title> output 
  494. * 
  495. * @api string $title The page title being put out. 
  496. */ 
  497.  
  498. return esc_html( strip_tags( stripslashes( apply_filters( 'wpseo_title', $title ) ) ) ); 
  499.  
  500. /** 
  501. * Function used when title needs to be force overridden. 
  502. * 
  503. * @return string 
  504. */ 
  505. function force_wp_title() { 
  506. global $wp_query; 
  507. $old_wp_query = null; 
  508.  
  509. if ( ! $wp_query->is_main_query() ) { 
  510. $old_wp_query = $wp_query; 
  511. wp_reset_query(); 
  512.  
  513. $title = $this->title( '' ); 
  514.  
  515. if ( ! empty( $old_wp_query ) ) { 
  516. $GLOBALS['wp_query'] = $old_wp_query; 
  517. unset( $old_wp_query ); 
  518.  
  519. return $title; 
  520.  
  521. /** 
  522. * Outputs or returns the debug marker, which is also used for title replacement when force rewrite is active. 
  523. * 
  524. * @param bool $echo Whether or not to echo the debug marker. 
  525. * 
  526. * @return string 
  527. */ 
  528. public function debug_mark( $echo = true ) { 
  529. $marker = sprintf( 
  530. '<!-- This site is optimized with the ' . $this->head_product_name() . '%1$s - https://yoast.com/wordpress/plugins/seo/ -->',  
  531. /** 
  532. * Filter: 'wpseo_hide_version' - can be used to hide the Yoast SEO version in the debug marker (only available in Yoast SEO Premium) 
  533. * 
  534. * @api bool 
  535. */ 
  536. ( ( apply_filters( 'wpseo_hide_version', false ) && $this->is_premium() ) ? '' : ' v' . WPSEO_VERSION ) 
  537. ); 
  538.  
  539. if ( $echo === false ) { 
  540. return $marker; 
  541. else { 
  542. echo "\n${marker}\n"; 
  543.  
  544. /** 
  545. * Output Webmaster Tools authentication strings 
  546. */ 
  547. public function webmaster_tools_authentication() { 
  548. // Bing. 
  549. if ( $this->options['msverify'] !== '' ) { 
  550. echo '<meta name="msvalidate.01" content="' . esc_attr( $this->options['msverify'] ) . "\" />\n"; 
  551.  
  552. // Google. 
  553. if ( $this->options['googleverify'] !== '' ) { 
  554. echo '<meta name="google-site-verification" content="' . esc_attr( $this->options['googleverify'] ) . "\" />\n"; 
  555.  
  556. // Pinterest. 
  557. if ( $this->options['pinterestverify'] !== '' ) { 
  558. echo '<meta name="p:domain_verify" content="' . esc_attr( $this->options['pinterestverify'] ) . "\" />\n"; 
  559.  
  560. // Yandex. 
  561. if ( $this->options['yandexverify'] !== '' ) { 
  562. echo '<meta name="yandex-verification" content="' . esc_attr( $this->options['yandexverify'] ) . "\" />\n"; 
  563.  
  564. /** 
  565. * Main wrapper function attached to wp_head. This combines all the output on the frontend of the Yoast SEO plugin. 
  566. */ 
  567. public function head() { 
  568. global $wp_query; 
  569.  
  570. $old_wp_query = null; 
  571.  
  572. if ( ! $wp_query->is_main_query() ) { 
  573. $old_wp_query = $wp_query; 
  574. wp_reset_query(); 
  575.  
  576. /** 
  577. * Action: 'wpseo_head' - Allow other plugins to output inside the Yoast SEO section of the head section. 
  578. */ 
  579. do_action( 'wpseo_head' ); 
  580.  
  581. echo '<!-- / ', $this->head_product_name(), ". -->\n\n"; 
  582.  
  583. if ( ! empty( $old_wp_query ) ) { 
  584. $GLOBALS['wp_query'] = $old_wp_query; 
  585. unset( $old_wp_query ); 
  586.  
  587. return; 
  588.  
  589.  
  590. /** 
  591. * Output the meta robots value. 
  592. * 
  593. * @return string 
  594. */ 
  595. public function robots() { 
  596. global $wp_query, $post; 
  597.  
  598. $robots = array(); 
  599. $robots['index'] = 'index'; 
  600. $robots['follow'] = 'follow'; 
  601. $robots['other'] = array(); 
  602.  
  603. if ( is_singular() && is_object( $post ) ) { 
  604.  
  605. $option_name = 'noindex-' . $post->post_type; 
  606. $noindex = isset( $this->options[ $option_name ] ) && $this->options[ $option_name ] === true; 
  607. $private = 'private' === $post->post_status; 
  608.  
  609. if ( $noindex || $private ) { 
  610. $robots['index'] = 'noindex'; 
  611.  
  612. $robots = $this->robots_for_single_post( $robots ); 
  613.  
  614. else { 
  615. if ( is_search() || is_404() ) { 
  616. $robots['index'] = 'noindex'; 
  617. elseif ( is_tax() || is_tag() || is_category() ) { 
  618. $term = $wp_query->get_queried_object(); 
  619. if ( is_object( $term ) && ( isset( $this->options[ 'noindex-tax-' . $term->taxonomy ] ) && $this->options[ 'noindex-tax-' . $term->taxonomy ] === true ) ) { 
  620. $robots['index'] = 'noindex'; 
  621.  
  622. // Three possible values, index, noindex and default, do nothing for default. 
  623. $term_meta = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'noindex' ); 
  624. if ( is_string( $term_meta ) && 'default' !== $term_meta ) { 
  625. $robots['index'] = $term_meta; 
  626.  
  627. if ( $this->is_multiple_terms_query() ) { 
  628. $robots['index'] = 'noindex'; 
  629. elseif ( 
  630. ( is_author() && $this->options['noindex-author-wpseo'] === true ) || 
  631. ( is_date() && $this->options['noindex-archive-wpseo'] === true ) 
  632. ) { 
  633. $robots['index'] = 'noindex'; 
  634. elseif ( is_home() ) { 
  635. if ( get_query_var( 'paged' ) > 1 && $this->options['noindex-subpages-wpseo'] === true ) { 
  636. $robots['index'] = 'noindex'; 
  637.  
  638. $page_for_posts = get_option( 'page_for_posts' ); 
  639. if ( $page_for_posts ) { 
  640. $robots = $this->robots_for_single_post( $robots, $page_for_posts ); 
  641. unset( $page_for_posts ); 
  642.  
  643. elseif ( is_post_type_archive() ) { 
  644. $post_type = get_query_var( 'post_type' ); 
  645.  
  646. if ( is_array( $post_type ) ) { 
  647. $post_type = reset( $post_type ); 
  648.  
  649. if ( isset( $this->options[ 'noindex-ptarchive-' . $post_type ] ) && $this->options[ 'noindex-ptarchive-' . $post_type ] === true ) { 
  650. $robots['index'] = 'noindex'; 
  651.  
  652. $is_paged = isset( $wp_query->query_vars['paged'] ) && ( $wp_query->query_vars['paged'] && $wp_query->query_vars['paged'] > 1 ); 
  653. $noindex_subpages = $this->options['noindex-subpages-wpseo'] === true; 
  654. if ( $is_paged && $noindex_subpages ) { 
  655. $robots['index'] = 'noindex'; 
  656.  
  657. if ( $this->options['noodp'] === true ) { 
  658. $robots['other'][] = 'noodp'; 
  659. unset( $robot ); 
  660.  
  661. // Force override to respect the WP settings. 
  662. if ( '0' == get_option( 'blog_public' ) || isset( $_GET['replytocom'] ) ) { 
  663. $robots['index'] = 'noindex'; 
  664.  
  665.  
  666. $robotsstr = $robots['index'] . ', ' . $robots['follow']; 
  667.  
  668. if ( $robots['other'] !== array() ) { 
  669. $robots['other'] = array_unique( $robots['other'] ); // TODO Most likely no longer needed, needs testing. 
  670. $robotsstr .= ', ' . implode( ', ', $robots['other'] ); 
  671.  
  672. $robotsstr = preg_replace( '`^index, follow, ?`', '', $robotsstr ); 
  673.  
  674. /** 
  675. * Filter: 'wpseo_robots' - Allows filtering of the meta robots output of Yoast SEO 
  676. * 
  677. * @api string $robotsstr The meta robots directives to be echoed. 
  678. */ 
  679. $robotsstr = apply_filters( 'wpseo_robots', $robotsstr ); 
  680.  
  681. if ( is_string( $robotsstr ) && $robotsstr !== '' ) { 
  682. echo '<meta name="robots" content="', esc_attr( $robotsstr ), '"/>', "\n"; 
  683.  
  684. return $robotsstr; 
  685.  
  686. /** 
  687. * Determine $robots values for a single post 
  688. * 
  689. * @param array $robots Robots data array. 
  690. * @param int $post_id The post ID for which to determine the $robots values, defaults to current post. 
  691. * 
  692. * @return array 
  693. */ 
  694. public function robots_for_single_post( $robots, $post_id = 0 ) { 
  695. $noindex = WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ); 
  696. if ( $noindex === '1' ) { 
  697. $robots['index'] = 'noindex'; 
  698. elseif ( $noindex === '2' ) { 
  699. $robots['index'] = 'index'; 
  700.  
  701. if ( WPSEO_Meta::get_value( 'meta-robots-nofollow', $post_id ) === '1' ) { 
  702. $robots['follow'] = 'nofollow'; 
  703.  
  704. $meta_robots_adv = WPSEO_Meta::get_value( 'meta-robots-adv', $post_id ); 
  705.  
  706. if ( $meta_robots_adv !== '' && ( $meta_robots_adv !== '-' && $meta_robots_adv !== 'none' ) ) { 
  707. $meta_robots_adv = explode( ', ', $meta_robots_adv ); 
  708. foreach ( $meta_robots_adv as $robot ) { 
  709. $robots['other'][] = $robot; 
  710. unset( $robot ); 
  711. elseif ( $meta_robots_adv === '' || $meta_robots_adv === '-' ) { 
  712. if ( $this->options['noodp'] === true ) { 
  713. $robots['other'][] = 'noodp'; 
  714. unset( $meta_robots_adv ); 
  715.  
  716. return $robots; 
  717.  
  718. /** 
  719. * This function normally outputs the canonical but is also used in other places to retrieve 
  720. * the canonical URL for the current page. 
  721. * 
  722. * @param bool $echo Whether or not to output the canonical element. 
  723. * @param bool $un_paged Whether or not to return the canonical with or without pagination added to the URL. 
  724. * @param bool $no_override Whether or not to return a manually overridden canonical. 
  725. * 
  726. * @return string $canonical 
  727. */ 
  728. public function canonical( $echo = true, $un_paged = false, $no_override = false ) { 
  729. if ( is_null( $this->canonical ) ) { 
  730. $this->generate_canonical(); 
  731.  
  732. $canonical = $this->canonical; 
  733.  
  734. if ( $un_paged ) { 
  735. $canonical = $this->canonical_unpaged; 
  736. elseif ( $no_override ) { 
  737. $canonical = $this->canonical_no_override; 
  738.  
  739. if ( $echo === false ) { 
  740. return $canonical; 
  741.  
  742. if ( is_string( $canonical ) && '' !== $canonical ) { 
  743. echo '<link rel="canonical" href="' . esc_url( $canonical, null, 'other' ) . '" />' . "\n"; 
  744.  
  745. /** 
  746. * This function normally outputs the canonical but is also used in other places to retrieve 
  747. * the canonical URL for the current page. 
  748. * 
  749. * @return void 
  750. */ 
  751. private function generate_canonical() { 
  752. $canonical = false; 
  753. $canonical_override = false; 
  754.  
  755. // Set decent canonicals for homepage, singulars and taxonomy pages. 
  756. if ( is_singular() ) { 
  757. $obj = get_queried_object(); 
  758. $canonical = get_permalink( $obj->ID ); 
  759.  
  760. $this->canonical_unpaged = $canonical; 
  761.  
  762. $canonical_override = WPSEO_Meta::get_value( 'canonical' ); 
  763.  
  764. // Fix paginated pages canonical, but only if the page is truly paginated. 
  765. if ( get_query_var( 'page' ) > 1 ) { 
  766. $num_pages = ( substr_count( $obj->post_content, '<!--nextpage-->' ) + 1 ); 
  767. if ( $num_pages && get_query_var( 'page' ) <= $num_pages ) { 
  768. if ( ! $GLOBALS['wp_rewrite']->using_permalinks() ) { 
  769. $canonical = add_query_arg( 'page', get_query_var( 'page' ), $canonical ); 
  770. else { 
  771. $canonical = user_trailingslashit( trailingslashit( $canonical ) . get_query_var( 'page' ) ); 
  772. else { 
  773. if ( is_search() ) { 
  774. $search_query = get_search_query(); 
  775.  
  776. // Regex catches case when /search/page/N without search term is itself mistaken for search term. R. 
  777. if ( ! empty( $search_query ) && ! preg_match( '|^page/\d+$|', $search_query ) ) { 
  778. $canonical = get_search_link(); 
  779. elseif ( is_front_page() ) { 
  780. $canonical = WPSEO_Utils::home_url(); 
  781. elseif ( $this->is_posts_page() ) { 
  782.  
  783. $posts_page_id = get_option( 'page_for_posts' ); 
  784. $canonical = WPSEO_Meta::get_value( 'canonical', $posts_page_id ); 
  785.  
  786. if ( empty( $canonical ) ) { 
  787. $canonical = get_permalink( $posts_page_id ); 
  788. elseif ( is_tax() || is_tag() || is_category() ) { 
  789.  
  790. $term = get_queried_object(); 
  791.  
  792. if ( ! empty( $term ) && ! $this->is_multiple_terms_query() ) { 
  793.  
  794. $canonical_override = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'canonical' ); 
  795. $term_link = get_term_link( $term, $term->taxonomy ); 
  796.  
  797. if ( ! is_wp_error( $term_link ) ) { 
  798. $canonical = $term_link; 
  799. elseif ( is_post_type_archive() ) { 
  800. $post_type = get_query_var( 'post_type' ); 
  801. if ( is_array( $post_type ) ) { 
  802. $post_type = reset( $post_type ); 
  803. $canonical = get_post_type_archive_link( $post_type ); 
  804. elseif ( is_author() ) { 
  805. $canonical = get_author_posts_url( get_query_var( 'author' ), get_query_var( 'author_name' ) ); 
  806. elseif ( is_archive() ) { 
  807. if ( is_date() ) { 
  808. if ( is_day() ) { 
  809. $canonical = get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); 
  810. elseif ( is_month() ) { 
  811. $canonical = get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ); 
  812. elseif ( is_year() ) { 
  813. $canonical = get_year_link( get_query_var( 'year' ) ); 
  814.  
  815. $this->canonical_unpaged = $canonical; 
  816.  
  817. if ( $canonical && get_query_var( 'paged' ) > 1 ) { 
  818. global $wp_rewrite; 
  819. if ( ! $wp_rewrite->using_permalinks() ) { 
  820. if ( is_front_page() ) { 
  821. $canonical = trailingslashit( $canonical ); 
  822. $canonical = add_query_arg( 'paged', get_query_var( 'paged' ), $canonical ); 
  823. else { 
  824. if ( is_front_page() ) { 
  825. $canonical = WPSEO_Sitemaps_Router::get_base_url( '' ); 
  826. $canonical = user_trailingslashit( trailingslashit( $canonical ) . trailingslashit( $wp_rewrite->pagination_base ) . get_query_var( 'paged' ) ); 
  827.  
  828. $this->canonical_no_override = $canonical; 
  829.  
  830. if ( is_string( $canonical ) && $canonical !== '' ) { 
  831. // Force canonical links to be absolute, relative is NOT an option. 
  832. if ( WPSEO_Utils::is_url_relative( $canonical ) === true ) { 
  833. $canonical = $this->base_url( $canonical ); 
  834.  
  835. if ( is_string( $canonical_override ) && $canonical_override !== '' ) { 
  836. $canonical = $canonical_override; 
  837.  
  838. /** 
  839. * Filter: 'wpseo_canonical' - Allow filtering of the canonical URL put out by Yoast SEO 
  840. * 
  841. * @api string $canonical The canonical URL 
  842. */ 
  843. $this->canonical = apply_filters( 'wpseo_canonical', $canonical ); 
  844.  
  845. /** 
  846. * Parse the home URL setting to find the base URL for relative URLs. 
  847. * 
  848. * @param string $path Optional path string. 
  849. * 
  850. * @return string 
  851. */ 
  852. private function base_url( $path = null ) { 
  853. $url = get_option( 'home' ); 
  854.  
  855. $parts = wp_parse_url( $url ); 
  856.  
  857. $base_url = trailingslashit( $parts['scheme'] . '://' . $parts['host'] ); 
  858.  
  859. if ( ! is_null( $path ) ) { 
  860. $base_url .= ltrim( $path, '/' ); 
  861.  
  862. return $base_url; 
  863.  
  864. /** 
  865. * Adds 'prev' and 'next' links to archives. 
  866. * 
  867. * @link http://googlewebmastercentral.blogspot.com/2011/09/pagination-with-relnext-and-relprev.html 
  868. * @since 1.0.3 
  869. */ 
  870. public function adjacent_rel_links() { 
  871. // Don't do this for Genesis, as the way Genesis handles homepage functionality is different and causes issues sometimes. 
  872. /** 
  873. * Filter 'wpseo_genesis_force_adjacent_rel_home' - Allows devs to allow echoing rel="next" / rel="prev" by Yoast SEO on Genesis installs 
  874. * 
  875. * @api bool $unsigned Whether or not to rel=next / rel=prev 
  876. */ 
  877. if ( is_home() && function_exists( 'genesis' ) && apply_filters( 'wpseo_genesis_force_adjacent_rel_home', false ) === false ) { 
  878. return; 
  879.  
  880. global $wp_query; 
  881.  
  882. if ( ! is_singular() ) { 
  883. $url = $this->canonical( false, true, true ); 
  884.  
  885. if ( is_string( $url ) && $url !== '' ) { 
  886. $paged = get_query_var( 'paged' ); 
  887.  
  888. if ( 0 == $paged ) { 
  889. $paged = 1; 
  890.  
  891. if ( $paged == 2 ) { 
  892. $this->adjacent_rel_link( 'prev', $url, ( $paged - 1 ), true ); 
  893.  
  894. // Make sure to use index.php when needed, done after paged == 2 check so the prev links to homepage will not have index.php erroneously. 
  895. if ( is_front_page() ) { 
  896. $url = WPSEO_Sitemaps_Router::get_base_url( '' ); 
  897.  
  898. if ( $paged > 2 ) { 
  899. $this->adjacent_rel_link( 'prev', $url, ( $paged - 1 ), true ); 
  900.  
  901. if ( $paged < $wp_query->max_num_pages ) { 
  902. $this->adjacent_rel_link( 'next', $url, ( $paged + 1 ), true ); 
  903. else { 
  904. $numpages = 0; 
  905. if ( isset( $wp_query->post->post_content ) ) { 
  906. $numpages = ( substr_count( $wp_query->post->post_content, '<!--nextpage-->' ) + 1 ); 
  907. if ( $numpages > 1 ) { 
  908. $page = get_query_var( 'page' ); 
  909. if ( ! $page ) { 
  910. $page = 1; 
  911.  
  912. $url = get_permalink( $wp_query->post->ID ); 
  913.  
  914. // If the current page is the frontpage, pagination should use /base/. 
  915. if ( $this->is_home_static_page() ) { 
  916. $usebase = true; 
  917. else { 
  918. $usebase = false; 
  919.  
  920. if ( $page > 1 ) { 
  921. $this->adjacent_rel_link( 'prev', $url, ( $page - 1 ), $usebase, 'single_paged' ); 
  922. if ( $page < $numpages ) { 
  923. $this->adjacent_rel_link( 'next', $url, ( $page + 1 ), $usebase, 'single_paged' ); 
  924.  
  925. /** 
  926. * Get adjacent pages link for archives 
  927. * 
  928. * @since 1.0.2 
  929. * 
  930. * @param string $rel Link relationship, prev or next. 
  931. * @param string $url the un-paginated URL of the current archive. 
  932. * @param string $page the page number to add on to $url for the $link tag. 
  933. * @param boolean $incl_pagination_base whether or not to include /page/ or not. 
  934. * 
  935. * @return void 
  936. */ 
  937. private function adjacent_rel_link( $rel, $url, $page, $incl_pagination_base ) { 
  938. global $wp_rewrite; 
  939. if ( ! $wp_rewrite->using_permalinks() ) { 
  940. if ( $page > 1 ) { 
  941. $url = add_query_arg( 'paged', $page, $url ); 
  942. else { 
  943. if ( $page > 1 ) { 
  944. $base = ''; 
  945. if ( $incl_pagination_base ) { 
  946. $base = trailingslashit( $wp_rewrite->pagination_base ); 
  947. $url = user_trailingslashit( trailingslashit( $url ) . $base . $page ); 
  948. /** 
  949. * Filter: 'wpseo_' . $rel . '_rel_link' - Allow changing link rel output by Yoast SEO 
  950. * 
  951. * @api string $unsigned The full `<link` element. 
  952. */ 
  953. $link = apply_filters( 'wpseo_' . $rel . '_rel_link', '<link rel="' . esc_attr( $rel ) . '" href="' . esc_url( $url ) . "\" />\n" ); 
  954.  
  955. if ( is_string( $link ) && $link !== '' ) { 
  956. echo $link; 
  957.  
  958. /** 
  959. * Output the rel=publisher code on every page of the site. 
  960. * 
  961. * @return boolean Boolean indicating whether the publisher link was printed 
  962. */ 
  963. public function publisher() { 
  964.  
  965. if ( $this->options['plus-publisher'] !== '' ) { 
  966. echo '<link rel="publisher" href="', esc_url( $this->options['plus-publisher'] ), '"/>', "\n"; 
  967.  
  968. return true; 
  969.  
  970. return false; 
  971.  
  972. /** 
  973. * Outputs the meta keywords element. 
  974. * 
  975. * @return void 
  976. */ 
  977. public function metakeywords() { 
  978. global $wp_query, $post; 
  979.  
  980. if ( $this->options['usemetakeywords'] === false ) { 
  981. return; 
  982.  
  983. $keywords = ''; 
  984.  
  985. if ( is_singular() ) { 
  986. $keywords = WPSEO_Meta::get_value( 'metakeywords' ); 
  987. if ( $keywords === '' && ( is_object( $post ) && ( ( isset( $this->options[ 'metakey-' . $post->post_type ] ) && $this->options[ 'metakey-' . $post->post_type ] !== '' ) ) ) ) { 
  988. $keywords = wpseo_replace_vars( $this->options[ 'metakey-' . $post->post_type ], $post ); 
  989. else { 
  990. if ( $this->is_home_posts_page() && $this->options['metakey-home-wpseo'] !== '' ) { 
  991. $keywords = wpseo_replace_vars( $this->options['metakey-home-wpseo'], array() ); 
  992. elseif ( $this->is_home_static_page() ) { 
  993. $keywords = WPSEO_Meta::get_value( 'metakeywords' ); 
  994. if ( $keywords === '' && ( is_object( $post ) && ( isset( $this->options[ 'metakey-' . $post->post_type ] ) && $this->options[ 'metakey-' . $post->post_type ] !== '' ) ) ) { 
  995. $keywords = wpseo_replace_vars( $this->options[ 'metakey-' . $post->post_type ], $post ); 
  996. elseif ( $this->is_posts_page() ) { 
  997. $keywords = $this->get_keywords( get_post( get_option( 'page_for_posts' ) ) ); 
  998. elseif ( is_category() || is_tag() || is_tax() ) { 
  999. $term = $wp_query->get_queried_object(); 
  1000.  
  1001. if ( is_object( $term ) ) { 
  1002. $keywords = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'metakey' ); 
  1003. if ( ( ! is_string( $keywords ) || $keywords === '' ) && ( isset( $this->options[ 'metakey-tax-' . $term->taxonomy ] ) && $this->options[ 'metakey-tax-' . $term->taxonomy ] !== '' ) ) { 
  1004. $keywords = wpseo_replace_vars( $this->options[ 'metakey-tax-' . $term->taxonomy ], $term ); 
  1005. elseif ( is_author() ) { 
  1006. $author_id = get_query_var( 'author' ); 
  1007. $keywords = get_the_author_meta( 'metakey', $author_id ); 
  1008. if ( ! $keywords && $this->options['metakey-author-wpseo'] !== '' ) { 
  1009. $keywords = wpseo_replace_vars( $this->options['metakey-author-wpseo'], $wp_query->get_queried_object() ); 
  1010. elseif ( is_post_type_archive() ) { 
  1011. $post_type = get_query_var( 'post_type' ); 
  1012. if ( is_array( $post_type ) ) { 
  1013. $post_type = reset( $post_type ); 
  1014. if ( isset( $this->options[ 'metakey-ptarchive-' . $post_type ] ) && $this->options[ 'metakey-ptarchive-' . $post_type ] !== '' ) { 
  1015. $keywords = wpseo_replace_vars( $this->options[ 'metakey-ptarchive-' . $post_type ], $wp_query->get_queried_object() ); 
  1016.  
  1017. $keywords = apply_filters( 'wpseo_metakey', trim( $keywords ) ); // TODO Make deprecated. 
  1018.  
  1019. /** 
  1020. * Filter: 'wpseo_metakeywords' - Allow changing the Yoast SEO meta keywords 
  1021. * 
  1022. * @api string $keywords The meta keywords to be echoed. 
  1023. */ 
  1024. $keywords = apply_filters( 'wpseo_metakeywords', trim( $keywords ) ); // More appropriately named. 
  1025.  
  1026. if ( is_string( $keywords ) && $keywords !== '' ) { 
  1027. echo '<meta name="keywords" content="', esc_attr( strip_tags( stripslashes( $keywords ) ) ), '"/>', "\n"; 
  1028.  
  1029. /** 
  1030. * Outputs the meta description element or returns the description text. 
  1031. * 
  1032. * @param bool $echo Echo or return output flag. 
  1033. * 
  1034. * @return string 
  1035. */ 
  1036. public function metadesc( $echo = true ) { 
  1037. if ( is_null( $this->metadesc ) ) { 
  1038. $this->generate_metadesc(); 
  1039.  
  1040. if ( $echo !== false ) { 
  1041. if ( is_string( $this->metadesc ) && $this->metadesc !== '' ) { 
  1042. echo '<meta name="description" content="', esc_attr( strip_tags( stripslashes( $this->metadesc ) ) ), '"/>', "\n"; 
  1043. $this->add_robot_content_noodp( $this->metadesc ); 
  1044. elseif ( current_user_can( 'manage_options' ) && is_singular() ) { 
  1045. echo '<!-- ', __( 'Admin only notice: this page doesn\'t show a meta description because it doesn\'t have one, either write it for this page specifically or go into the SEO -> Titles menu and set up a template.', 'wordpress-seo' ), ' -->', "\n"; 
  1046. else { 
  1047. return $this->metadesc; 
  1048.  
  1049. /** 
  1050. * Generates the meta description text. 
  1051. */ 
  1052. private function generate_metadesc() { 
  1053. global $post, $wp_query; 
  1054.  
  1055. $metadesc = ''; 
  1056. $metadesc_override = false; 
  1057. $post_type = ''; 
  1058. $template = ''; 
  1059.  
  1060. if ( is_object( $post ) && ( isset( $post->post_type ) && $post->post_type !== '' ) ) { 
  1061. $post_type = $post->post_type; 
  1062.  
  1063. if ( is_singular() ) { 
  1064. if ( ( $metadesc === '' && $post_type !== '' ) && isset( $this->options[ 'metadesc-' . $post_type ] ) ) { 
  1065. $template = $this->options[ 'metadesc-' . $post_type ]; 
  1066. $term = $post; 
  1067. $metadesc_override = WPSEO_Meta::get_value( 'metadesc' ); 
  1068. else { 
  1069. if ( is_search() ) { 
  1070. $metadesc = ''; 
  1071. elseif ( $this->is_home_posts_page() ) { 
  1072. $template = $this->options['metadesc-home-wpseo']; 
  1073. $term = array(); 
  1074.  
  1075. if ( empty( $template ) ) { 
  1076. $template = get_bloginfo( 'description' ); 
  1077. elseif ( $this->is_posts_page() ) { 
  1078. $metadesc = WPSEO_Meta::get_value( 'metadesc', get_option( 'page_for_posts' ) ); 
  1079. if ( ( $metadesc === '' && $post_type !== '' ) && isset( $this->options[ 'metadesc-' . $post_type ] ) ) { 
  1080. $page = get_post( get_option( 'page_for_posts' ) ); 
  1081. $template = $this->options[ 'metadesc-' . $post_type ]; 
  1082. $term = $page; 
  1083. elseif ( $this->is_home_static_page() ) { 
  1084. $metadesc = WPSEO_Meta::get_value( 'metadesc' ); 
  1085. if ( ( $metadesc === '' && $post_type !== '' ) && isset( $this->options[ 'metadesc-' . $post_type ] ) ) { 
  1086. $template = $this->options[ 'metadesc-' . $post_type ]; 
  1087. elseif ( is_category() || is_tag() || is_tax() ) { 
  1088. $term = $wp_query->get_queried_object(); 
  1089. $metadesc_override = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'desc' ); 
  1090. if ( is_object( $term ) && isset( $term->taxonomy, $this->options[ 'metadesc-tax-' . $term->taxonomy ] ) ) { 
  1091. $template = $this->options[ 'metadesc-tax-' . $term->taxonomy ]; 
  1092. elseif ( is_author() ) { 
  1093. $author_id = get_query_var( 'author' ); 
  1094. $metadesc = get_the_author_meta( 'wpseo_metadesc', $author_id ); 
  1095. if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && '' !== $this->options['metadesc-author-wpseo'] ) { 
  1096. $template = $this->options['metadesc-author-wpseo']; 
  1097. elseif ( is_post_type_archive() ) { 
  1098. $post_type = get_query_var( 'post_type' ); 
  1099. if ( is_array( $post_type ) ) { 
  1100. $post_type = reset( $post_type ); 
  1101. if ( isset( $this->options[ 'metadesc-ptarchive-' . $post_type ] ) ) { 
  1102. $template = $this->options[ 'metadesc-ptarchive-' . $post_type ]; 
  1103. elseif ( is_archive() ) { 
  1104. $template = $this->options['metadesc-archive-wpseo']; 
  1105.  
  1106. // If we're on a paginated page, and the template doesn't change for paginated pages, bail. 
  1107. if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && get_query_var( 'paged' ) && get_query_var( 'paged' ) > 1 && $template !== '' ) { 
  1108. if ( strpos( $template, '%%page' ) === false ) { 
  1109. $metadesc = ''; 
  1110.  
  1111. $post_data = $post; 
  1112.  
  1113. if ( is_string( $metadesc_override ) && '' !== $metadesc_override ) { 
  1114. $metadesc = $metadesc_override; 
  1115. if ( isset( $term ) ) { 
  1116. $post_data = $term; 
  1117. else if ( ( ! is_string( $metadesc ) || '' === $metadesc ) && '' !== $template ) { 
  1118. if ( ! isset( $term ) ) { 
  1119. $term = $wp_query->get_queried_object(); 
  1120.  
  1121. $metadesc = $template; 
  1122. $post_data = $term; 
  1123.  
  1124. $metadesc = wpseo_replace_vars( $metadesc, $post_data ); 
  1125.  
  1126. /** 
  1127. * Filter: 'wpseo_metadesc' - Allow changing the Yoast SEO meta description sentence. 
  1128. * 
  1129. * @api string $metadesc The description sentence. 
  1130. */ 
  1131. $this->metadesc = apply_filters( 'wpseo_metadesc', trim( $metadesc ) ); 
  1132.  
  1133. /** 
  1134. * Based on the redirect meta value, this function determines whether it should redirect the current post / page. 
  1135. * 
  1136. * @return boolean 
  1137. */ 
  1138. function page_redirect() { 
  1139. if ( is_singular() ) { 
  1140. global $post; 
  1141. if ( ! isset( $post ) || ! is_object( $post ) ) { 
  1142. return false; 
  1143.  
  1144. $redir = WPSEO_Meta::get_value( 'redirect', $post->ID ); 
  1145. if ( $redir !== '' ) { 
  1146. wp_redirect( $redir, 301 ); 
  1147. exit; 
  1148.  
  1149. return false; 
  1150.  
  1151. /** 
  1152. * Outputs noindex values for the current page. 
  1153. */ 
  1154. public function noindex_page() { 
  1155. echo '<meta name="robots" content="noindex" />', "\n"; 
  1156.  
  1157. /** 
  1158. * Send a Robots HTTP header preventing URL from being indexed in the search results while allowing search engines 
  1159. * to follow the links in the object at the URL. 
  1160. * 
  1161. * @since 1.1.7 
  1162. * @return boolean Boolean indicating whether the noindex header was sent 
  1163. */ 
  1164. public function noindex_feed() { 
  1165.  
  1166. if ( ( is_feed() || is_robots() ) && headers_sent() === false ) { 
  1167. header( 'X-Robots-Tag: noindex, follow', true ); 
  1168.  
  1169. return true; 
  1170.  
  1171. return false; 
  1172.  
  1173. /** 
  1174. * Adds rel="nofollow" to a link, only used for login / registration links. 
  1175. * 
  1176. * @param string $input The link element as a string. 
  1177. * 
  1178. * @return string 
  1179. */ 
  1180. public function nofollow_link( $input ) { 
  1181. return str_replace( '<a ', '<a rel="nofollow" ', $input ); 
  1182.  
  1183. /** 
  1184. * When certain archives are disabled, this redirects those to the homepage. 
  1185. * 
  1186. * @return boolean False when no redirect was triggered 
  1187. */ 
  1188. function archive_redirect() { 
  1189. global $wp_query; 
  1190.  
  1191. if ( 
  1192. ( $this->options['disable-date'] === true && $wp_query->is_date ) || 
  1193. ( $this->options['disable-author'] === true && $wp_query->is_author ) || 
  1194. ( $this->options['disable-post_format'] === true && $wp_query->is_tax( 'post_format' ) ) 
  1195. ) { 
  1196. wp_safe_redirect( get_bloginfo( 'url' ), 301 ); 
  1197. exit; 
  1198.  
  1199. return false; 
  1200.  
  1201. /** 
  1202. * If the option to redirect attachments to their parent is checked, this performs the redirect. 
  1203. * 
  1204. * An extra check is done for when the attachment has no parent. 
  1205. * 
  1206. * @return boolean False when no redirect was triggered 
  1207. */ 
  1208. function attachment_redirect() { 
  1209. global $post; 
  1210. if ( is_attachment() && ( ( is_object( $post ) && isset( $post->post_parent ) ) && ( is_numeric( $post->post_parent ) && $post->post_parent != 0 ) ) ) { 
  1211. wp_safe_redirect( get_permalink( $post->post_parent ), 301 ); 
  1212. exit; 
  1213.  
  1214. return false; 
  1215.  
  1216. /** 
  1217. * Trailing slashes for everything except is_single(). 
  1218. * 
  1219. * Thanks to Mark Jaquith for this code. 
  1220. * 
  1221. * @param string $url URL string. 
  1222. * @param string $type Context (such as single). 
  1223. * 
  1224. * @return string 
  1225. */ 
  1226. function add_trailingslash( $url, $type ) { 
  1227. if ( 'single' === $type || 'single_paged' === $type ) { 
  1228. return $url; 
  1229. else { 
  1230. return trailingslashit( $url ); 
  1231.  
  1232. /** 
  1233. * Removes the ?replytocom variable from the link, replacing it with a #comment-<number> anchor. 
  1234. * 
  1235. * @todo Should this function also allow for relative urls ? 
  1236. * 
  1237. * @param string $link The comment link as a string. 
  1238. * 
  1239. * @return string 
  1240. */ 
  1241. public function remove_reply_to_com( $link ) { 
  1242. return preg_replace( '`href=(["\'])(?:.*(?:\?|&|&)replytocom=(\d+)#respond)`', 'href=$1#comment-$2', $link ); 
  1243.  
  1244. /** 
  1245. * Redirect out the ?replytocom variables when cleanreplytocom is enabled 
  1246. * 
  1247. * @since 1.4.13 
  1248. * @return boolean 
  1249. */ 
  1250. function replytocom_redirect() { 
  1251.  
  1252. if ( isset( $_GET['replytocom'] ) && is_singular() ) { 
  1253. $url = get_permalink( $GLOBALS['post']->ID ); 
  1254. $hash = sanitize_text_field( $_GET['replytocom'] ); 
  1255. $query_string = remove_query_arg( 'replytocom', sanitize_text_field( $_SERVER['QUERY_STRING'] ) ); 
  1256. if ( ! empty( $query_string ) ) { 
  1257. $url .= '?' . $query_string; 
  1258. $url .= '#comment-' . $hash; 
  1259. wp_safe_redirect( $url, 301 ); 
  1260. exit; 
  1261.  
  1262. return false; 
  1263.  
  1264. /** 
  1265. * Removes unneeded query variables from the URL. 
  1266. * 
  1267. * @return boolean 
  1268. */ 
  1269. public function clean_permalink() { 
  1270. if ( is_robots() || get_query_var( 'sitemap' ) || empty( $_GET ) ) { 
  1271. return false; 
  1272.  
  1273. global $wp_query; 
  1274.  
  1275. // Recreate current URL. 
  1276. $cururl = 'http'; 
  1277. if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) { 
  1278. $cururl .= 's'; 
  1279. $cururl .= '://'; 
  1280.  
  1281. if ( $_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443' ) { 
  1282. $cururl .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; 
  1283. else { 
  1284. $cururl .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; 
  1285. $properurl = ''; 
  1286.  
  1287. if ( is_singular() ) { 
  1288. global $post; 
  1289. if ( empty( $post ) ) { 
  1290. $post = $wp_query->get_queried_object(); 
  1291.  
  1292. $properurl = get_permalink( $post->ID ); 
  1293.  
  1294. $page = get_query_var( 'page' ); 
  1295. if ( $page && $page != 1 ) { 
  1296. $post = get_post( $post->ID ); 
  1297. $page_count = substr_count( $post->post_content, '<!--nextpage-->' ); 
  1298. if ( $page > ( $page_count + 1 ) ) { 
  1299. $properurl = user_trailingslashit( trailingslashit( $properurl ) . ( $page_count + 1 ) ); 
  1300. else { 
  1301. $properurl = user_trailingslashit( trailingslashit( $properurl ) . $page ); 
  1302.  
  1303. // Fix reply to comment links, whoever decided this should be a GET variable? 
  1304. if ( preg_match( '`(\?replytocom=[^&]+)`', sanitize_text_field( $_SERVER['REQUEST_URI'] ), $matches ) ) { 
  1305. $properurl .= str_replace( '?replytocom=', '#comment-', $matches[0] ); 
  1306. unset( $matches ); 
  1307.  
  1308. // Prevent cleaning out posts & page previews for people capable of viewing them. 
  1309. if ( isset( $_GET['preview'], $_GET['preview_nonce'] ) && current_user_can( 'edit_post' ) ) { 
  1310. $properurl = ''; 
  1311. elseif ( is_front_page() ) { 
  1312. if ( $this->is_home_posts_page() ) { 
  1313. $properurl = get_bloginfo( 'url' ) . '/'; 
  1314. elseif ( $this->is_home_static_page() ) { 
  1315. $properurl = get_permalink( $GLOBALS['post']->ID ); 
  1316. elseif ( is_category() || is_tag() || is_tax() ) { 
  1317. $term = $wp_query->get_queried_object(); 
  1318. if ( is_feed() ) { 
  1319. $properurl = get_term_feed_link( $term->term_id, $term->taxonomy ); 
  1320. else { 
  1321. $properurl = get_term_link( $term, $term->taxonomy ); 
  1322. elseif ( is_search() ) { 
  1323. $s = urlencode( preg_replace( '`(%20|\+)`', ' ', get_search_query() ) ); 
  1324. $properurl = get_bloginfo( 'url' ) . '/?s=' . $s; 
  1325. elseif ( is_404() ) { 
  1326. if ( is_multisite() && ! is_subdomain_install() && is_main_site() ) { 
  1327. if ( $cururl == get_bloginfo( 'url' ) . '/blog/' || $cururl == get_bloginfo( 'url' ) . '/blog' ) { 
  1328. if ( $this->is_home_static_page() ) { 
  1329. $properurl = get_permalink( get_option( 'page_for_posts' ) ); 
  1330. else { 
  1331. $properurl = get_bloginfo( 'url' ) . '/'; 
  1332.  
  1333. if ( ! empty( $properurl ) && $wp_query->query_vars['paged'] != 0 && $wp_query->post_count != 0 ) { 
  1334. if ( is_search() && ! empty( $s ) ) { 
  1335. $properurl = get_bloginfo( 'url' ) . '/page/' . $wp_query->query_vars['paged'] . '/?s=' . $s; 
  1336. else { 
  1337. $properurl = user_trailingslashit( trailingslashit( $properurl ) . 'page/' . $wp_query->query_vars['paged'] ); 
  1338.  
  1339. // Prevent cleaning out the WP Subscription managers interface for everyone. 
  1340. if ( isset( $_GET['wp-subscription-manager'] ) ) { 
  1341. $properurl = ''; 
  1342.  
  1343. /** 
  1344. * Filter: 'wpseo_whitelist_permalink_vars' - Allow plugins to register their own variables not to clean 
  1345. * 
  1346. * @api array $unsigned Array of permalink variables _not_ to clean. Empty by default. 
  1347. */ 
  1348. $whitelisted_extravars = apply_filters( 'wpseo_whitelist_permalink_vars', array() ); 
  1349.  
  1350. if ( $this->options['cleanpermalink-googlesitesearch'] === true ) { 
  1351. // Prevent cleaning out Google Site searches. 
  1352. $whitelisted_extravars = array_merge( $whitelisted_extravars, array( 
  1353. 'q',  
  1354. 'cx',  
  1355. 'debug',  
  1356. 'cof',  
  1357. 'ie',  
  1358. 'sa',  
  1359. ) ); 
  1360.  
  1361. if ( $this->options['cleanpermalink-googlecampaign'] === true ) { 
  1362. // Prevent cleaning out Google Analytics campaign variables. 
  1363. $whitelisted_extravars = array_merge( $whitelisted_extravars, array( 
  1364. 'utm_campaign',  
  1365. 'utm_medium',  
  1366. 'utm_source',  
  1367. 'utm_content',  
  1368. 'utm_term',  
  1369. 'utm_id',  
  1370. 'gclid',  
  1371. ) ); 
  1372.  
  1373. if ( $this->options['cleanpermalink-extravars'] !== '' ) { 
  1374. $extravars = explode( ', ', $this->options['cleanpermalink-extravars'] ); 
  1375. $extravars = array_map( 'trim', $extravars ); 
  1376. $whitelisted_extravars = array_merge( $whitelisted_extravars, $extravars ); 
  1377. unset( $extravars ); 
  1378.  
  1379. foreach ( $whitelisted_extravars as $get ) { 
  1380. if ( isset( $_GET[ trim( $get ) ] ) ) { 
  1381. $properurl = ''; 
  1382. unset( $get ); 
  1383.  
  1384. if ( ! empty( $properurl ) && $cururl != $properurl ) { 
  1385. wp_safe_redirect( $properurl, 301 ); 
  1386. exit; 
  1387.  
  1388. /** 
  1389. * Replaces the possible RSS variables with their actual values. 
  1390. * 
  1391. * @param string $content The RSS content that should have the variables replaced. 
  1392. * 
  1393. * @return string 
  1394. */ 
  1395. function rss_replace_vars( $content ) { 
  1396. global $post; 
  1397.  
  1398. /** 
  1399. * Allow the developer to determine whether or not to follow the links in the bits Yoast SEO adds to the RSS feed, defaults to true. 
  1400. * 
  1401. * @api bool $unsigned Whether or not to follow the links in RSS feed, defaults to true. 
  1402. * 
  1403. * @since 1.4.20 
  1404. */ 
  1405. $no_follow = apply_filters( 'nofollow_rss_links', true ); 
  1406. $no_follow_attr = ''; 
  1407. if ( $no_follow === true ) { 
  1408. $no_follow_attr = 'rel="nofollow" '; 
  1409.  
  1410. $author_link = ''; 
  1411. if ( is_object( $post ) ) { 
  1412. $author_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_author_posts_url( $post->post_author ) ) . '">' . get_the_author() . '</a>'; 
  1413.  
  1414. $post_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_permalink() ) . '">' . get_the_title() . '</a>'; 
  1415. $blog_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_bloginfo( 'url' ) ) . '">' . get_bloginfo( 'name' ) . '</a>'; 
  1416. $blog_desc_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_bloginfo( 'url' ) ) . '">' . get_bloginfo( 'name' ) . ' - ' . strip_tags( get_bloginfo( 'description' ) ) . '</a>'; 
  1417.  
  1418. $content = stripslashes( trim( $content ) ); 
  1419. $content = str_replace( '%%AUTHORLINK%%', $author_link, $content ); 
  1420. $content = str_replace( '%%POSTLINK%%', $post_link, $content ); 
  1421. $content = str_replace( '%%BLOGLINK%%', $blog_link, $content ); 
  1422. $content = str_replace( '%%BLOGDESCLINK%%', $blog_desc_link, $content ); 
  1423.  
  1424. return $content; 
  1425.  
  1426. /** 
  1427. * Adds the RSS footer (or header) to the full RSS feed item. 
  1428. * 
  1429. * @param string $content Feed item content. 
  1430. * 
  1431. * @return string 
  1432. */ 
  1433. function embed_rssfooter( $content ) { 
  1434. return $this->embed_rss( $content, 'full' ); 
  1435.  
  1436. /** 
  1437. * Adds the RSS footer (or header) to the excerpt RSS feed item. 
  1438. * 
  1439. * @param string $content Feed item excerpt. 
  1440. * 
  1441. * @return string 
  1442. */ 
  1443. function embed_rssfooter_excerpt( $content ) { 
  1444. return $this->embed_rss( $content, 'excerpt' ); 
  1445.  
  1446. /** 
  1447. * Adds the RSS footer and/or header to an RSS feed item. 
  1448. * 
  1449. * @since 1.4.14 
  1450. * 
  1451. * @param string $content Feed item content. 
  1452. * @param string $context Feed item context, either 'excerpt' or 'full'. 
  1453. * 
  1454. * @return string 
  1455. */ 
  1456. function embed_rss( $content, $context = 'full' ) { 
  1457.  
  1458. /** 
  1459. * Filter: 'wpseo_include_rss_footer' - Allow the RSS footer to be dynamically shown/hidden. 
  1460. * 
  1461. * @api boolean $show_embed Indicates if the RSS footer should be shown or not 
  1462. * 
  1463. * @param string $context The context of the RSS content - 'full' or 'excerpt'. 
  1464. */ 
  1465. if ( ! apply_filters( 'wpseo_include_rss_footer', true, $context ) ) { 
  1466. return $content; 
  1467.  
  1468. if ( is_feed() ) { 
  1469. $before = ''; 
  1470. $after = ''; 
  1471.  
  1472. if ( $this->options['rssbefore'] !== '' ) { 
  1473. $before = wpautop( $this->rss_replace_vars( $this->options['rssbefore'] ) ); 
  1474. if ( $this->options['rssafter'] !== '' ) { 
  1475. $after = wpautop( $this->rss_replace_vars( $this->options['rssafter'] ) ); 
  1476. if ( $before !== '' || $after !== '' ) { 
  1477. if ( ( isset( $context ) && $context === 'excerpt' ) && trim( $content ) !== '' ) { 
  1478. $content = wpautop( $content ); 
  1479. $content = $before . $content . $after; 
  1480.  
  1481. return $content; 
  1482.  
  1483.  
  1484. /** 
  1485. * Used in the force rewrite functionality this retrieves the output, replaces the title with the proper SEO 
  1486. * title and then flushes the output. 
  1487. */ 
  1488. function flush_cache() { 
  1489.  
  1490. global $wp_query; 
  1491.  
  1492. if ( $this->ob_started !== true ) { 
  1493. return false; 
  1494.  
  1495. $content = ob_get_clean(); 
  1496.  
  1497. $old_wp_query = $wp_query; 
  1498.  
  1499. wp_reset_query(); 
  1500.  
  1501. $title = $this->title( '' ); 
  1502.  
  1503. // Find all titles, strip them out and add the new one in within the debug marker, so it's easily identified whether a site uses force rewrite. 
  1504. $content = preg_replace( '/<title.*?\/title>/i', '', $content ); 
  1505. $content = str_replace( $this->debug_mark( false ), $this->debug_mark( false ) . "\n" . '<title>' . $title . '</title>', $content ); 
  1506.  
  1507. $GLOBALS['wp_query'] = $old_wp_query; 
  1508.  
  1509. echo $content; 
  1510.  
  1511. return true; 
  1512.  
  1513. /** 
  1514. * Starts the output buffer so it can later be fixed by flush_cache() 
  1515. */ 
  1516. function force_rewrite_output_buffer() { 
  1517. $this->ob_started = true; 
  1518. ob_start(); 
  1519.  
  1520. /** 
  1521. * Function used in testing whether the title should be force rewritten or not. 
  1522. * 
  1523. * @param string $title Title string. 
  1524. * 
  1525. * @return string 
  1526. */ 
  1527. function title_test_helper( $title ) { 
  1528. $wpseo_titles = get_option( 'wpseo_titles' ); 
  1529.  
  1530. $wpseo_titles['title_test'] ++; 
  1531. update_option( 'wpseo_titles', $wpseo_titles ); 
  1532.  
  1533. // Prevent this setting from being on forever when something breaks, as it breaks caching. 
  1534. if ( $wpseo_titles['title_test'] > 5 ) { 
  1535. $wpseo_titles['title_test'] = 0; 
  1536. update_option( 'wpseo_titles', $wpseo_titles ); 
  1537.  
  1538. remove_filter( 'wpseo_title', array( $this, 'title_test_helper' ) ); 
  1539.  
  1540. return $title; 
  1541.  
  1542. if ( ! defined( 'DONOTCACHEPAGE' ) ) { 
  1543. define( 'DONOTCACHEPAGE', true ); 
  1544. if ( ! defined( 'DONOTCACHCEOBJECT' ) ) { 
  1545. define( 'DONOTCACHCEOBJECT', true ); 
  1546. if ( ! defined( 'DONOTMINIFY' ) ) { 
  1547. define( 'DONOTMINIFY', true ); 
  1548.  
  1549. if ( $_SERVER['HTTP_USER_AGENT'] === "WordPress/{$GLOBALS['wp_version']}; " . get_bloginfo( 'url' ) . ' - Yoast' ) { 
  1550. return 'This is a Yoast Test Title'; 
  1551.  
  1552. return $title; 
  1553.  
  1554. /** 
  1555. * Get the product name in the head section 
  1556. * 
  1557. * @return string 
  1558. */ 
  1559. private function head_product_name() { 
  1560. if ( $this->is_premium() ) { 
  1561. return 'Yoast SEO Premium plugin'; 
  1562. else { 
  1563. return 'Yoast SEO plugin'; 
  1564.  
  1565. /** 
  1566. * Check if this plugin is the premium version of WPSEO 
  1567. * 
  1568. * @return bool 
  1569. */ 
  1570. private function is_premium() { 
  1571. return file_exists( WPSEO_PATH . 'premium/' ); 
  1572.  
  1573. /** 
  1574. * Checks whether the user has written a meta-description. If written, makes sure meta robots content is noodp. 
  1575. * 
  1576. * @param String $description The content of the meta description. 
  1577. */ 
  1578. private function add_robot_content_noodp( $description ) { 
  1579. if ( ! ( empty( $description ) ) && $this->options['noodp'] === false ) { 
  1580. $this->options['noodp'] = true; 
  1581.  
  1582. /** 
  1583. * Getting the keywords 
  1584. * 
  1585. * @param WP_Post $post The post object with the values. 
  1586. * 
  1587. * @return string 
  1588. */ 
  1589. private function get_keywords( $post ) { 
  1590. $keywords = WPSEO_Meta::get_value( 'metakeywords', $post->ID ); 
  1591. $option_meta_key = 'metakey-' . $post->post_type; 
  1592.  
  1593. if ( $keywords === '' && ( is_object( $post ) && ( isset( $this->options[ $option_meta_key ] ) && $this->options[ $option_meta_key ] !== '' ) ) ) { 
  1594. $keywords = wpseo_replace_vars( $this->options[ $option_meta_key ], $post ); 
  1595.  
  1596. return $keywords; 
  1597.  
  1598. /** 
  1599. * Check if term archive query is for multiple terms (/term-1, term2/ or /term-1+term-2/). 
  1600. * 
  1601. * @return bool 
  1602. */ 
  1603. protected function is_multiple_terms_query() { 
  1604.  
  1605. global $wp_query; 
  1606.  
  1607. if ( ! is_tax() && ! is_tag() && ! is_category() ) { 
  1608. return false; 
  1609.  
  1610. $term = get_queried_object(); 
  1611. $queried_terms = $wp_query->tax_query->queried_terms; 
  1612.  
  1613. if ( empty( $queried_terms[ $term->taxonomy ]['terms'] ) ) { 
  1614. return false; 
  1615.  
  1616. return count( $queried_terms[ $term->taxonomy ]['terms'] ) > 1; 
  1617.  
  1618. /** Deprecated functions */ 
  1619. // @codeCoverageIgnoreStart 
  1620. /** 
  1621. * Outputs or returns the debug marker, which is also used for title replacement when force rewrite is active. 
  1622. * 
  1623. * @deprecated 4.4 
  1624. * 
  1625. * @param bool $echo Whether or not to echo the debug marker. 
  1626. * @return string 
  1627. */ 
  1628. public function debug_marker( $echo = false ) { 
  1629. return $this->debug_mark( $echo ); 
  1630.  
  1631. // @codeCoverageIgnoreEnd 
.