output built from them. * * @package ACSSEO */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Title, meta description, canonical URL and robots directives. */ class ACSSEO_Meta { /** * Singleton instance. * * @var ACSSEO_Meta|null */ private static $instance = null; /** * Return the shared instance. * * @return ACSSEO_Meta */ public static function instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Hook everything up. */ private function __construct() { add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) ); add_action( 'save_post', array( $this, 'save' ), 10, 2 ); add_filter( 'pre_get_document_title', array( $this, 'filter_document_title' ), 20 ); add_action( 'wp_head', array( $this, 'output_head' ), 2 ); } /** * Post types that get the SEO meta box. * * @return string[] */ public static function supported_post_types() { $types = get_post_types( array( 'public' => true, ), 'names' ); unset( $types['attachment'] ); /** * Filter the post types that receive the ACS SEO Plus meta box. * * @param string[] $types Post type names. */ return (array) apply_filters( 'acsseo_supported_post_types', array_values( $types ) ); } /** * Register the meta box on every supported post type. * * @return void */ public function add_meta_box() { foreach ( self::supported_post_types() as $post_type ) { add_meta_box( 'acsseo_meta_box', __( 'SEO', 'acs-seo-plus' ), array( $this, 'render_meta_box' ), $post_type, 'normal', 'high' ); } } /** * Render the meta box. * * @param WP_Post $post Current post. * @return void */ public function render_meta_box( $post ) { wp_nonce_field( 'acsseo_save_meta', 'acsseo_meta_nonce' ); $title = (string) get_post_meta( $post->ID, '_acsseo_title', true ); $description = (string) get_post_meta( $post->ID, '_acsseo_description', true ); $canonical = (string) get_post_meta( $post->ID, '_acsseo_canonical', true ); $noindex = (string) get_post_meta( $post->ID, '_acsseo_noindex', true ); $nofollow = (string) get_post_meta( $post->ID, '_acsseo_nofollow', true ); $schema = (string) get_post_meta( $post->ID, '_acsseo_schema', true ); $social = (string) get_post_meta( $post->ID, '_acsseo_social_image', true ); ?>



post_type ) { return; } $title = isset( $_POST['acsseo_title'] ) ? sanitize_text_field( wp_unslash( $_POST['acsseo_title'] ) ) : ''; $this->update_or_delete( $post_id, '_acsseo_title', $title ); $description = isset( $_POST['acsseo_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['acsseo_description'] ) ) : ''; $this->update_or_delete( $post_id, '_acsseo_description', $description ); $canonical = isset( $_POST['acsseo_canonical'] ) ? esc_url_raw( wp_unslash( $_POST['acsseo_canonical'] ) ) : ''; $this->update_or_delete( $post_id, '_acsseo_canonical', $canonical ); $social = isset( $_POST['acsseo_social_image'] ) ? esc_url_raw( wp_unslash( $_POST['acsseo_social_image'] ) ) : ''; $this->update_or_delete( $post_id, '_acsseo_social_image', $social ); foreach ( array( 'noindex', 'nofollow', 'schema' ) as $flag ) { $value = ( isset( $_POST[ 'acsseo_' . $flag ] ) && '1' === $_POST[ 'acsseo_' . $flag ] ) ? '1' : ''; $this->update_or_delete( $post_id, '_acsseo_' . $flag, $value ); } } /** * Store a value, or remove the meta row when the value is empty. * * @param int $post_id Post ID. * @param string $key Meta key. * @param string $value Sanitized value. * @return void */ private function update_or_delete( $post_id, $key, $value ) { if ( '' === $value ) { delete_post_meta( $post_id, $key ); return; } update_post_meta( $post_id, $key, $value ); } /** * Replace the document title with the stored SEO title. * * Returning a non empty string here short circuits wp_get_document_title(), * which is why the plugin never prints a second element. * * @param string $title Title supplied so far. * @return string */ public function filter_document_title( $title ) { if ( ! ACSSEO_Settings::get( 'output_title' ) ) { return $title; } if ( is_front_page() ) { $home_title = (string) ACSSEO_Settings::get( 'home_title', '' ); return '' !== $home_title ? $home_title : $title; } if ( ! is_singular() ) { return $title; } $post_id = get_queried_object_id(); if ( ! $post_id ) { return $title; } $custom = (string) get_post_meta( $post_id, '_acsseo_title', true ); return '' !== $custom ? $custom : $title; } /** * Resolve the description used for meta description and social tags. * * @param int $post_id Post ID. * @return string */ public static function resolve_description( $post_id ) { $description = (string) get_post_meta( $post_id, '_acsseo_description', true ); if ( '' !== $description ) { return $description; } $post = get_post( $post_id ); if ( ! $post instanceof WP_Post ) { return ''; } $excerpt = has_excerpt( $post ) ? $post->post_excerpt : $post->post_content; $excerpt = wp_strip_all_tags( strip_shortcodes( $excerpt ), true ); if ( '' === trim( $excerpt ) ) { return ''; } if ( mb_strlen( $excerpt ) > 160 ) { $excerpt = mb_substr( $excerpt, 0, 157 ) . '...'; } return $excerpt; } /** * Print meta description, robots and canonical tags. * * @return void */ public function output_head() { if ( is_front_page() && ACSSEO_Settings::get( 'output_description' ) ) { $home_description = (string) ACSSEO_Settings::get( 'home_description', '' ); if ( '' !== $home_description ) { echo '<meta name="description" content="' . esc_attr( $home_description ) . '" />' . "\n"; if ( ! is_singular() ) { return; } $this->output_robots_and_canonical( get_queried_object_id() ); return; } } if ( ! is_singular() ) { return; } $post_id = get_queried_object_id(); if ( ! $post_id ) { return; } if ( ACSSEO_Settings::get( 'output_description' ) ) { $description = self::resolve_description( $post_id ); if ( '' !== $description ) { echo '<meta name="description" content="' . esc_attr( $description ) . '" />' . "\n"; } } $this->output_robots_and_canonical( $post_id ); } /** * Print the robots and canonical tags of one entry. * * @param int $post_id Post ID. * @return void */ private function output_robots_and_canonical( $post_id ) { if ( ! $post_id ) { return; } if ( ACSSEO_Settings::get( 'output_robots' ) ) { $directives = array(); if ( '1' === (string) get_post_meta( $post_id, '_acsseo_noindex', true ) ) { $directives[] = 'noindex'; } if ( '1' === (string) get_post_meta( $post_id, '_acsseo_nofollow', true ) ) { $directives[] = 'nofollow'; } if ( ! empty( $directives ) ) { echo '<meta name="robots" content="' . esc_attr( implode( ', ', $directives ) ) . '" />' . "\n"; } } if ( ACSSEO_Settings::get( 'output_canonical' ) ) { $canonical = (string) get_post_meta( $post_id, '_acsseo_canonical', true ); if ( '' !== $canonical ) { // Drop the core tag so the page carries exactly one canonical URL. remove_action( 'wp_head', 'rel_canonical' ); echo '<link rel="canonical" href="' . esc_url( $canonical ) . '" />' . "\n"; } } } }