f ( $this->frontend_page_type->is_home_static_page() ) { $metadesc = $this->get_seo_meta_value( 'metadesc' ); if ( ( $metadesc === '' && $post_type !== '' ) && WPSEO_Options::get( 'metadesc-' . $post_type, '' ) !== '' ) { $template = WPSEO_Options::get( 'metadesc-' . $post_type ); } } elseif ( is_category() || is_tag() || is_tax() ) { $term = $wp_query->get_queried_object(); $metadesc_override = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'desc' ); if ( is_object( $term ) && isset( $term->taxonomy ) && WPSEO_Options::get( 'metadesc-tax-' . $term->taxonomy, '' ) !== '' ) { $template = WPSEO_Options::get( 'metadesc-tax-' . $term->taxonomy ); } } elseif ( is_author() ) { $author_id = get_query_var( 'author' ); $metadesc = get_the_author_meta( 'wpseo_metadesc', $author_id ); if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && WPSEO_Options::get( 'metadesc-author-wpseo', '' ) !== '' ) { $template = WPSEO_Options::get( 'metadesc-author-wpseo' ); } } elseif ( is_post_type_archive() ) { $post_type = $this->get_queried_post_type(); if ( WPSEO_Options::get( 'metadesc-ptarchive-' . $post_type, '' ) !== '' ) { $template = WPSEO_Options::get( 'metadesc-ptarchive-' . $post_type ); } } elseif ( is_archive() ) { $template = WPSEO_Options::get( 'metadesc-archive-wpseo' ); } // If we're on a paginated page, and the template doesn't change for paginated pages, bail. if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && get_query_var( 'paged' ) && get_query_var( 'paged' ) > 1 && $template !== '' ) { if ( strpos( $template, '%%page' ) === false ) { $metadesc = ''; } } } $post_data = $post; if ( is_string( $metadesc_override ) && '' !== $metadesc_override ) { $metadesc = $metadesc_override; if ( isset( $term ) ) { $post_data = $term; } } elseif ( ( ! is_string( $metadesc ) || '' === $metadesc ) && '' !== $template ) { if ( ! isset( $term ) ) { $term = $wp_query->get_queried_object(); } $metadesc = $template; $post_data = $term; } $metadesc = $this->replace_vars( $metadesc, $post_data ); /** * Filter: 'wpseo_metadesc' - Allow changing the Yoast SEO meta description sentence. * * @api string $metadesc The description sentence. */ $this->metadesc = apply_filters( 'wpseo_metadesc', trim( $metadesc ) ); } /** * Based on the redirect meta value, this function determines whether it should redirect the current post / page. * * @return boolean */ public function page_redirect() { if ( is_singular() ) { global $post; if ( ! isset( $post ) || ! is_object( $post ) ) { return false; } $redir = $this->get_seo_meta_value( 'redirect', $post->ID ); if ( $redir !== '' ) { header( 'X-Redirect-By: Yoast SEO' ); wp_redirect( $redir, 301, 'Yoast SEO' ); exit; } } return false; } /** * Outputs noindex values for the current page. */ public function noindex_page() { remove_action( 'wpseo_head', array( $this, 'canonical' ), 20 ); echo '', "\n"; } /** * Send a Robots HTTP header preventing URL from being indexed in the search results while allowing search engines * to follow the links in the object at the URL. * * @since 1.1.7 * @return boolean Boolean indicating whether the noindex header was sent. */ public function noindex_feed() { if ( ( is_feed() || is_robots() ) && headers_sent() === false ) { header( 'X-Robots-Tag: noindex, follow', true ); return true; } return false; } /** * Adds rel="nofollow" to a link, only used for login / registration links. * * @param string $input The link element as a string. * * @return string */ public function nofollow_link( $input ) { return str_replace( 'is_date ) || ( WPSEO_Options::get( 'disable-author', false ) && $wp_query->is_author ) || ( WPSEO_Options::get( 'disable-post_format', false ) && $wp_query->is_tax( 'post_format' ) ) ) { $this->redirect( get_bloginfo( 'url' ), 301 ); return true; } return false; } /** * If the option to disable attachment URLs is checked, this performs the redirect to the attachment. * * @return bool Returns succes status. */ public function attachment_redirect() { if ( WPSEO_Options::get( 'disable-attachment', false ) === false ) { return false; } if ( ! is_attachment() ) { return false; } /** * Allow the developer to change the target redirection URL for attachments. * * @api string $attachment_url The attachment URL for the queried object. * @api object $queried_object The queried object. * * @since 7.5.3 */ $url = apply_filters( 'wpseo_attachment_redirect_url', wp_get_attachment_url( get_queried_object_id() ), get_queried_object() ); if ( ! empty( $url ) ) { $this->do_attachment_redirect( $url ); return true; } return false; } /** * Performs the redirect from the attachment page to the image file itself. * * @param string $attachment_url The attachment image url. * * @return void */ public function do_attachment_redirect( $attachment_url ) { header( 'X-Redirect-By: Yoast SEO' ); wp_redirect( $attachment_url, 301, 'Yoast SEO' ); exit; } /** * Replaces the possible RSS variables with their actual values. * * @param string $content The RSS content that should have the variables replaced. * * @return string */ public function rss_replace_vars( $content ) { global $post; /** * 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. * * @api bool $unsigned Whether or not to follow the links in RSS feed, defaults to true. * * @since 1.4.20 */ $no_follow = apply_filters( 'nofollow_rss_links', true ); $no_follow_attr = ''; if ( $no_follow === true ) { $no_follow_attr = 'rel="nofollow" '; } $author_link = ''; if ( is_object( $post ) ) { $author_link = '' . esc_html( get_the_author() ) . ''; } $post_link = '' . esc_html( get_the_title() ) . ''; $blog_link = '' . esc_html( get_bloginfo( 'name' ) ) . ''; $blog_desc_link = '' . esc_html( get_bloginfo( 'name' ) ) . ' - ' . esc_html( get_bloginfo( 'description' ) ) . ''; $content = stripslashes( trim( $content ) ); $content = str_replace( '%%AUTHORLINK%%', $author_link, $content ); $content = str_replace( '%%POSTLINK%%', $post_link, $content ); $content = str_replace( '%%BLOGLINK%%', $blog_link, $content ); $content = str_replace( '%%BLOGDESCLINK%%', $blog_desc_link, $content ); return $content; } /** * Adds the RSS footer (or header) to the full RSS feed item. * * @param string $content Feed item content. * * @return string */ public function embed_rssfooter( $content ) { return $this->embed_rss( $content, 'full' ); } /** * Adds the RSS footer (or header) to the excerpt RSS feed item. * * @param string $content Feed item excerpt. * * @return string */ public function embed_rssfooter_excerpt( $content ) { return $this->embed_rss( $content, 'excerpt' ); } /** * Adds the RSS footer and/or header to an RSS feed item. * * @since 1.4.14 * * @param string $content Feed item content. * @param string $context Feed item context, either 'excerpt' or 'full'. * * @return string */ public function embed_rss( $content, $context = 'full' ) { /** * Filter: 'wpseo_include_rss_footer' - Allow the RSS footer to be dynamically shown/hidden. * * @api boolean $show_embed Indicates if the RSS footer should be shown or not. * * @param string $context The context of the RSS content - 'full' or 'excerpt'. */ if ( ! apply_filters( 'wpseo_include_rss_footer', true, $context ) ) { return $content; } if ( is_feed() ) { $before = ''; $after = ''; if ( WPSEO_Options::get( 'rssbefore', '' ) !== '' ) { $before = wpautop( $this->rss_replace_vars( WPSEO_Options::get( 'rssbefore' ) ) ); } if ( WPSEO_Options::get( 'rssafter', '' ) !== '' ) { $after = wpautop( $this->rss_replace_vars( WPSEO_Options::get( 'rssafter' ) ) ); } if ( $before !== '' || $after !== '' ) { if ( ( isset( $context ) && $context === 'excerpt' ) && trim( $content ) !== '' ) { $content = wpautop( $content ); } $content = $before . $content . $after; } } return $content; } /** * Used in the force rewrite functionality this retrieves the output, replaces the title with the proper SEO * title and then flushes the output. */ public function flush_cache() { global $wp_query; if ( $this->ob_started !== true ) { return false; } $content = ob_get_clean(); $old_wp_query = $wp_query; wp_reset_query(); // Only replace the debug marker when it is hooked. if ( $this->show_debug_marker() ) { $title = $this->title( '' ); $debug_mark = $this->get_debug_mark(); /* * 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. */ $content = preg_replace( '//i', '', $content ); $content = str_replace( $debug_mark, $debug_mark . "\n" . '' . esc_html( $title ) . '', $content ); } $GLOBALS['wp_query'] = $old_wp_query; echo $content; return true; } /** * Starts the output buffer so it can later be fixed by flush_cache(). */ public function force_rewrite_output_buffer() { $this->ob_started = true; ob_start(); } /** * Get the product name in the head section. * * @return string */ private function head_product_name() { if ( $this->is_premium() ) { return 'Yoast SEO Premium plugin'; } return 'Yoast SEO plugin'; } /** * Check if this plugin is the premium version of WPSEO. * * @return bool */ protected function is_premium() { return WPSEO_Utils::is_yoast_seo_premium(); } /** * Check if term archive query is for multiple terms (/term-1,term2/ or /term-1+term-2/). * * @return bool */ protected function is_multiple_terms_query() { global $wp_query; if ( ! is_tax() && ! is_tag() && ! is_category() ) { return false; } $term = get_queried_object(); $queried_terms = $wp_query->tax_query->queried_terms; if ( empty( $queried_terms[ $term->taxonomy ]['terms'] ) ) { return false; } return count( $queried_terms[ $term->taxonomy ]['terms'] ) > 1; } /** * Wraps wp_safe_redirect to allow testing for redirects. * * @param string $location The path to redirect to. * @param int $status Status code to use. */ public function redirect( $location, $status = 302 ) { header( 'X-Redirect-By: Yoast SEO' ); wp_safe_redirect( $location, $status, 'Yoast SEO' ); exit; } /** * Checks if the debug mark action has been added. * * @return bool True when the action exists. */ protected function show_debug_marker() { return has_action( 'wpseo_head', array( $this, 'debug_mark' ) ) !== false; } /** * Shows the closing debug mark. * * @return string The closing debug mark comment. */ protected function show_closing_debug_mark() { if ( ! $this->show_debug_marker() ) { return ''; } return sprintf( "\n\n", esc_html( $this->head_product_name() ) ); } /** * Builds the title for a post type archive. * * @param string $separator The title separator. * @param string $separator_location The location of the title separator. * * @return string The title to use on a post type archive. */ protected function get_post_type_archive_title( $separator, $separator_location ) { $post_type = $this->get_queried_post_type(); $title = $this->get_title_from_options( 'title-ptarchive-' . $post_type ); if ( ! is_string( $title ) || '' === $title ) { $post_type_obj = get_post_type_object( $post_type ); $title_part = ''; if ( isset( $post_type_obj->labels->menu_name ) ) { $title_part = $post_type_obj->labels->menu_name; } elseif ( isset( $post_type_obj->name ) ) { $title_part = $post_type_obj->name; } $title = $this->get_default_title( $separator, $separator_location, $title_part ); } return $title; } /** * Retrieves the WooCommerce title. * * @return string The WooCommerce title. */ protected function get_woocommerce_title() { $shop_page_id = $this->woocommerce_shop_page->get_shop_page_id(); $post = get_post( $shop_page_id ); $title = $this->get_seo_title( $post ); if ( is_string( $title ) && $title !== '' ) { return $title; } if ( $shop_page_id !== -1 && is_archive() ) { $title = $this->get_template( 'title-' . $post->post_type ); $title = $this->replace_vars( $title, $post ); } return $title; } /** * Retrieves a template from the options. * * @param string $template The template to retrieve. * * @return string The set template. */ protected function get_template( $template ) { return WPSEO_Options::get( $template ); } /** * Retrieves the queried post type. * * @return string The queried post type. */ protected function get_queried_post_type() { $post_type = get_query_var( 'post_type' ); if ( is_array( $post_type ) ) { $post_type = reset( $post_type ); } return $post_type; } /** * Retrieves the SEO Meta value for the supplied key and optional post. * * @param string $key The key to retrieve. * @param int $post_id Optional. The post to retrieve the key for. * * @return string Meta value. */ protected function get_seo_meta_value( $key, $post_id = 0 ) { return WPSEO_Meta::get_value( $key, $post_id ); } /** * Replaces the dynamic variables in a string. * * @param string $string The string to replace the variables in. * @param array $args The object some of the replacement values might come from, * could be a post, taxonomy or term. * @param array $omit Variables that should not be replaced by this function. * * @return string The replaced string. */ protected function replace_vars( $string, $args, $omit = array() ) { $replacer = new WPSEO_Replace_Vars(); return $replacer->replace( $string, $args, $omit ); } /** * Adds shortcode support to category descriptions. * * @param string $desc String to add shortcodes in. * * @return string Content with shortcodes filtered out. */ public function custom_category_descriptions_add_shortcode_support( $desc ) { // Wrap in output buffering to prevent shortcodes that echo stuff instead of return from breaking things. ob_start(); $desc = do_shortcode( $desc ); ob_end_clean(); return $desc; } /* ********************* DEPRECATED METHODS ********************* */ /** * Outputs the meta keywords element. * * @deprecated 6.3 * @codeCoverageIgnore * * @return void */ public function metakeywords() { if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'manage_options' ) ) { _deprecated_function( 'WPSEO_Frontend::metakeywords', '6.3' ); } } /** * Removes unneeded query variables from the URL. * * @deprecated 7.0 * @codeCoverageIgnore * * @return void */ public function clean_permalink() { // As this is a frontend method, we want to make sure it is not displayed for non-logged in users. if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'manage_options' ) ) { _deprecated_function( 'WPSEO_Frontend::clean_permalink', '7.0' ); } } /** * Trailing slashes for everything except is_single(). * * @deprecated 7.0 * @codeCoverageIgnore */ public function add_trailingslash() { // As this is a frontend method, we want to make sure it is not displayed for non-logged in users. if ( function_exists( 'wp_get_current_user' ) && current_user_can( 'manage_options' ) ) { _deprecated_function( 'WPSEO_Frontend::add_trailingslash', '7.0', null ); } } /** * Removes the ?replytocom variable from the link, replacing it with a #comment- anchor. * * @deprecated 7.0 * @codeCoverageIgnore * * @param string $link The comment link as a string. * * @return string The modified link. */ public function remove_reply_to_com( $link ) { _deprecated_function( 'WPSEO_Frontend::remove_reply_to_com', '7.0', 'WPSEO_Remove_Reply_To_Com::remove_reply_to_com' ); $remove_replytocom = new WPSEO_Remove_Reply_To_Com(); return $remove_replytocom->remove_reply_to_com( $link ); } /** * Redirects out the ?replytocom variables. * * @deprecated 7.0 * @codeCoverageIgnore * * @return boolean True when redirect has been done. */ public function replytocom_redirect() { _deprecated_function( 'WPSEO_Frontend::replytocom_redirect', '7.0', 'WPSEO_Remove_Reply_To_Com::replytocom_redirect' ); $remove_replytocom = new WPSEO_Remove_Reply_To_Com(); return $remove_replytocom->replytocom_redirect(); } /** * Determine whether this is the homepage and shows posts. * * @deprecated 7.7 * @codeCoverageIgnore * * @return bool Whether or not the current page is the homepage that displays posts. */ public function is_home_posts_page() { _deprecated_function( __FUNCTION__, '7.7', 'WPSEO_Frontend_Page_Type::is_home_posts_page' ); return $this->frontend_page_type->is_home_posts_page(); } /** * Determine whether the this is the static frontpage. * * @deprecated 7.7 * @codeCoverageIgnore * * @return bool Whether or not the current page is a static frontpage. */ public function is_home_static_page() { _deprecated_function( __FUNCTION__, '7.7', 'WPSEO_Frontend_Page_Type::is_home_static_page' ); return $this->frontend_page_type->is_home_static_page(); } /** * Determine whether this is the posts page, when it's not the frontpage. * * @deprecated 7.7 * @codeCoverageIgnore * * @return bool Whether or not it's a non-frontpage, posts page. */ public function is_posts_page() { _deprecated_function( __FUNCTION__, '7.7', 'WPSEO_Frontend_Page_Type::is_posts_page' ); return $this->frontend_page_type->is_posts_page(); } /** * Function used in testing whether the title should be force rewritten or not. * * @deprecated 9.6 * @codeCoverageIgnore * * @param string $title Title string. * * @return string */ public function title_test_helper( $title ) { _deprecated_function( __METHOD__, 'WPSEO 9.6' ); return $title; } /** * Output the rel=publisher code on every page of the site. * * @deprecated 10.1.3 * * @return boolean Boolean indicating whether the publisher link was printed. */ public function publisher() { _deprecated_function( __METHOD__, 'WPSEO 10.1.3' ); return false; } }