/ Make sure we're replacing TITLE that's actually in the HEAD. $head = ( $title && preg_match( "~{$head_rx}~ix", $head ) ) ? preg_replace( "~{$title_rx}~i", "{$title}-cq9武圣选马还是刀", $head ) : $head; return preg_replace( '/__SMARTCRAWL_NL__/', "\n", preg_replace( '/__SMARTCRAWL_DOLLAR__/', '\$', $head ) ); } /** * Get queried entity. * * @return Entity */ private function get_queried_entity() { if ( ! $this->queried_entity ) { $this->queried_entity = Endpoint_Resolver::resolve()->get_queried_entity(); } return $this->queried_entity; } /** * Gets the processed HTML title * * @param string $title Original title. * * @return string */ public function smartcrawl_title( $title ) { $entity = $this->get_queried_entity(); return $entity ? esc_html( wp_strip_all_tags( stripslashes( $entity->get_meta_title() ) ) ) : $title; } /** * Processes the stuff that goes into the HTML head */ public function smartcrawl_head() { if ( $this->force_rewrite_title() ) { $this->smartcrawl_stop_title_buffer(); // STOP processing the buffer. } $this->head_start(); $this->smartcrawl_canonical(); $this->smartcrawl_rel_links(); if ( ! $this->wp_robots_api_available() ) { $this->smartcrawl_robots(); } $this->smartcrawl_metadesc(); $this->print_meta_tags(); $this->head_end(); } /** * Extra items for head. * * @return void */ public function smartcrawl_head_extras() { $this->head_start(); $this->print_meta_tags(); $this->head_end(); } /** * Print html tag. * * @param string $html HTML content. * * @return bool */ private function print_html_tag( $html ) { if ( ! preg_match( '/\<(link|meta)/', $html ) ) { // Do not allow plaintext output. return false; } echo wp_kses( $html, array( 'meta' => array( 'name' => array(), 'content' => array(), 'http-equiv' => array(), 'charset' => array(), 'scheme' => array(), ), 'link' => array( 'charset' => array(), 'crossorigin' => array(), 'use-credentials' => array(), 'href' => array(), 'hreflang' => array(), 'media' => array(), 'rel' => array(), 'stylesheet' => array(), 'rev' => array(), 'sizes' => array(), 'any' => array(), 'target' => array(), 'frame_name' => array(), 'type' => array(), ), ) ); return true; } /** * Stops buffering the output - the title should now be in the buffer. */ private function smartcrawl_stop_title_buffer() { if ( function_exists( '\ob_list_handlers' ) ) { $active_handlers = \ob_list_handlers(); } else { $active_handlers = array(); } if ( count( $active_handlers ) > 0 ) { $offset = count( $active_handlers ) - 1; $handler = ! empty( $active_handlers[ $offset ] ) && is_string( $active_handlers[ $offset ] ) ? trim( $active_handlers[ $offset ] ) : ''; if ( preg_match( '/::smartcrawl_process_title_buffer$/', $handler ) ) { ob_end_flush(); } } } /** * Handle canonical link rendering * * @return bool Status */ private function smartcrawl_canonical() { if ( function_exists( '\bp_is_blog_page' ) // If we have BuddyPress. && // ... and ! ( \bp_is_blog_page() || is_404() ) // ... we're on a BP page. ) { // Because apparently BP prints it's own canonical URLs. return false; } if ( ! apply_filters( 'wds_process_canonical', true ) ) { return false; } // Allow optional filtering out. // Set decent canonicals for homepage, singulars and taxonomy pages. $canonical = $this->get_canonical_url(); // Let's check if we're dealing with the redundant canonical. if ( \smartcrawl_is_switch_active( 'SMARTCRAWL_SUPPRESS_REDUNDANT_CANONICAL' ) ) { global $wp; $current_url = add_query_arg( $_GET, trailingslashit( home_url( $wp->request ) ) ); // phpcs:ignore -- Nonce not applicable. if ( $current_url === $canonical ) { $canonical = false; } } if ( ! empty( $canonical ) && ! is_wp_error( $canonical ) ) { $this->print_html_tag( '' . "\n" ); } return ! empty( $canonical ); } /** * Get canonical URL. * * @return bool|mixed|string|\WP_Error */ public function get_canonical_url() { $entity = $this->get_queried_entity(); return $entity ? $entity->get_canonical_url() : ''; } /** * Output link rel tags. */ private function smartcrawl_rel_links() { global $wp_query, $paged; if ( ! $wp_query->max_num_pages ) { return false; } // Short out on missing max page number. if ( ! apply_filters( 'wds_process_rel_links', true ) ) { return false; } // Allow optional filtering out. $is_taxonomy = ( is_tax() || is_tag() || is_category() || is_date() ); $requested_year = get_query_var( 'year' ); $requested_month = get_query_var( 'monthnum' ); $is_date = is_date() && ! empty( $requested_year ); $date_callback = ! empty( $requested_year ) && empty( $requested_month ) ? 'get_year_link' : 'get_month_link'; $pageable = ( $is_taxonomy || ( is_home() && 'posts' === get_option( 'show_on_front' ) ) ); if ( ! $pageable ) { return false; } $term = $wp_query->get_queried_object(); $canonical = ! empty( $term->taxonomy ) && $is_taxonomy ? \smartcrawl_get_term_meta( $term, $term->taxonomy, 'wds_canonical' ) : false; if ( ! $canonical ) { if ( (int) $paged > 1 ) { $prev = is_home() ? home_url() : ( $is_date ? $date_callback( $requested_year, $requested_month ) : get_term_link( $term, $term->taxonomy ) ); $prev = ( '' === get_option( 'permalink_structure' ) ) ? ( ( $paged > 2 ) ? add_query_arg( 'page', $paged - 1, $prev ) : $prev ) : ( ( $paged > 2 ) ? trailingslashit( $prev ) . 'page/' . ( $paged - 1 ) : $prev ); $prev = esc_attr( trailingslashit( $prev ) ); $this->print_html_tag( "\n" ); } $is_paged = (int) $paged ? (int) $paged : 1; if ( $is_paged && $is_paged < $wp_query->max_num_pages ) { $next = is_home() ? home_url() : ( $is_date ? $date_callback( $requested_year, $requested_month ) : get_term_link( $term, $term->taxonomy ) ); $next_page = $is_paged + 1; $next = ( '' === get_option( 'permalink_structure' ) ) ? add_query_arg( 'page', $next_page, $next ) : trailingslashit( $next ) . 'page/' . $next_page; $next = esc_attr( trailingslashit( $next ) ); $this->print_html_tag( "\n" ); } } return true; } /** * Get robots string. * * @return string */ private function get_robots_string() { $entity = $this->get_queried_entity(); $robots = $entity ? $entity->get_robots() : ''; // Clean up, index, follow is the default and doesn't need to be in output. All other combinations should be. if ( 'index,follow' === $robots ) { $robots = ''; } if ( strpos( $robots, 'index,follow,' ) === 0 ) { $robots = str_replace( 'index,follow,', '', $robots ); } return rtrim( $robots, ',' ); } /** * Output meta robots tag * * @return bool */ private function smartcrawl_robots() { if ( $this->robots_processing_disabled() ) { return false; } $robots = $this->get_robots_string(); if ( $this->is_blog_public() && '' !== $robots ) { $this->print_html_tag( '' . "\n" ); } return true; } /** * Add items to robots. * * @param array $wp_robots Robots. * * @return mixed */ public function add_smartcrawl_robots_to_wp_robots( $wp_robots ) { if ( ! $this->is_blog_public() // If user has an override at the blog level. || $this->robots_processing_disabled() // or robots processing is disabled. ) { // leave everything to WP. return $wp_robots; } $sc_robots_string = $this->get_robots_string(); if ( empty( $sc_robots_string ) ) { return $wp_robots; } $sc_robots = explode( ',', $sc_robots_string ); foreach ( $sc_robots as $directive ) { $wp_robots[ $directive ] = true; } return $wp_robots; } /** * Outputs meta description. * * @return bool */ private function smartcrawl_metadesc() { if ( is_admin() ) { return false; } $entity = $this->get_queried_entity(); $metadesc = $entity ? $entity->get_meta_description() : ''; $metadesc = wp_kses( strip_tags( stripslashes( $metadesc ) ), // phpcs:ignore array(), array() ); if ( ! empty( $metadesc ) ) { echo '' . "\n"; } return true; } /** * Gets (custom) meta tags for output. * * @return array */ public function get_meta_tags() { // Sitemap options are shown on the settings page so the decision to fallback should be made after checking. // if Settings::TAB_SETTINGS is allowed. // This logic follows the pattern used in Smartcrawl_Settings._populate_options. $smartcrawl_options = get_option( Settings::TAB_SITEMAP . '_options', array() ); $metas = array(); $include_verifications = (bool) ( empty( $smartcrawl_options['verification-pages'] ) || ( ! empty( $smartcrawl_options['verification-pages'] ) && 'home' === $smartcrawl_options['verification-pages'] && is_front_page() ) ); // Full meta overrides. if ( ! empty( $smartcrawl_options['verification-google-meta'] ) && $include_verifications ) { $metas['google'] = $smartcrawl_options['verification-google-meta']; } if ( ! empty( $smartcrawl_options['verification-bing-meta'] ) && $include_verifications ) { $metas['bing'] = $smartcrawl_options['verification-bing-meta']; } $additional = ! empty( $smartcrawl_options['additional-metas'] ) ? $smartcrawl_options['additional-metas'] : array(); if ( ! is_array( $additional ) ) { $additional = array(); } foreach ( $additional as $meta ) { $metas[] = $meta; } return $metas; } /** * Force rewrite title. * * @return bool */ private function force_rewrite_title() { return \smartcrawl_is_switch_active( '\SMARTCRAWL_FORCE_REWRITE_TITLE' ); } /** * Output head start items. * * @return void */ private function head_start() { $is_white_label = \smartcrawl_is_switch_active( 'SMARTCRAWL_WHITELABEL_ON' ) || White_Label::get()->is_hide_wpmudev_branding(); if ( ! $is_white_label ) { $project = defined( 'SMARTCRAWL_PROJECT_TITLE' ) ? SMARTCRAWL_PROJECT_TITLE : 'SmartCrawl'; echo '\n"; } } /** * Output to head end. * * @return void */ private function head_end() { do_action( 'wds_head-after_output' ); // phpcs:ignore if ( ! \smartcrawl_is_switch_active( 'SMARTCRAWL_WHITELABEL_ON' ) ) { echo "\n"; } } /** * Print meta tags. * * @return void */ private function print_meta_tags() { $metas = $this->get_meta_tags(); foreach ( $metas as $meta ) { $this->print_html_tag( "{$meta}\n" ); } } /** * Check if robots processing is disabled. * * @return bool */ private function robots_processing_disabled() { return ! apply_filters( 'wds_process_robots', true ); } /** * Check if robots API is available. * * @return bool */ private function wp_robots_api_available() { global $wp_version; return version_compare( $wp_version, '5.7', '>=' ); } /** * Check if blog is public. * * @return bool */ private function is_blog_public() { return 1 === (int) get_option( 'blog_public' ); } }