$post_type ) ) { $post_type = reset( $post_type ); } if ( isset( $this->options[ 'metadesc-ptarchive-' . $post_type ] ) ) { $template = $this->options[ 'metadesc-ptarchive-' . $post_type ]; } } elseif ( is_archive() ) { $template = $this->options['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 = wpseo_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 */ function page_redirect() { if ( is_singular() ) { global $post; if ( ! isset( $post ) || ! is_object( $post ) ) { return false; } $redir = WPSEO_Meta::get_value( 'redirect', $post->ID ); if ( $redir !== '' ) { wp_redirect( $redir, 301 ); exit; } } return false; } /** * Outputs noindex values for the current page. */ public function noindex_page() { 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( 'options['disable-date'] === true && $wp_query->is_date ) || ( $this->options['disable-author'] === true && $wp_query->is_author ) || ( $this->options['disable-post_format'] === true && $wp_query->is_tax( 'post_format' ) ) ) { wp_safe_redirect( get_bloginfo( 'url' ), 301 ); exit; } return false; } /** * If the option to redirect attachments to their parent is checked, this performs the redirect. * * An extra check is done for when the attachment has no parent. * * @return boolean False when no redirect was triggered */ function attachment_redirect() { global $post; if ( is_attachment() && ( ( is_object( $post ) && isset( $post->post_parent ) ) && ( is_numeric( $post->post_parent ) && $post->post_parent != 0 ) ) ) { wp_safe_redirect( get_permalink( $post->post_parent ), 301 ); exit; } return false; } /** * Trailing slashes for everything except is_single(). * * Thanks to Mark Jaquith for this code. * * @param string $url URL string. * @param string $type Context (such as single). * * @return string */ function add_trailingslash( $url, $type ) { if ( 'single' === $type || 'single_paged' === $type ) { return $url; } else { return trailingslashit( $url ); } } /** * Removes the ?replytocom variable from the link, replacing it with a #comment- anchor. * * @todo Should this function also allow for relative urls ? * * @param string $link The comment link as a string. * * @return string */ public function remove_reply_to_com( $link ) { return preg_replace( '`href=(["\'])(?:.*(?:\?|&|&)replytocom=(\d+)#respond)`', 'href=$1#comment-$2', $link ); } /** * Redirect out the ?replytocom variables when cleanreplytocom is enabled * * @since 1.4.13 * @return boolean */ function replytocom_redirect() { if ( isset( $_GET['replytocom'] ) && is_singular() ) { $url = get_permalink( $GLOBALS['post']->ID ); $hash = sanitize_text_field( $_GET['replytocom'] ); $query_string = remove_query_arg( 'replytocom', sanitize_text_field( $_SERVER['QUERY_STRING'] ) ); if ( ! empty( $query_string ) ) { $url .= '?' . $query_string; } $url .= '#comment-' . $hash; wp_safe_redirect( $url, 301 ); exit; } return false; } /** * Removes unneeded query variables from the URL. * * @return boolean */ public function clean_permalink() { if ( is_robots() || get_query_var( 'sitemap' ) || empty( $_GET ) ) { return false; } global $wp_query; // Recreate current URL. $cururl = 'http'; if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) { $cururl .= 's'; } $cururl .= '://'; if ( $_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443' ) { $cururl .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; } else { $cururl .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; } $properurl = ''; if ( is_singular() ) { global $post; if ( empty( $post ) ) { $post = $wp_query->get_queried_object(); } $properurl = get_permalink( $post->ID ); $page = get_query_var( 'page' ); if ( $page && $page != 1 ) { $post = get_post( $post->ID ); $page_count = substr_count( $post->post_content, '' ); if ( $page > ( $page_count + 1 ) ) { $properurl = user_trailingslashit( trailingslashit( $properurl ) . ( $page_count + 1 ) ); } else { $properurl = user_trailingslashit( trailingslashit( $properurl ) . $page ); } } // Fix reply to comment links, whoever decided this should be a GET variable? if ( preg_match( '`(\?replytocom=[^&]+)`', sanitize_text_field( $_SERVER['REQUEST_URI'] ), $matches ) ) { $properurl .= str_replace( '?replytocom=', '#comment-', $matches[0] ); } unset( $matches ); // Prevent cleaning out posts & page previews for people capable of viewing them. if ( isset( $_GET['preview'], $_GET['preview_nonce'] ) && current_user_can( 'edit_post' ) ) { $properurl = ''; } } elseif ( is_front_page() ) { if ( $this->is_home_posts_page() ) { $properurl = get_bloginfo( 'url' ) . '/'; } elseif ( $this->is_home_static_page() ) { $properurl = get_permalink( $GLOBALS['post']->ID ); } } elseif ( is_category() || is_tag() || is_tax() ) { $term = $wp_query->get_queried_object(); if ( is_feed() ) { $properurl = get_term_feed_link( $term->term_id, $term->taxonomy ); } else { $properurl = get_term_link( $term, $term->taxonomy ); } } elseif ( is_search() ) { $s = urlencode( preg_replace( '`(%20|\+)`', ' ', get_search_query() ) ); $properurl = get_bloginfo( 'url' ) . '/?s=' . $s; } elseif ( is_404() ) { if ( is_multisite() && ! is_subdomain_install() && is_main_site() ) { if ( $cururl == get_bloginfo( 'url' ) . '/blog/' || $cururl == get_bloginfo( 'url' ) . '/blog' ) { if ( $this->is_home_static_page() ) { $properurl = get_permalink( get_option( 'page_for_posts' ) ); } else { $properurl = get_bloginfo( 'url' ) . '/'; } } } } if ( ! empty( $properurl ) && $wp_query->query_vars['paged'] != 0 && $wp_query->post_count != 0 ) { if ( is_search() && ! empty( $s ) ) { $properurl = get_bloginfo( 'url' ) . '/page/' . $wp_query->query_vars['paged'] . '/?s=' . $s; } else { $properurl = user_trailingslashit( trailingslashit( $properurl ) . 'page/' . $wp_query->query_vars['paged'] ); } } // Prevent cleaning out the WP Subscription managers interface for everyone. if ( isset( $_GET['wp-subscription-manager'] ) ) { $properurl = ''; } /** * Filter: 'wpseo_whitelist_permalink_vars' - Allow plugins to register their own variables not to clean * * @api array $unsigned Array of permalink variables _not_ to clean. Empty by default. */ $whitelisted_extravars = apply_filters( 'wpseo_whitelist_permalink_vars', array() ); if ( $this->options['cleanpermalink-googlesitesearch'] === true ) { // Prevent cleaning out Google Site searches. $whitelisted_extravars = array_merge( $whitelisted_extravars, array( 'q', 'cx', 'debug', 'cof', 'ie', 'sa', ) ); } if ( $this->options['cleanpermalink-googlecampaign'] === true ) { // Prevent cleaning out Google Analytics campaign variables. $whitelisted_extravars = array_merge( $whitelisted_extravars, array( 'utm_campaign', 'utm_medium', 'utm_source', 'utm_content', 'utm_term', 'utm_id', 'gclid', ) ); } if ( $this->options['cleanpermalink-extravars'] !== '' ) { $extravars = explode( ',', $this->options['cleanpermalink-extravars'] ); $extravars = array_map( 'trim', $extravars ); $whitelisted_extravars = array_merge( $whitelisted_extravars, $extravars ); unset( $extravars ); } foreach ( $whitelisted_extravars as $get ) { if ( isset( $_GET[ trim( $get ) ] ) ) { $properurl = ''; } } unset( $get ); if ( ! empty( $properurl ) && $cururl != $properurl ) { wp_safe_redirect( $properurl, 301 ); exit; } } /** * Replaces the possible RSS variables with their actual values. * * @param string $content The RSS content that should have the variables replaced. * * @return string */ 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 */ 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 */ 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 */ 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 ( $this->options['rssbefore'] !== '' ) { $before = wpautop( $this->rss_replace_vars( $this->options['rssbefore'] ) ); } if ( $this->options['rssafter'] !== '' ) { $after = wpautop( $this->rss_replace_vars( $this->options['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. */ 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(); $title = $this->title( '' ); // 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( $this->debug_mark( false ), $this->debug_mark( false ) . "\n" . '' . $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() */ function force_rewrite_output_buffer() { $this->ob_started = true; ob_start(); } /** * Function used in testing whether the title should be force rewritten or not. * * @param string $title Title string. * * @return string */ function title_test_helper( $title ) { $wpseo_titles = get_option( 'wpseo_titles' ); $wpseo_titles['title_test'] ++; update_option( 'wpseo_titles', $wpseo_titles ); // Prevent this setting from being on forever when something breaks, as it breaks caching. if ( $wpseo_titles['title_test'] > 5 ) { $wpseo_titles['title_test'] = 0; update_option( 'wpseo_titles', $wpseo_titles ); remove_filter( 'wpseo_title', array( $this, 'title_test_helper' ) ); return $title; } if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); } if ( ! defined( 'DONOTCACHCEOBJECT' ) ) { define( 'DONOTCACHCEOBJECT', true ); } if ( ! defined( 'DONOTMINIFY' ) ) { define( 'DONOTMINIFY', true ); } if ( $_SERVER['HTTP_USER_AGENT'] === "WordPress/{$GLOBALS['wp_version']}; " . get_bloginfo( 'url' ) . ' - Yoast' ) { return 'This is a Yoast Test Title'; } return $title; } /** * Get the product name in the head section * * @return string */ private function head_product_name() { if ( $this->is_premium() ) { return 'Yoast SEO Premium plugin'; } else { return 'Yoast SEO plugin'; } } /** * Check if this plugin is the premium version of WPSEO * * @return bool */ private function is_premium() { return file_exists( WPSEO_PATH . 'premium/' ); } /** * Getting the keywords * * @param WP_Post $post The post object with the values. * * @return string */ private function get_keywords( $post ) { $keywords = WPSEO_Meta::get_value( 'metakeywords', $post->ID ); $option_meta_key = 'metakey-' . $post->post_type; if ( $keywords === '' && ( is_object( $post ) && ( isset( $this->options[ $option_meta_key ] ) && $this->options[ $option_meta_key ] !== '' ) ) ) { $keywords = wpseo_replace_vars( $this->options[ $option_meta_key ], $post ); } return $keywords; } /** * 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; } /** Deprecated functions */ // @codeCoverageIgnoreStart /** * Outputs or returns the debug marker, which is also used for title replacement when force rewrite is active. * * @deprecated 4.4 * * @param bool $echo Whether or not to echo the debug marker. * @return string */ public function debug_marker( $echo = false ) { return $this->debug_mark( $echo ); } // @codeCoverageIgnoreEnd }