PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Represents the classifier for determine the type of the currently opened page.
*/
class WPSEO_Frontend_Page_Type {
/**
* Checks if the currently opened page is a simple page.
*
* @return bool Whether the currently opened page is a simple page.
*/
public function is_simple_page() {
return $this->get_simple_page_id() > 0;
}
/**
* Returns the id of the currently opened page.
*
* @return int The id of the currently opened page.
*/
public function get_simple_page_id() {
if ( is_singular() ) {
return get_the_ID();
}
if ( $this->is_posts_page() ) {
return get_option( 'page_for_posts' );
}
/**
* Filter: Allow changing the default page id.
*
* @api int $page_id The default page id.
*/
return apply_filters( 'wpseo_frontend_page_type_simple_page_id', 0 );
}
/**
* Determine whether this is the homepage and shows posts.
*
* @return bool Whether or not the current page is the homepage that displays posts.
*/
public function is_home_posts_page() {
return ( is_home() && get_option( 'show_on_front' ) === 'posts' );
}
/**
* Determine whether this is the static frontpage.
*
* @return bool Whether or not the current page is a static frontpage.
*/
public function is_home_static_page() {
return ( is_front_page() && get_option( 'show_on_front' ) === 'page' && is_page( get_option( 'page_on_front' ) ) );
}
/**
* Determine whether this is the statically set posts page, when it's not the frontpage.
*
* @return bool Whether or not it's a non-frontpage, statically set posts page.
*/
public function is_posts_page() {
return ( is_home() && get_option( 'show_on_front' ) === 'page' );
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,92 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Class WPSEO_Handle_404
*
* Handles intercepting requests.
*
* @since 9.4
*/
class WPSEO_Handle_404 implements WPSEO_WordPress_Integration {
/**
* Registers all hooks to WordPress.
*
* @return void
*/
public function register_hooks() {
add_filter( 'pre_handle_404', array( $this, 'handle_404' ) );
}
/**
* Handle the 404 status code.
*
* @param bool $handled Whether we've handled the request.
*
* @return bool True if it's 404.
*/
public function handle_404( $handled ) {
if ( is_feed() ) {
return $this->is_feed_404( $handled );
}
return $handled;
}
/**
* If there are no posts in a feed, make it 404 instead of sending an empty RSS feed.
*
* @global WP_Query $wp_query
*
* @param bool $handled Whether we've handled the request.
*
* @return bool True if it's 404.
*/
protected function is_feed_404( $handled ) {
global $wp_query;
// Don't 404 if the query contains post(s) or an object.
if ( $wp_query->posts || $wp_query->get_queried_object() ) {
return $handled;
}
// Don't 404 if it isn't archive or singular.
if ( ! $wp_query->is_archive() && ! $wp_query->is_singular() ) {
return $handled;
}
$wp_query->is_feed = false;
$this->set_404();
add_filter( 'old_slug_redirect_url', '__return_false' );
add_filter( 'redirect_canonical', '__return_false' );
return true;
}
/**
* Sets the 404 status code.
*
* @global WP_Query $wp_query
*
* @return void
*/
private function set_404() {
global $wp_query;
// Overwrite Content-Type header.
if ( ! headers_sent() ) {
header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
}
$wp_query->set_404();
status_header( 404 );
nocache_headers();
}
}

View File

@ -0,0 +1,707 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Class WPSEO_OpenGraph_Image.
*/
class WPSEO_OpenGraph_Image {
/**
* @var string
*/
const EXTERNAL_IMAGE_ID = '-1';
/**
* Holds the images that have been put out as OG image.
*
* @var array
*/
protected $images = array();
/**
* Holds the WPSEO_OpenGraph instance, so we can call og_tag.
*
* @var WPSEO_OpenGraph
*/
private $opengraph;
/**
* Holds the WPSEO_Frontend_Page_Type instance.
*
* @var WPSEO_Frontend_Page_Type
*/
private $frontend_page_type;
/**
* Image tags that we output for each image.
*
* @var array
*/
private $image_tags = array(
'width' => 'width',
'height' => 'height',
'mime-type' => 'type',
);
/**
* The parameters we have for Facebook images.
*
* @var array
*/
private $image_params = array(
'min_width' => 200,
'max_width' => 2000,
'min_height' => 200,
'max_height' => 2000,
);
/**
* Image types that are supported by OpenGraph.
*
* @var array
*/
private $valid_image_types = array( 'image/jpeg', 'image/gif', 'image/png' );
/**
* Image extensions that are supported by OpenGraph.
*
* @var array
*/
private $valid_image_extensions = array( 'jpeg', 'jpg', 'gif', 'png' );
/**
* Constructor.
*
* @param null|string $image Optional. The Image to use.
* @param WPSEO_OpenGraph $opengraph Optional. The OpenGraph object.
*/
public function __construct( $image = null, WPSEO_OpenGraph $opengraph = null ) {
if ( $opengraph === null ) {
global $wpseo_og;
// Use the global if available.
if ( empty( $wpseo_og ) ) {
$wpseo_og = new WPSEO_OpenGraph();
}
$opengraph = $wpseo_og;
}
$this->opengraph = $opengraph;
if ( ! empty( $image ) && is_string( $image ) ) {
$this->add_image_by_url( $image );
}
if ( ! post_password_required() ) {
$this->set_images();
}
}
/**
* Gets the class for determine the current page type.
*
* @return WPSEO_Frontend_Page_Type
*/
protected function get_frontend_page_type() {
if ( ! isset( $this->frontend_page_type ) ) {
$this->frontend_page_type = new WPSEO_Frontend_Page_Type();
}
return $this->frontend_page_type;
}
/**
* Outputs the images.
*
* @return void
*/
public function show() {
foreach ( $this->get_images() as $image => $image_meta ) {
$this->og_image_tag( $image );
$this->show_image_meta( $image_meta );
}
}
/**
* Output the image metadata.
*
* @param array $image_meta Image meta data to output.
*
* @return void
*/
private function show_image_meta( $image_meta ) {
foreach ( $this->image_tags as $key => $value ) {
if ( ! empty( $image_meta[ $key ] ) ) {
$this->opengraph->og_tag( 'og:image:' . $key, $image_meta[ $key ] );
}
}
}
/**
* Outputs an image tag based on whether it's https or not.
*
* @param string $image_url The image URL.
*
* @return void
*/
private function og_image_tag( $image_url ) {
$this->opengraph->og_tag( 'og:image', esc_url( $image_url ) );
// Add secure URL if detected. Not all services implement this, so the regular one also needs to be rendered.
if ( strpos( $image_url, 'https://' ) === 0 ) {
$this->opengraph->og_tag( 'og:image:secure_url', esc_url( $image_url ) );
}
}
/**
* Return the images array.
*
* @return array The images.
*/
public function get_images() {
return $this->images;
}
/**
* Check whether we have images or not.
*
* @return bool True if we have images, false if we don't.
*/
public function has_images() {
return ! empty( $this->images );
}
/**
* Display an OpenGraph image tag.
*
* @param string|array $attachment Attachment array.
*
* @return void
*/
public function add_image( $attachment ) {
// In the past `add_image` accepted an image url, so leave this for backwards compatibility.
if ( is_string( $attachment ) ) {
$attachment = array( 'url' => $attachment );
}
if ( ! is_array( $attachment ) || empty( $attachment['url'] ) ) {
return;
}
// If the URL ends in `.svg`, we need to return.
if ( ! $this->is_valid_image_url( $attachment['url'] ) ) {
return;
}
/**
* Filter: 'wpseo_opengraph_image' - Allow changing the OpenGraph image.
*
* @api string - The URL of the OpenGraph image.
*/
$image_url = trim( apply_filters( 'wpseo_opengraph_image', $attachment['url'] ) );
if ( empty( $image_url ) ) {
return;
}
if ( WPSEO_Utils::is_url_relative( $image_url ) === true ) {
$image_url = WPSEO_Image_Utils::get_relative_path( $image_url );
}
if ( array_key_exists( $image_url, $this->images ) ) {
return;
}
$this->images[ $image_url ] = $attachment;
}
/**
* Adds an image by ID if possible and by URL if the ID isn't present.
*
* @param string $image_id The image ID as set in the database.
* @param string $image_url The saved URL for the image.
* @param callable $on_save_id Function to call to save the ID if it needs to be saved.
*
* @return void
*/
private function add_image_by_id_or_url( $image_id, $image_url, $on_save_id ) {
switch ( $image_id ) {
case self::EXTERNAL_IMAGE_ID:
// Add image by URL, but skip attachment_to_id call. We already know this is an external image.
$this->add_image( array( 'url' => $image_url ) );
break;
case '':
// Add image by URL, try to save the ID afterwards. So we can use the ID the next time.
$attachment_id = $this->add_image_by_url( $image_url );
if ( $attachment_id !== null ) {
call_user_func( $on_save_id, $attachment_id );
}
break;
default:
// Add the image by ID. This is our ideal scenario.
$this->add_image_by_id( $image_id );
break;
}
}
/**
* Saves the ID to the frontpage Open Graph image ID.
*
* @param string $attachment_id The ID to save.
*
* @return void
*/
private function save_frontpage_image_id( $attachment_id ) {
WPSEO_Options::set( 'og_frontpage_image_id', $attachment_id );
}
/**
* If the frontpage image exists, call add_image.
*
* @return void
*/
private function set_front_page_image() {
if ( get_option( 'show_on_front' ) === 'page' ) {
$this->set_user_defined_image();
// Don't fall back to the frontpage image below, as that's not set for this situation, so we should fall back to the default image.
return;
}
$frontpage_image_url = WPSEO_Options::get( 'og_frontpage_image' );
$frontpage_image_id = WPSEO_Options::get( 'og_frontpage_image_id' );
$this->add_image_by_id_or_url( $frontpage_image_id, $frontpage_image_url, array( $this, 'save_frontpage_image_id' ) );
}
/**
* Get the images of the posts page.
*
* @return void
*/
private function set_posts_page_image() {
$post_id = get_option( 'page_for_posts' );
$this->set_image_post_meta( $post_id );
if ( $this->has_images() ) {
return;
}
$this->set_featured_image( $post_id );
}
/**
* Get the images of the singular post.
*
* @param null|int $post_id The post id to get the images for.
*
* @return void
*/
private function set_singular_image( $post_id = null ) {
if ( $post_id === null ) {
$post_id = $this->get_post_id();
}
$this->set_user_defined_image( $post_id );
if ( $this->has_images() ) {
return;
}
$this->add_first_usable_content_image( get_post( $post_id ) );
}
/**
* Gets the user-defined image of the post.
*
* @param null|int $post_id The post id to get the images for.
*
* @return void
*/
private function set_user_defined_image( $post_id = null ) {
if ( $post_id === null ) {
$post_id = $this->get_post_id();
}
$this->set_image_post_meta( $post_id );
if ( $this->has_images() ) {
return;
}
$this->set_featured_image( $post_id );
}
/**
* Saves the default image ID for Open Graph images to the database.
*
* @param string $attachment_id The ID to save.
*
* @return void
*/
private function save_default_image_id( $attachment_id ) {
WPSEO_Options::set( 'og_default_image_id', $attachment_id );
}
/**
* Get default image and call add_image.
*
* @return void
*/
private function maybe_set_default_image() {
if ( $this->has_images() ) {
return;
}
$default_image_url = WPSEO_Options::get( 'og_default_image', '' );
$default_image_id = WPSEO_Options::get( 'og_default_image_id', '' );
if ( $default_image_url === '' && $default_image_id === '' ) {
return;
}
$this->add_image_by_id_or_url( $default_image_id, $default_image_url, array( $this, 'save_default_image_id' ) );
}
/**
* Saves the Open Graph image meta to the database for the current post.
*
* @param string $attachment_id The ID to save.
*
* @return void
*/
private function save_opengraph_image_id_meta( $attachment_id ) {
$post_id = $this->get_post_id();
WPSEO_Meta::set_value( 'opengraph-image-id', (string) $attachment_id, $post_id );
}
/**
* If opengraph-image is set, call add_image and return true.
*
* @param int $post_id Optional post ID to use.
*
* @return void
*/
private function set_image_post_meta( $post_id = 0 ) {
$image_id = WPSEO_Meta::get_value( 'opengraph-image-id', $post_id );
$image_url = WPSEO_Meta::get_value( 'opengraph-image', $post_id );
$this->add_image_by_id_or_url( $image_id, $image_url, array( $this, 'save_opengraph_image_id_meta' ) );
}
/**
* Check if taxonomy has an image and add this image.
*
* @return void
*/
private function set_taxonomy_image() {
$image_url = WPSEO_Taxonomy_Meta::get_meta_without_term( 'opengraph-image' );
$this->add_image_by_url( $image_url );
}
/**
* If there is a featured image, check image size. If image size is correct, call add_image and return true.
*
* @param int $post_id The post ID.
*
* @return void
*/
private function set_featured_image( $post_id ) {
if ( has_post_thumbnail( $post_id ) ) {
$attachment_id = get_post_thumbnail_id( $post_id );
$this->add_image_by_id( $attachment_id );
}
}
/**
* If this is an attachment page, call add_image with the attachment.
*
* @return void
*/
private function set_attachment_page_image() {
$post_id = $this->get_post_id();
if ( wp_attachment_is_image( $post_id ) ) {
$this->add_image_by_id( $post_id );
}
}
/**
* Adds an image based on a given URL, and attempts to be smart about it.
*
* @param string $url The given URL.
*
* @return null|number Returns the found attachment ID if it exists. Otherwise -1.
* If the URL is empty we return null.
*/
public function add_image_by_url( $url ) {
if ( empty( $url ) ) {
return null;
}
$attachment_id = WPSEO_Image_Utils::get_attachment_by_url( $url );
if ( $attachment_id > 0 ) {
$this->add_image_by_id( $attachment_id );
return $attachment_id;
}
$this->add_image( array( 'url' => $url ) );
return -1;
}
/**
* Returns the overridden image size if it has been overridden.
*
* @return null|string The overridden image size or null.
*/
protected function get_overridden_image_size() {
/**
* Filter: 'wpseo_opengraph_image_size' - Allow overriding the image size used
* for OpenGraph sharing. If this filter is used, the defined size will always be
* used for the og:image. The image will still be rejected if it is too small.
*
* Only use this filter if you manually want to determine the best image size
* for the `og:image` tag.
*
* Use the `wpseo_image_sizes` filter if you want to use our logic. That filter
* can be used to add an image size that needs to be taken into consideration
* within our own logic.
*
* @api string $size Size string.
*/
return apply_filters( 'wpseo_opengraph_image_size', null );
}
/**
* Determines if the OpenGraph image size should overridden.
*
* @return bool Whether the size should be overridden.
*/
protected function is_size_overridden() {
return $this->get_overridden_image_size() !== null;
}
/**
* Adds the possibility to short-circuit all the optimal variation logic with
* your own size.
*
* @param int $attachment_id The attachment ID that is used.
*
* @return void
*/
protected function get_overridden_image( $attachment_id ) {
$attachment = WPSEO_Image_Utils::get_image( $attachment_id, $this->get_overridden_image_size() );
if ( $attachment ) {
$this->add_image( $attachment );
}
}
/**
* Adds an image to the list by attachment ID.
*
* @param int $attachment_id The attachment ID to add.
*
* @return void
*/
public function add_image_by_id( $attachment_id ) {
if ( ! $this->is_valid_attachment( $attachment_id ) ) {
return;
}
if ( $this->is_size_overridden() ) {
$this->get_overridden_image( $attachment_id );
return;
}
$variations = WPSEO_Image_Utils::get_variations( $attachment_id );
$variations = WPSEO_Image_Utils::filter_usable_dimensions( $this->image_params, $variations );
$variations = WPSEO_Image_Utils::filter_usable_file_size( $variations );
// If we are left without variations, there is no valid variation for this attachment.
if ( empty( $variations ) ) {
return;
}
// The variations are ordered so the first variations is by definition the best one.
$attachment = $variations[0];
if ( $attachment ) {
$this->add_image( $attachment );
}
}
/**
* Sets the images based on the page type.
*
* @return void
*/
protected function set_images() {
/**
* Filter: wpseo_add_opengraph_images - Allow developers to add images to the OpenGraph tags.
*
* @api WPSEO_OpenGraph_Image The current object.
*/
do_action( 'wpseo_add_opengraph_images', $this );
switch ( true ) {
case is_front_page():
$this->set_front_page_image();
break;
case is_home():
$this->set_posts_page_image();
break;
case is_attachment():
$this->set_attachment_page_image();
break;
case $this->get_frontend_page_type()->is_simple_page():
$this->set_singular_image();
break;
case is_category():
case is_tag():
case is_tax():
$this->set_taxonomy_image();
}
/**
* Filter: wpseo_add_opengraph_additional_images - Allows to add additional images to the OpenGraph tags.
*
* @api WPSEO_OpenGraph_Image The current object.
*/
do_action( 'wpseo_add_opengraph_additional_images', $this );
$this->maybe_set_default_image();
}
/**
* Determines whether or not the wanted attachment is considered valid.
*
* @param int $attachment_id The attachment ID to get the attachment by.
*
* @return bool Whether or not the attachment is valid.
*/
protected function is_valid_attachment( $attachment_id ) {
$attachment = get_post_mime_type( $attachment_id );
if ( $attachment === false ) {
return false;
}
return $this->is_valid_image_type( $attachment );
}
/**
* Determines whether the passed mime type is a valid image type.
*
* @param string $mime_type The detected mime type.
*
* @return bool Whether or not the attachment is a valid image type.
*/
protected function is_valid_image_type( $mime_type ) {
return in_array( $mime_type, $this->valid_image_types, true );
}
/**
* Determines whether the passed URL is considered valid.
*
* @param string $url The URL to check.
*
* @return bool Whether or not the URL is a valid image.
*/
protected function is_valid_image_url( $url ) {
if ( ! is_string( $url ) ) {
return false;
}
$image_extension = $this->get_extension_from_url( $url );
$is_valid = in_array( $image_extension, $this->valid_image_extensions, true );
/**
* Filter: 'wpseo_opengraph_is_valid_image_url' - Allows extra validation for an image url.
*
* @api bool - Current validation result.
*
* @param string $url The image url to validate.
*/
return apply_filters( 'wpseo_opengraph_is_valid_image_url', $is_valid, $url );
}
/**
* Gets the image path from the passed URL.
*
* @param string $url The URL to get the path from.
*
* @return string The path of the image URL. Returns an empty string if URL parsing fails.
*/
protected function get_image_url_path( $url ) {
return (string) wp_parse_url( $url, PHP_URL_PATH );
}
/**
* Determines the file extension of the passed URL.
*
* @param string $url The URL.
*
* @return string The extension.
*/
protected function get_extension_from_url( $url ) {
$extension = '';
$path = $this->get_image_url_path( $url );
if ( $path === '' ) {
return $extension;
}
$parts = explode( '.', $path );
if ( ! empty( $parts ) ) {
$extension = end( $parts );
}
return $extension;
}
/**
* Gets the post ID.
*
* @return int The post ID.
*/
protected function get_post_id() {
return $this->get_frontend_page_type()->get_simple_page_id();
}
/**
* Adds the first usable attachment image from the post content.
*
* @param WP_Post $post The post object.
*
* @return void
*/
private function add_first_usable_content_image( $post ) {
$image_finder = new WPSEO_Content_Images();
$images = $image_finder->get_images( $post->ID, $post );
if ( ! is_array( $images ) || $images === array() ) {
return;
}
$image_url = reset( $images );
if ( ! $image_url ) {
return;
}
$this->add_image( array( 'url' => $image_url ) );
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Class WPSEO_OpenGraph_OEmbed.
*/
class WPSEO_OpenGraph_OEmbed implements WPSEO_WordPress_Integration {
/**
* Registers the hooks.
*/
public function register_hooks() {
// Check to make sure opengraph is enabled before adding filter.
if ( WPSEO_Options::get( 'opengraph' ) ) {
add_filter( 'oembed_response_data', array( $this, 'set_oembed_data' ), 10, 2 );
}
}
/**
* Callback function to pass to the oEmbed's response data that will enable
* support for using the image and title set by the WordPress SEO plugin's fields. This
* address the concern where some social channels/subscribed use oEmebed data over OpenGraph data
* if both are present.
*
* @param array $data The oEmbed data.
* @param WP_Post $post The current Post object.
*
* @link https://developer.wordpress.org/reference/hooks/oembed_response_data/ for hook info.
*
* @return array $filter_data - An array of oEmbed data with modified values where appropriate.
*/
public function set_oembed_data( $data, $post ) {
// Data to be returned.
$filter_data = $data;
// Look for the Yoast meta values (image and title)...
$opengraph_title = WPSEO_Meta::get_value( 'opengraph-title', $post->ID );
$opengraph_image = WPSEO_Meta::get_value( 'opengraph-image', $post->ID );
// If yoast has a title set, update oEmbed with Yoast's title.
if ( ! empty( $opengraph_title ) ) {
$filter_data['title'] = $opengraph_title;
}
// If WPSEO Image was _not_ set, return the `$filter_data` as it currently is.
if ( empty( $opengraph_image ) ) {
return $filter_data;
}
// Since the a WPSEO Image was set, update the oEmbed data with the Yoast Image's info.
// Get the image's ID from a URL.
$image_id = WPSEO_Image_Utils::get_attachment_by_url( $opengraph_image );
// Get the image's info from it's ID.
$image_info = wp_get_attachment_metadata( $image_id );
// Update the oEmbed data.
$filter_data['thumbnail_url'] = $opengraph_image;
if ( ! empty( $image_info['height'] ) ) {
$filter_data['thumbnail_height'] = $image_info['height'];
}
if ( ! empty( $image_info['width'] ) ) {
$filter_data['thumbnail_width'] = $image_info['width'];
}
return $filter_data;
}
}

View File

@ -0,0 +1,806 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* This code adds the OpenGraph output.
*/
class WPSEO_OpenGraph {
/**
* @var WPSEO_Frontend_Page_Type
*/
protected $frontend_page_type;
/**
* Class constructor.
*/
public function __construct() {
if ( isset( $GLOBALS['fb_ver'] ) || class_exists( 'Facebook_Loader', false ) ) {
add_filter( 'fb_meta_tags', array( $this, 'facebook_filter' ), 10, 1 );
}
else {
add_filter( 'language_attributes', array( $this, 'add_opengraph_namespace' ), 15 );
add_action( 'wpseo_opengraph', array( $this, 'locale' ), 1 );
add_action( 'wpseo_opengraph', array( $this, 'type' ), 5 );
add_action( 'wpseo_opengraph', array( $this, 'og_title' ), 10 );
add_action( 'wpseo_opengraph', array( $this, 'app_id' ), 20 );
add_action( 'wpseo_opengraph', array( $this, 'description' ), 11 );
add_action( 'wpseo_opengraph', array( $this, 'url' ), 12 );
add_action( 'wpseo_opengraph', array( $this, 'site_name' ), 13 );
add_action( 'wpseo_opengraph', array( $this, 'website_facebook' ), 14 );
if ( is_singular() && ! is_front_page() ) {
add_action( 'wpseo_opengraph', array( $this, 'article_author_facebook' ), 15 );
add_action( 'wpseo_opengraph', array( $this, 'tags' ), 16 );
add_action( 'wpseo_opengraph', array( $this, 'category' ), 17 );
add_action( 'wpseo_opengraph', array( $this, 'publish_date' ), 19 );
}
add_action( 'wpseo_opengraph', array( $this, 'image' ), 30 );
}
add_filter( 'jetpack_enable_open_graph', '__return_false' );
add_action( 'wpseo_head', array( $this, 'opengraph' ), 30 );
// Class for determine the current page type.
$this->frontend_page_type = new WPSEO_Frontend_Page_Type();
}
/**
* Main OpenGraph output.
*/
public function opengraph() {
wp_reset_query();
/**
* Action: 'wpseo_opengraph' - Hook to add all Facebook OpenGraph output to so they're close together.
*/
do_action( 'wpseo_opengraph' );
}
/**
* Internal function to output FB tags. This also adds an output filter to each bit of output based on the property.
*
* @param string $property Property attribute value.
* @param string $content Content attribute value.
*
* @return boolean
*/
public function og_tag( $property, $content ) {
$og_property = str_replace( ':', '_', $property );
/**
* Filter: 'wpseo_og_' . $og_property - Allow developers to change the content of specific OG meta tags.
*
* @api string $content The content of the property.
*/
$content = apply_filters( 'wpseo_og_' . $og_property, $content );
if ( empty( $content ) ) {
return false;
}
echo '<meta property="', esc_attr( $property ), '" content="', esc_attr( $content ), '" />', "\n";
return true;
}
/**
* Filter the Facebook plugins metadata.
*
* @param array $meta_tags The array to fix.
*
* @return array $meta_tags
*/
public function facebook_filter( $meta_tags ) {
$meta_tags['http://ogp.me/ns#type'] = $this->type( false );
$meta_tags['http://ogp.me/ns#title'] = $this->og_title( false );
// Filter the locale too because the Facebook plugin locale code is not as good as ours.
$meta_tags['http://ogp.me/ns#locale'] = $this->locale( false );
$ogdesc = $this->description( false );
if ( ! empty( $ogdesc ) ) {
$meta_tags['http://ogp.me/ns#description'] = $ogdesc;
}
return $meta_tags;
}
/**
* Filter for the namespace, adding the OpenGraph namespace.
*
* @link https://developers.facebook.com/docs/web/tutorials/scrumptious/open-graph-object/
*
* @param string $input The input namespace string.
*
* @return string
*/
public function add_opengraph_namespace( $input ) {
$namespaces = array(
'og: http://ogp.me/ns#',
);
/**
* Allow for adding additional namespaces to the <html> prefix attributes.
*
* @since 3.9.0
*
* @param array $namespaces Currently registered namespaces which are to be
* added to the prefix attribute.
* Namespaces are strings and have the following syntax:
* ns: http://url.to.namespace/definition
*/
$namespaces = apply_filters( 'wpseo_html_namespaces', $namespaces );
$namespace_string = implode( ' ', array_unique( $namespaces ) );
if ( strpos( $input, ' prefix=' ) !== false ) {
$regex = '`prefix=([\'"])(.+?)\1`';
$replace = 'prefix="$2 ' . $namespace_string . '"';
$input = preg_replace( $regex, $replace, $input );
}
else {
$input .= ' prefix="' . $namespace_string . '"';
}
return $input;
}
/**
* Outputs the authors FB page.
*
* @link https://developers.facebook.com/blog/post/2013/06/19/platform-updates--new-open-graph-tags-for-media-publishers-and-more/
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
*
* @return boolean
*/
public function article_author_facebook() {
if ( ! is_singular() ) {
return false;
}
/**
* Filter: 'wpseo_opengraph_author_facebook' - Allow developers to filter the Yoast SEO post authors facebook profile URL.
*
* @api bool|string $unsigned The Facebook author URL, return false to disable.
*/
$facebook = apply_filters( 'wpseo_opengraph_author_facebook', get_the_author_meta( 'facebook', $GLOBALS['post']->post_author ) );
if ( $facebook && ( is_string( $facebook ) && $facebook !== '' ) ) {
$this->og_tag( 'article:author', $facebook );
return true;
}
return false;
}
/**
* Outputs the websites FB page.
*
* @link https://developers.facebook.com/blog/post/2013/06/19/platform-updates--new-open-graph-tags-for-media-publishers-and-more/
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
* @return boolean
*/
public function website_facebook() {
if ( 'article' === $this->type( false ) && WPSEO_Options::get( 'facebook_site', '' ) !== '' ) {
$this->og_tag( 'article:publisher', WPSEO_Options::get( 'facebook_site' ) );
return true;
}
return false;
}
/**
* Outputs the SEO title as OpenGraph title.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
*
* @param bool $echo Whether or not to echo the output.
*
* @return string|boolean
*/
public function og_title( $echo = true ) {
$frontend = WPSEO_Frontend::get_instance();
if ( $this->frontend_page_type->is_simple_page() ) {
$post_id = $this->frontend_page_type->get_simple_page_id();
$post = get_post( $post_id );
$title = WPSEO_Meta::get_value( 'opengraph-title', $post_id );
if ( $title === '' ) {
$title = $frontend->title( '' );
}
else {
// Replace Yoast SEO Variables.
$title = wpseo_replace_vars( $title, $post );
}
}
elseif ( is_front_page() ) {
$title = ( WPSEO_Options::get( 'og_frontpage_title', '' ) !== '' ) ? WPSEO_Options::get( 'og_frontpage_title' ) : $frontend->title( '' );
}
elseif ( is_category() || is_tax() || is_tag() ) {
$title = WPSEO_Taxonomy_Meta::get_meta_without_term( 'opengraph-title' );
if ( $title === '' ) {
$title = $frontend->title( '' );
}
else {
// Replace Yoast SEO Variables.
$title = wpseo_replace_vars( $title, $GLOBALS['wp_query']->get_queried_object() );
}
}
else {
$title = $frontend->title( '' );
}
/**
* Filter: 'wpseo_opengraph_title' - Allow changing the title specifically for OpenGraph.
*
* @api string $unsigned The title string.
*/
$title = trim( apply_filters( 'wpseo_opengraph_title', $title ) );
if ( is_string( $title ) && $title !== '' ) {
if ( $echo !== false ) {
$this->og_tag( 'og:title', $title );
return true;
}
}
if ( $echo === false ) {
return $title;
}
return false;
}
/**
* Outputs the canonical URL as OpenGraph URL, which consolidates likes and shares.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
* @return boolean
*/
public function url() {
$url = WPSEO_Frontend::get_instance()->canonical( false, false );
$unpaged_url = WPSEO_Frontend::get_instance()->canonical( false, true );
/*
* If the unpaged URL is the same as the normal URL but just with pagination added, use that.
* This makes sure we always use the unpaged URL when we can, but doesn't break for overridden canonicals.
*/
if ( is_string( $unpaged_url ) && strpos( $url, $unpaged_url ) === 0 ) {
$url = $unpaged_url;
}
/**
* Filter: 'wpseo_opengraph_url' - Allow changing the OpenGraph URL.
*
* @api string $unsigned Canonical URL.
*/
$url = apply_filters( 'wpseo_opengraph_url', $url );
if ( is_string( $url ) && $url !== '' ) {
$this->og_tag( 'og:url', esc_url( $url ) );
return true;
}
return false;
}
/**
* Output the locale, doing some conversions to make sure the proper Facebook locale is outputted.
*
* Last update/compare with FB list done on 2015-03-16 by Rarst.
*
* @link http://www.facebook.com/translations/FacebookLocales.xml for the list of supported locales.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
*
* @param bool $echo Whether to echo or return the locale.
*
* @return string $locale
*/
public function locale( $echo = true ) {
/**
* Filter: 'wpseo_locale' - Allow changing the locale output.
*
* @api string $unsigned Locale string.
*/
$locale = apply_filters( 'wpseo_locale', get_locale() );
// Catch some weird locales served out by WP that are not easily doubled up.
$fix_locales = array(
'ca' => 'ca_ES',
'en' => 'en_US',
'el' => 'el_GR',
'et' => 'et_EE',
'ja' => 'ja_JP',
'sq' => 'sq_AL',
'uk' => 'uk_UA',
'vi' => 'vi_VN',
'zh' => 'zh_CN',
);
if ( isset( $fix_locales[ $locale ] ) ) {
$locale = $fix_locales[ $locale ];
}
// Convert locales like "es" to "es_ES", in case that works for the given locale (sometimes it does).
if ( strlen( $locale ) === 2 ) {
$locale = strtolower( $locale ) . '_' . strtoupper( $locale );
}
// These are the locales FB supports.
$fb_valid_fb_locales = array(
'af_ZA', // Afrikaans.
'ak_GH', // Akan.
'am_ET', // Amharic.
'ar_AR', // Arabic.
'as_IN', // Assamese.
'ay_BO', // Aymara.
'az_AZ', // Azerbaijani.
'be_BY', // Belarusian.
'bg_BG', // Bulgarian.
'bp_IN', // Bhojpuri.
'bn_IN', // Bengali.
'br_FR', // Breton.
'bs_BA', // Bosnian.
'ca_ES', // Catalan.
'cb_IQ', // Sorani Kurdish.
'ck_US', // Cherokee.
'co_FR', // Corsican.
'cs_CZ', // Czech.
'cx_PH', // Cebuano.
'cy_GB', // Welsh.
'da_DK', // Danish.
'de_DE', // German.
'el_GR', // Greek.
'en_GB', // English (UK).
'en_PI', // English (Pirate).
'en_UD', // English (Upside Down).
'en_US', // English (US).
'em_ZM',
'eo_EO', // Esperanto.
'es_ES', // Spanish (Spain).
'es_LA', // Spanish.
'es_MX', // Spanish (Mexico).
'et_EE', // Estonian.
'eu_ES', // Basque.
'fa_IR', // Persian.
'fb_LT', // Leet Speak.
'ff_NG', // Fulah.
'fi_FI', // Finnish.
'fo_FO', // Faroese.
'fr_CA', // French (Canada).
'fr_FR', // French (France).
'fy_NL', // Frisian.
'ga_IE', // Irish.
'gl_ES', // Galician.
'gn_PY', // Guarani.
'gu_IN', // Gujarati.
'gx_GR', // Classical Greek.
'ha_NG', // Hausa.
'he_IL', // Hebrew.
'hi_IN', // Hindi.
'hr_HR', // Croatian.
'hu_HU', // Hungarian.
'ht_HT', // Haitian Creole.
'hy_AM', // Armenian.
'id_ID', // Indonesian.
'ig_NG', // Igbo.
'is_IS', // Icelandic.
'it_IT', // Italian.
'ik_US',
'iu_CA',
'ja_JP', // Japanese.
'ja_KS', // Japanese (Kansai).
'jv_ID', // Javanese.
'ka_GE', // Georgian.
'kk_KZ', // Kazakh.
'km_KH', // Khmer.
'kn_IN', // Kannada.
'ko_KR', // Korean.
'ks_IN', // Kashmiri.
'ku_TR', // Kurdish (Kurmanji).
'ky_KG', // Kyrgyz.
'la_VA', // Latin.
'lg_UG', // Ganda.
'li_NL', // Limburgish.
'ln_CD', // Lingala.
'lo_LA', // Lao.
'lt_LT', // Lithuanian.
'lv_LV', // Latvian.
'mg_MG', // Malagasy.
'mi_NZ', // Maori.
'mk_MK', // Macedonian.
'ml_IN', // Malayalam.
'mn_MN', // Mongolian.
'mr_IN', // Marathi.
'ms_MY', // Malay.
'mt_MT', // Maltese.
'my_MM', // Burmese.
'nb_NO', // Norwegian (bokmal).
'nd_ZW', // Ndebele.
'ne_NP', // Nepali.
'nl_BE', // Dutch (Belgie).
'nl_NL', // Dutch.
'nn_NO', // Norwegian (nynorsk).
'nr_ZA', // Southern Ndebele.
'ns_ZA', // Northern Sotho.
'ny_MW', // Chewa.
'om_ET', // Oromo.
'or_IN', // Oriya.
'pa_IN', // Punjabi.
'pl_PL', // Polish.
'ps_AF', // Pashto.
'pt_BR', // Portuguese (Brazil).
'pt_PT', // Portuguese (Portugal).
'qc_GT', // Quiché.
'qu_PE', // Quechua.
'qr_GR',
'qz_MM', // Burmese (Zawgyi).
'rm_CH', // Romansh.
'ro_RO', // Romanian.
'ru_RU', // Russian.
'rw_RW', // Kinyarwanda.
'sa_IN', // Sanskrit.
'sc_IT', // Sardinian.
'se_NO', // Northern Sami.
'si_LK', // Sinhala.
'su_ID', // Sundanese.
'sk_SK', // Slovak.
'sl_SI', // Slovenian.
'sn_ZW', // Shona.
'so_SO', // Somali.
'sq_AL', // Albanian.
'sr_RS', // Serbian.
'ss_SZ', // Swazi.
'st_ZA', // Southern Sotho.
'sv_SE', // Swedish.
'sw_KE', // Swahili.
'sy_SY', // Syriac.
'sz_PL', // Silesian.
'ta_IN', // Tamil.
'te_IN', // Telugu.
'tg_TJ', // Tajik.
'th_TH', // Thai.
'tk_TM', // Turkmen.
'tl_PH', // Filipino.
'tl_ST', // Klingon.
'tn_BW', // Tswana.
'tr_TR', // Turkish.
'ts_ZA', // Tsonga.
'tt_RU', // Tatar.
'tz_MA', // Tamazight.
'uk_UA', // Ukrainian.
'ur_PK', // Urdu.
'uz_UZ', // Uzbek.
've_ZA', // Venda.
'vi_VN', // Vietnamese.
'wo_SN', // Wolof.
'xh_ZA', // Xhosa.
'yi_DE', // Yiddish.
'yo_NG', // Yoruba.
'zh_CN', // Simplified Chinese (China).
'zh_HK', // Traditional Chinese (Hong Kong).
'zh_TW', // Traditional Chinese (Taiwan).
'zu_ZA', // Zulu.
'zz_TR', // Zazaki.
);
// Check to see if the locale is a valid FB one, if not, use en_US as a fallback.
if ( ! in_array( $locale, $fb_valid_fb_locales, true ) ) {
$locale = strtolower( substr( $locale, 0, 2 ) ) . '_' . strtoupper( substr( $locale, 0, 2 ) );
if ( ! in_array( $locale, $fb_valid_fb_locales, true ) ) {
$locale = 'en_US';
}
}
if ( $echo !== false ) {
$this->og_tag( 'og:locale', $locale );
}
return $locale;
}
/**
* Output the OpenGraph type.
*
* @param boolean $echo Whether to echo or return the type.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/object/
*
* @return string $type
*/
public function type( $echo = true ) {
if ( is_front_page() || is_home() ) {
$type = 'website';
}
elseif ( is_singular() ) {
// This'll usually only be changed by plugins right now.
$type = WPSEO_Meta::get_value( 'og_type' );
if ( $type === '' ) {
$type = 'article';
}
}
else {
// We use "object" for archives etc. as article doesn't apply there.
$type = 'object';
}
/**
* Filter: 'wpseo_opengraph_type' - Allow changing the OpenGraph type of the page.
*
* @api string $type The OpenGraph type string.
*/
$type = apply_filters( 'wpseo_opengraph_type', $type );
if ( is_string( $type ) && $type !== '' ) {
if ( $echo !== false ) {
$this->og_tag( 'og:type', $type );
}
else {
return $type;
}
}
return '';
}
/**
* Create new WPSEO_OpenGraph_Image class and get the images to set the og:image.
*
* @param string|bool $image Optional. Image URL.
*
* @return void
*/
public function image( $image = false ) {
$opengraph_image = new WPSEO_OpenGraph_Image( $image, $this );
$opengraph_image->show();
}
/**
* Output the OpenGraph description, specific OG description first, if not, grab the meta description.
*
* @param bool $echo Whether to echo or return the description.
*
* @return string $ogdesc
*/
public function description( $echo = true ) {
$ogdesc = '';
$frontend = WPSEO_Frontend::get_instance();
if ( is_front_page() ) {
if ( WPSEO_Options::get( 'og_frontpage_desc', '' ) !== '' ) {
$ogdesc = wpseo_replace_vars( WPSEO_Options::get( 'og_frontpage_desc' ), null );
}
else {
$ogdesc = $frontend->metadesc( false );
}
}
if ( $this->frontend_page_type->is_simple_page() ) {
$post_id = $this->frontend_page_type->get_simple_page_id();
$post = get_post( $post_id );
$ogdesc = WPSEO_Meta::get_value( 'opengraph-description', $post_id );
// Replace Yoast SEO Variables.
$ogdesc = wpseo_replace_vars( $ogdesc, $post );
// Use metadesc if $ogdesc is empty.
if ( $ogdesc === '' ) {
$ogdesc = $frontend->metadesc( false );
}
// Tag og:description is still blank so grab it from get_the_excerpt().
if ( ! is_string( $ogdesc ) || ( is_string( $ogdesc ) && $ogdesc === '' ) ) {
$ogdesc = str_replace( '[&hellip;]', '&hellip;', wp_strip_all_tags( get_the_excerpt() ) );
}
}
if ( is_author() ) {
$ogdesc = $frontend->metadesc( false );
}
if ( is_category() || is_tag() || is_tax() ) {
$ogdesc = WPSEO_Taxonomy_Meta::get_meta_without_term( 'opengraph-description' );
if ( $ogdesc === '' ) {
$ogdesc = $frontend->metadesc( false );
}
if ( $ogdesc === '' ) {
$ogdesc = wp_strip_all_tags( term_description() );
}
if ( $ogdesc === '' ) {
$ogdesc = WPSEO_Taxonomy_Meta::get_meta_without_term( 'desc' );
}
$ogdesc = wpseo_replace_vars( $ogdesc, get_queried_object() );
}
// Strip shortcodes if any.
$ogdesc = strip_shortcodes( $ogdesc );
/**
* Filter: 'wpseo_opengraph_desc' - Allow changing the OpenGraph description.
*
* @api string $ogdesc The description string.
*/
$ogdesc = trim( apply_filters( 'wpseo_opengraph_desc', $ogdesc ) );
if ( is_string( $ogdesc ) && $ogdesc !== '' ) {
if ( $echo !== false ) {
$this->og_tag( 'og:description', $ogdesc );
}
}
return $ogdesc;
}
/**
* Output the site name straight from the blog info.
*/
public function site_name() {
/**
* Filter: 'wpseo_opengraph_site_name' - Allow changing the OpenGraph site name.
*
* @api string $unsigned Blog name string.
*/
$name = apply_filters( 'wpseo_opengraph_site_name', get_bloginfo( 'name' ) );
if ( is_string( $name ) && $name !== '' ) {
$this->og_tag( 'og:site_name', $name );
}
}
/**
* Output the article tags as article:tag tags.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
* @return boolean
*/
public function tags() {
if ( ! is_singular() ) {
return false;
}
$tags = get_the_tags();
if ( ! is_wp_error( $tags ) && ( is_array( $tags ) && $tags !== array() ) ) {
foreach ( $tags as $tag ) {
$this->og_tag( 'article:tag', $tag->name );
}
return true;
}
return false;
}
/**
* Output the article category as an article:section tag.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
* @return boolean;
*/
public function category() {
if ( ! is_singular() ) {
return false;
}
$post = get_post();
if ( ! $post ) {
return false;
}
$primary_term = new WPSEO_Primary_Term( 'category', $post->ID );
$primary_category = $primary_term->get_primary_term();
if ( $primary_category ) {
// We can only show one section here, so we take the first one.
$this->og_tag( 'article:section', get_cat_name( $primary_category ) );
return true;
}
$terms = get_the_category();
if ( ! is_wp_error( $terms ) && is_array( $terms ) && ! empty( $terms ) ) {
// We can only show one section here, so we take the first one.
$term = reset( $terms );
$this->og_tag( 'article:section', $term->name );
return true;
}
return false;
}
/**
* Output the article publish and last modification date.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
* @return boolean;
*/
public function publish_date() {
if ( ! is_singular( 'post' ) ) {
/**
* Filter: 'wpseo_opengraph_show_publish_date' - Allow showing publication date for other post types.
*
* @api bool $unsigned Whether or not to show publish date.
*
* @param string $post_type The current URL's post type.
*/
if ( false === apply_filters( 'wpseo_opengraph_show_publish_date', false, get_post_type() ) ) {
return false;
}
}
$post = get_post();
$pub = mysql2date( DATE_W3C, $post->post_date_gmt, false );
$this->og_tag( 'article:published_time', $pub );
$mod = mysql2date( DATE_W3C, $post->post_modified_gmt, false );
if ( $mod !== $pub ) {
$this->og_tag( 'article:modified_time', $mod );
$this->og_tag( 'og:updated_time', $mod );
}
return true;
}
/**
* Outputs the Facebook app_id.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
*
* @return void
*/
public function app_id() {
$app_id = WPSEO_Options::get( 'fbadminapp', '' );
if ( $app_id !== '' ) {
$this->og_tag( 'fb:app_id', $app_id );
}
}
/* ********************* DEPRECATED METHODS ********************* */
/**
* Outputs the site owner.
*
* @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/
* @return void
*
* @deprecated 7.1
* @codeCoverageIgnore
*/
public function site_owner() {
// 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_OpenGraph::site_owner', '7.1', null );
}
}
/**
* Fallback method for plugins using image_output.
*
* @param string|bool $image Image URL.
*
* @deprecated 7.4
* @codeCoverageIgnore
*/
public function image_output( $image = false ) {
_deprecated_function( 'WPSEO_OpenGraph::image_output', '7.4', 'WPSEO_OpenGraph::image' );
$this->image( $image );
}
} /* End of class */

View File

@ -0,0 +1,71 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Adds customizations to the front end for the primary category.
*/
class WPSEO_Frontend_Primary_Category implements WPSEO_WordPress_Integration {
/**
* Registers the hooks necessary for correct primary category behaviour.
*/
public function register_hooks() {
add_filter( 'post_link_category', array( $this, 'post_link_category' ), 10, 3 );
}
/**
* Filters post_link_category to change the category to the chosen category by the user.
*
* @param stdClass $category The category that is now used for the post link.
* @param array $categories This parameter is not used.
* @param WP_Post $post The post in question.
*
* @return array|null|object|WP_Error The category we want to use for the post link.
*/
public function post_link_category( $category, $categories = null, $post = null ) {
$post = get_post( $post );
$primary_category = $this->get_primary_category( $post );
if ( false !== $primary_category && $primary_category !== $category->cat_ID ) {
$category = $this->get_category( $primary_category );
}
return $category;
}
/**
* Get the id of the primary category.
*
* @param WP_Post $post The post in question.
*
* @return int Primary category id.
*/
protected function get_primary_category( $post = null ) {
$post = get_post( $post );
if ( $post === null ) {
return false;
}
$primary_term = new WPSEO_Primary_Term( 'category', $post->ID );
return $primary_term->get_primary_term();
}
/**
* Wrapper for get category to make mocking easier.
*
* @param int $primary_category ID of primary category.
*
* @return array|null|object|WP_Error
*/
protected function get_category( $primary_category ) {
$category = get_category( $primary_category );
return $category;
}
}

View File

@ -0,0 +1,83 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Class WPSEO_Remove_Reply_To_Com.
*
* @since 7.0
*/
class WPSEO_Remove_Reply_To_Com implements WPSEO_WordPress_Integration {
/**
* Registers the hooks necessary to handle removing ?replytocom.
*
* @since 7.0
*
* @return void
*/
public function register_hooks() {
if ( $this->clean_reply_to_com() ) {
add_filter( 'comment_reply_link', array( $this, 'remove_reply_to_com' ) );
add_action( 'template_redirect', array( $this, 'replytocom_redirect' ), 1 );
}
}
/**
* Removes the ?replytocom variable from the link, replacing it with a #comment-<number> anchor.
*
* @todo Should this function also allow for relative urls ?
*
* @param string $link The comment link as a string.
*
* @return string The modified link.
*/
public function remove_reply_to_com( $link ) {
return preg_replace( '`href=(["\'])(?:.*(?:\?|&|&#038;)replytocom=(\d+)#respond)`', 'href=$1#comment-$2', $link );
}
/**
* Redirects out the ?replytocom variables.
*
* @since 1.4.13
* @return boolean True when redirect has been done.
*/
public function replytocom_redirect() {
if ( isset( $_GET['replytocom'] ) && is_singular() ) {
$url = get_permalink( $GLOBALS['post']->ID );
$hash = sanitize_text_field( wp_unslash( $_GET['replytocom'] ) );
$query_string = '';
if ( isset( $_SERVER['QUERY_STRING'] ) ) {
$query_string = remove_query_arg( 'replytocom', sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) );
}
if ( ! empty( $query_string ) ) {
$url .= '?' . $query_string;
}
$url .= '#comment-' . $hash;
$front = WPSEO_Frontend::get_instance();
$front->redirect( $url, 301 );
return true;
}
return false;
}
/**
* Checks whether we can allow the feature that removes ?replytocom query parameters.
*
* @return bool True to remove, false not to remove.
*/
private function clean_reply_to_com() {
/**
* Filter: 'wpseo_remove_reply_to_com' - Allow disabling the feature that removes ?replytocom query parameters.
*
* @param bool $return True to remove, false not to remove.
*/
return (bool) apply_filters( 'wpseo_remove_reply_to_com', true );
}
}

View File

@ -0,0 +1,688 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* This class handles the Twitter card functionality.
*
* @link https://dev.twitter.com/docs/cards
*/
class WPSEO_Twitter {
/**
* Instance of this class.
*
* @var object
*/
public static $instance;
/**
* Images.
*
* @var array
*/
private $images = array();
/**
* Images.
*
* @var array
*/
public $shown_images = array();
/**
* @var WPSEO_Frontend_Page_Type
*/
protected $frontend_page_type;
/**
* Will hold the Twitter card type being created.
*
* @var string
*/
private $type;
/**
* Card types currently allowed by Twitter.
*
* @link https://dev.twitter.com/cards/types
*
* @var array
*/
private $valid_types = array(
'summary',
'summary_large_image',
'app',
'player',
);
/**
* Class constructor.
*/
public function __construct() {
// Class for determine the current page type.
$this->frontend_page_type = new WPSEO_Frontend_Page_Type();
$this->twitter();
}
/**
* Outputs the Twitter Card code on singular pages.
*/
public function twitter() {
/**
* Filter: 'wpseo_output_twitter_card' - Allow disabling of the Twitter card.
*
* @api bool $enabled Enabled/disabled flag
*/
if ( false === apply_filters( 'wpseo_output_twitter_card', true ) ) {
return;
}
wp_reset_query();
$this->type();
$this->description();
$this->title();
$this->site_twitter();
if ( ! post_password_required() ) {
$this->image();
}
if ( is_singular() ) {
$this->author();
}
/**
* Action: 'wpseo_twitter' - Hook to add all Yoast SEO Twitter output to so they're close together.
*/
do_action( 'wpseo_twitter' );
}
/**
* Display the Twitter card type.
*
* This defaults to summary but can be filtered using the <code>wpseo_twitter_card_type</code> filter.
*
* @link https://dev.twitter.com/docs/cards
*/
protected function type() {
$this->determine_card_type();
$this->sanitize_card_type();
$this->output_metatag( 'card', $this->type );
}
/**
* Determines the twitter card type for the current page.
*/
private function determine_card_type() {
$this->type = WPSEO_Options::get( 'twitter_card_type' );
// @todo This should be reworked to use summary_large_image for any fitting image R.
if ( is_singular() && has_shortcode( $GLOBALS['post']->post_content, 'gallery' ) ) {
$this->images = get_post_gallery_images();
if ( count( $this->images ) > 0 ) {
$this->type = 'summary_large_image';
}
}
/**
* Filter: 'wpseo_twitter_card_type' - Allow changing the Twitter Card type as output in the Twitter card by Yoast SEO.
*
* @api string $unsigned The type string.
*/
$this->type = apply_filters( 'wpseo_twitter_card_type', $this->type );
}
/**
* Determines whether the card type is of a type currently allowed by Twitter.
*
* @link https://dev.twitter.com/cards/types
*/
private function sanitize_card_type() {
if ( ! in_array( $this->type, $this->valid_types, true ) ) {
$this->type = 'summary';
}
}
/**
* Output the metatag.
*
* @param string $name Tag name string.
* @param string $value Tag value string.
* @param bool $escaped Force escape flag.
*/
private function output_metatag( $name, $value, $escaped = false ) {
// Escape the value if not escaped.
if ( false === $escaped ) {
$value = esc_attr( $value );
}
/**
* Filter: 'wpseo_twitter_metatag_key' - Make the Twitter metatag key filterable.
*
* @api string $key The Twitter metatag key.
*/
$metatag_key = apply_filters( 'wpseo_twitter_metatag_key', 'name' );
// Output meta.
echo '<meta ', esc_attr( $metatag_key ), '="twitter:', esc_attr( $name ), '" content="', $value, '" />', "\n";
}
/**
* Displays the description for Twitter.
*
* Only used when OpenGraph is inactive.
*/
protected function description() {
if ( $this->frontend_page_type->is_simple_page() ) {
$meta_desc = $this->single_description( $this->frontend_page_type->get_simple_page_id() );
}
elseif ( is_category() || is_tax() || is_tag() ) {
$meta_desc = $this->taxonomy_description();
}
else {
$meta_desc = $this->fallback_description();
}
$meta_desc = wpseo_replace_vars( $meta_desc, get_queried_object() );
/**
* Filter: 'wpseo_twitter_description' - Allow changing the Twitter description as output in the Twitter card by Yoast SEO.
*
* @api string $twitter The description string.
*/
$meta_desc = apply_filters( 'wpseo_twitter_description', $meta_desc );
if ( is_string( $meta_desc ) && $meta_desc !== '' ) {
$this->output_metatag( 'description', $meta_desc );
}
}
/**
* Returns the description for a singular page.
*
* @param int $post_id Post ID.
*
* @return string
*/
private function single_description( $post_id = 0 ) {
$meta_desc = trim( WPSEO_Meta::get_value( 'twitter-description', $post_id ) );
if ( is_string( $meta_desc ) && '' !== $meta_desc ) {
return $meta_desc;
}
$meta_desc = $this->fallback_description();
if ( is_string( $meta_desc ) && '' !== $meta_desc ) {
return $meta_desc;
}
return wp_strip_all_tags( get_the_excerpt() );
}
/**
* Getting the description for the taxonomy.
*
* @return bool|mixed|string
*/
private function taxonomy_description() {
$meta_desc = WPSEO_Taxonomy_Meta::get_meta_without_term( 'twitter-description' );
if ( ! is_string( $meta_desc ) || $meta_desc === '' ) {
$meta_desc = $this->fallback_description();
}
if ( is_string( $meta_desc ) && $meta_desc !== '' ) {
return $meta_desc;
}
return wp_strip_all_tags( term_description() );
}
/**
* Returns a fallback description.
*
* @return string
*/
private function fallback_description() {
return trim( WPSEO_Frontend::get_instance()->metadesc( false ) );
}
/**
* Displays the title for Twitter.
*
* Only used when OpenGraph is inactive.
*/
protected function title() {
if ( $this->frontend_page_type->is_simple_page() ) {
$title = $this->single_title( $this->frontend_page_type->get_simple_page_id() );
}
elseif ( is_category() || is_tax() || is_tag() ) {
$title = $this->taxonomy_title();
}
else {
$title = $this->fallback_title();
}
$title = wpseo_replace_vars( $title, get_queried_object() );
/**
* Filter: 'wpseo_twitter_title' - Allow changing the Twitter title as output in the Twitter card by Yoast SEO.
*
* @api string $twitter The title string.
*/
$title = apply_filters( 'wpseo_twitter_title', $title );
if ( is_string( $title ) && $title !== '' ) {
$this->output_metatag( 'title', $title );
}
}
/**
* Returns the Twitter title for a single post.
*
* @param int $post_id Post ID.
*
* @return string
*/
private function single_title( $post_id = 0 ) {
$title = WPSEO_Meta::get_value( 'twitter-title', $post_id );
if ( ! is_string( $title ) || $title === '' ) {
return $this->fallback_title();
}
return $title;
}
/**
* Getting the title for the taxonomy.
*
* @return bool|mixed|string
*/
private function taxonomy_title() {
$title = WPSEO_Taxonomy_Meta::get_meta_without_term( 'twitter-title' );
if ( ! is_string( $title ) || $title === '' ) {
return $this->fallback_title();
}
return $title;
}
/**
* Returns the Twitter title for any page.
*
* @return string
*/
private function fallback_title() {
return WPSEO_Frontend::get_instance()->title( '' );
}
/**
* Displays the Twitter account for the site.
*/
protected function site_twitter() {
switch ( WPSEO_Options::get( 'company_or_person', '' ) ) {
case 'person':
$user_id = (int) WPSEO_Options::get( 'company_or_person_user_id', false );
$twitter = get_the_author_meta( 'twitter', $user_id );
// For backwards compat reasons, if there is no twitter ID for person, we fall back to site.
if ( empty( $twitter ) ) {
$twitter = WPSEO_Options::get( 'twitter_site' );
}
break;
case 'company':
default:
$twitter = WPSEO_Options::get( 'twitter_site' );
break;
}
/**
* Filter: 'wpseo_twitter_site' - Allow changing the Twitter site account as output in the Twitter card by Yoast SEO.
*
* @api string $unsigned Twitter site account string.
*/
$site = apply_filters( 'wpseo_twitter_site', $twitter );
$site = $this->get_twitter_id( $site );
if ( is_string( $site ) && $site !== '' ) {
$this->output_metatag( 'site', '@' . $site );
}
}
/**
* Checks if the given id is actually an id or a url and if url, distills the id from it.
*
* Solves issues with filters returning urls and theme's/other plugins also adding a user meta
* twitter field which expects url rather than an id (which is what we expect).
*
* @param string $id Twitter ID or url.
*
* @return string|bool Twitter ID or false if it failed to get a valid Twitter ID.
*/
private function get_twitter_id( $id ) {
if ( preg_match( '`([A-Za-z0-9_]{1,25})$`', $id, $match ) ) {
return $match[1];
}
return false;
}
/**
* Displays the image for Twitter.
*
* Only used when OpenGraph is inactive or Summary Large Image card is chosen.
*/
protected function image() {
if ( is_category() || is_tax() || is_tag() ) {
$this->taxonomy_image_output();
}
else {
$this->single_image_output();
}
if ( count( $this->shown_images ) === 0 && WPSEO_Options::get( 'og_default_image', '' ) !== '' ) {
$this->image_output( WPSEO_Options::get( 'og_default_image' ) );
}
}
/**
* Outputs the first image of a gallery.
*/
private function gallery_images_output() {
$this->image_output( reset( $this->images ) );
}
/**
* @return bool
*/
private function taxonomy_image_output() {
foreach ( array( 'twitter-image', 'opengraph-image' ) as $tag ) {
$img = WPSEO_Taxonomy_Meta::get_meta_without_term( $tag );
if ( is_string( $img ) && $img !== '' ) {
$this->image_output( $img );
return true;
}
}
/**
* Filter: wpseo_twitter_taxonomy_image - Allow developers to set a custom Twitter image for taxonomies.
*
* @api bool|string $unsigned Return string to supply image to use, false to use no image.
*/
$img = apply_filters( 'wpseo_twitter_taxonomy_image', false );
if ( is_string( $img ) && $img !== '' ) {
$this->image_output( $img );
return true;
}
return false;
}
/**
* Takes care of image output when we only need to display a single image.
*
* @return void
*/
private function single_image_output() {
if ( $this->homepage_image_output() ) {
return;
}
// Posts page, which won't be caught by is_singular() below.
if ( $this->posts_page_image_output() ) {
return;
}
if ( $this->frontend_page_type->is_simple_page() ) {
$post_id = $this->frontend_page_type->get_simple_page_id();
if ( $this->image_from_meta_values_output( $post_id ) ) {
return;
}
if ( $this->image_of_attachment_page_output( $post_id ) ) {
return;
}
if ( $this->image_thumbnail_output( $post_id ) ) {
return;
}
if ( count( $this->images ) > 0 ) {
$this->gallery_images_output();
return;
}
if ( $this->image_from_content_output( $post_id ) ) {
return;
}
}
}
/**
* Show the front page image.
*
* @return bool
*/
private function homepage_image_output() {
if ( is_front_page() ) {
if ( WPSEO_Options::get( 'og_frontpage_image', '' ) !== '' ) {
$this->image_output( WPSEO_Options::get( 'og_frontpage_image' ) );
return true;
}
}
return false;
}
/**
* Show the posts page image.
*
* @return bool
*/
private function posts_page_image_output() {
if ( is_front_page() || ! is_home() ) {
return false;
}
$post_id = get_option( 'page_for_posts' );
if ( $this->image_from_meta_values_output( $post_id ) ) {
return true;
}
if ( $this->image_thumbnail_output( $post_id ) ) {
return true;
}
return false;
}
/**
* Outputs a Twitter image tag for a given image.
*
* @param string $img The source URL to the image.
*
* @return bool
*/
protected function image_output( $img ) {
/**
* Filter: 'wpseo_twitter_image' - Allow changing the Twitter Card image.
*
* @api string $img Image URL string.
*/
$img = apply_filters( 'wpseo_twitter_image', $img );
if ( WPSEO_Utils::is_url_relative( $img ) === true && $img[0] === '/' ) {
$parsed_url = wp_parse_url( home_url() );
$img = $parsed_url['scheme'] . '://' . $parsed_url['host'] . $img;
}
$escaped_img = esc_url( $img );
if ( in_array( $escaped_img, $this->shown_images, true ) ) {
return false;
}
if ( is_string( $escaped_img ) && $escaped_img !== '' ) {
$this->output_metatag( 'image', $escaped_img, true );
array_push( $this->shown_images, $escaped_img );
return true;
}
return false;
}
/**
* Retrieve images from the post meta values.
*
* @param int $post_id Optional post ID to use.
*
* @return bool
*/
private function image_from_meta_values_output( $post_id = 0 ) {
foreach ( array( 'twitter-image', 'opengraph-image' ) as $tag ) {
$img = WPSEO_Meta::get_value( $tag, $post_id );
if ( $img !== '' ) {
$this->image_output( $img );
return true;
}
}
return false;
}
/**
* Retrieve an attachment page's attachment.
*
* @param string $attachment_id The ID of the attachment for which to retrieve the image.
*
* @return bool
*/
private function image_of_attachment_page_output( $attachment_id ) {
if ( get_post_type( $attachment_id ) === 'attachment' ) {
$mime_type = get_post_mime_type( $attachment_id );
switch ( $mime_type ) {
case 'image/jpeg':
case 'image/png':
case 'image/gif':
$this->image_output( wp_get_attachment_url( $attachment_id ) );
return true;
}
}
return false;
}
/**
* Retrieve the featured image.
*
* @param int $post_id Optional post ID to use.
*
* @return bool
*/
private function image_thumbnail_output( $post_id = 0 ) {
if ( empty( $post_id ) ) {
$post_id = get_the_ID();
}
if ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail( $post_id ) ) {
/**
* Filter: 'wpseo_twitter_image_size' - Allow changing the Twitter Card image size.
*
* @api string $featured_img Image size string.
*/
$featured_img = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), apply_filters( 'wpseo_twitter_image_size', 'full' ) );
if ( $featured_img ) {
$this->image_output( $featured_img[0] );
return true;
}
}
return false;
}
/**
* Retrieve the image from the content.
*
* @param int $post_id The post id to extract the images from.
*
* @return bool True when images output succeeded.
*/
private function image_from_content_output( $post_id ) {
$image_finder = new WPSEO_Content_Images();
$images = $image_finder->get_images( $post_id );
if ( ! is_array( $images ) || $images === array() ) {
return false;
}
$image_url = reset( $images );
if ( ! $image_url ) {
return false;
}
$this->image_output( $image_url );
return true;
}
/**
* Displays the authors Twitter account.
*/
protected function author() {
$post = get_post();
$twitter = null;
if ( is_object( $post ) ) {
$twitter = ltrim( trim( get_the_author_meta( 'twitter', $post->post_author ) ), '@' );
}
/**
* Filter: 'wpseo_twitter_creator_account' - Allow changing the Twitter account as output in the Twitter card by Yoast SEO.
*
* @api string $twitter The twitter account name string.
*/
$twitter = apply_filters( 'wpseo_twitter_creator_account', $twitter );
$twitter = $this->get_twitter_id( $twitter );
if ( is_string( $twitter ) && $twitter !== '' ) {
$this->output_metatag( 'creator', '@' . $twitter );
}
elseif ( WPSEO_Options::get( 'twitter_site', '' ) !== '' && is_string( WPSEO_Options::get( 'twitter_site' ) ) ) {
$this->output_metatag( 'creator', '@' . WPSEO_Options::get( 'twitter_site' ) );
}
}
/**
* Get the singleton instance of this class.
*
* @return object
*/
public static function get_instance() {
if ( ! ( self::$instance instanceof self ) ) {
self::$instance = new self();
}
return self::$instance;
}
} /* End of class */

View File

@ -0,0 +1,96 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend
*/
/**
* Represents the logic to determine if the current page is a WooCommerce shop page.
*/
class WPSEO_WooCommerce_Shop_Page implements WPSEO_WordPress_Integration {
/**
* Holds the shop page id.
*
* @var int
*/
protected static $shop_page_id;
/**
* True when current page is the shop page.
*
* @var bool
*/
protected static $is_shop_page;
/**
* Registers the hooks.
*
* @return void
*/
public function register_hooks() {
if ( ! $this->is_woocommerce_active() ) {
return;
}
add_filter( 'wpseo_frontend_page_type_simple_page_id', array( $this, 'get_page_id' ) );
}
/**
* Determines whether or not WooCommerce is active.
*
* @return bool True if woocommerce plugin is active.
*/
private function is_woocommerce_active() {
return WPSEO_Utils::is_woocommerce_active();
}
/**
* Returns the ID of the WooCommerce shop page when the currently opened page is the shop page.
*
* @param int $page_id The page id.
*
* @return int The Page ID of the shop.
*/
public function get_page_id( $page_id ) {
if ( ! $this->is_shop_page() ) {
return $page_id;
}
return $this->get_shop_page_id();
}
/**
* Checks if the current page is the shop page.
*
* @return bool Whether the current page is the WooCommerce shop page.
*/
public function is_shop_page() {
global $wp_query;
// Prevents too early "caching".
if ( ! isset( $wp_query ) ) {
return false;
}
if ( ! isset( self::$is_shop_page ) ) {
self::$is_shop_page = $this->is_woocommerce_active() && is_shop() && ! is_search();
}
return self::$is_shop_page;
}
/**
* Returns the id of the set WooCommerce shop page.
*
* @return int The ID of the set page.
*/
public function get_shop_page_id() {
if ( ! isset( self::$shop_page_id ) ) {
self::$shop_page_id = function_exists( 'wc_get_page_id' ) ? wc_get_page_id( 'shop' ) : ( -1 );
}
return self::$shop_page_id;
}
}

View File

@ -0,0 +1,4 @@
<?php
/**
* Nothing to see here.
*/

View File

@ -0,0 +1,183 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema Article data.
*
* @since 10.2
*/
class WPSEO_Schema_Article implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
$this->context = $context;
}
/**
* Determines whether or not a piece should be added to the graph.
*
* @return bool
*/
public function is_needed() {
/**
* Filter: 'wpseo_schema_article_post_types' - Allow changing for which post types we output Article schema.
*
* @api string[] $post_types The post types for which we output Article.
*/
$post_types = apply_filters( 'wpseo_schema_article_post_types', array( 'post' ) );
return is_singular( $post_types );
}
/**
* Returns Article data.
*
* @return array $data Article data.
*/
public function generate() {
$post = get_post( $this->context->id );
$comment_count = get_comment_count( $this->context->id );
$data = array(
'@type' => 'Article',
'@id' => $this->context->canonical . WPSEO_Schema_IDs::ARTICLE_HASH,
'isPartOf' => array( '@id' => $this->context->canonical . WPSEO_Schema_IDs::WEBPAGE_HASH ),
'author' => array(
'@id' => $this->get_author_url( $post ),
'name' => get_the_author_meta( 'display_name', $post->post_author ),
),
'publisher' => array( '@id' => $this->get_publisher_url() ),
'headline' => get_the_title(),
'datePublished' => mysql2date( DATE_W3C, $post->post_date_gmt, false ),
'dateModified' => mysql2date( DATE_W3C, $post->post_modified_gmt, false ),
'commentCount' => $comment_count['approved'],
'mainEntityOfPage' => $this->context->canonical . WPSEO_Schema_IDs::WEBPAGE_HASH,
);
$data = $this->add_image( $data );
$data = $this->add_keywords( $data );
$data = $this->add_sections( $data );
return $data;
}
/**
* Determine the proper author URL.
*
* @param \WP_Post $post Post object.
*
* @return string
*/
private function get_author_url( $post ) {
if ( $this->context->site_represents === 'person' && $this->context->site_user_id === (int) $post->post_author ) {
return $this->context->site_url . WPSEO_Schema_IDs::PERSON_HASH;
}
return get_author_posts_url( $post->post_author ) . WPSEO_Schema_IDs::AUTHOR_HASH;
}
/**
* Determine the proper publisher URL.
*
* @return string
*/
private function get_publisher_url() {
if ( $this->context->site_represents === 'person' ) {
return $this->context->site_url . WPSEO_Schema_IDs::PERSON_HASH;
}
return $this->context->site_url . WPSEO_Schema_IDs::ORGANIZATION_HASH;
}
/**
* Adds tags as keywords, if tags are assigned.
*
* @param array $data Article data.
*
* @return array $data Article data.
*/
private function add_keywords( $data ) {
/**
* Filter: 'wpseo_schema_article_keywords_taxonomy' - Allow changing the taxonomy used to assign keywords to a post type Article data.
*
* @api string $taxonomy The chosen taxonomy.
*/
$taxonomy = apply_filters( 'wpseo_schema_article_keywords_taxonomy', 'post_tag' );
return $this->add_terms( $data, 'keywords', $taxonomy );
}
/**
* Adds categories as sections, if categories are assigned.
*
* @param array $data Article data.
*
* @return array $data Article data.
*/
private function add_sections( $data ) {
/**
* Filter: 'wpseo_schema_article_sections_taxonomy' - Allow changing the taxonomy used to assign keywords to a post type Article data.
*
* @api string $taxonomy The chosen taxonomy.
*/
$taxonomy = apply_filters( 'wpseo_schema_article_sections_taxonomy', 'category' );
return $this->add_terms( $data, 'articleSection', $taxonomy );
}
/**
* Adds a term or multiple terms, comma separated, to a field.
*
* @param array $data Article data.
* @param string $key The key in data to save the terms in.
* @param string $taxonomy The taxonomy to retrieve the terms from.
*
* @return mixed array $data Article data.
*/
private function add_terms( $data, $key, $taxonomy ) {
$terms = get_the_terms( $this->context->id, $taxonomy );
if ( is_array( $terms ) ) {
$keywords = array();
foreach ( $terms as $term ) {
// We are checking against the WordPress internal translation.
// @codingStandardsIgnoreLine
if ( $term->name !== __( 'Uncategorized' ) ) {
$keywords[] = $term->name;
}
}
$data[ $key ] = implode( ',', $keywords );
}
return $data;
}
/**
* Adds an image node if the post has a featured image.
*
* @param array $data The Article data.
*
* @return array $data The Article data.
*/
private function add_image( $data ) {
if ( has_post_thumbnail( $this->context->id ) ) {
$data['image'] = array(
'@id' => $this->context->canonical . WPSEO_Schema_IDs::PRIMARY_IMAGE_HASH,
);
}
return $data;
}
}

View File

@ -0,0 +1,131 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema Person data.
*
* @since 10.2
*
* @property WPSEO_Schema_Context $context A value object with context variables.
*/
class WPSEO_Schema_Author extends WPSEO_Schema_Person implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
parent::__construct( $context );
$this->context = $context;
$this->logo_hash = WPSEO_Schema_IDs::AUTHOR_LOGO_HASH;
}
/**
* Determine whether we should return Person schema.
*
* @return bool
*/
public function is_needed() {
if ( is_author() ) {
return true;
}
if ( $this->is_post_author() ) {
$post = get_post( $this->context->id );
// If the author is the user the site represents, no need for an extra author block.
if ( (int) $post->post_author === $this->context->site_user_id ) {
return false;
}
return true;
}
return false;
}
/**
* Builds our array of Schema Person data for a given user ID.
*
* @param int $user_id The user ID to use.
*
* @return array An array of Schema Person data.
*/
protected function build_person_data( $user_id ) {
$data = parent::build_person_data( $user_id );
// If this is an author page, the Person object is the main object, so we set it as such here.
if ( is_author() ) {
$data['mainEntityOfPage'] = array(
'@id' => $this->context->canonical . WPSEO_Schema_IDs::WEBPAGE_HASH,
);
}
return $data;
}
/**
* Determine whether the current URL is worthy of Article schema.
*
* @return bool
*/
protected function is_post_author() {
/**
* Filter: 'wpseo_schema_article_post_type' - Allow changing for which post types we output Article schema.
*
* @api array $post_types The post types for which we output Article.
*/
$post_types = apply_filters( 'wpseo_schema_article_post_type', array( 'post' ) );
if ( is_singular( $post_types ) ) {
return true;
}
return false;
}
/**
* Determines a User ID for the Person data.
*
* @return bool|int User ID or false upon return.
*/
protected function determine_user_id() {
switch ( true ) {
case is_author():
$user_id = get_queried_object_id();
break;
default:
$post = get_post( $this->context->id );
$user_id = (int) $post->post_author;
break;
}
/**
* Filter: 'wpseo_schema_person_user_id' - Allows filtering of user ID used for person output.
*
* @api int|bool $user_id The user ID currently determined.
*/
return apply_filters( 'wpseo_schema_person_user_id', $user_id );
}
/**
* Returns the string to use in Schema's `@id`.
*
* @param int $user_id The user ID if we're on a user page.
*
* @return string The `@id` string value.
*/
protected function determine_schema_id( $user_id ) {
return get_author_posts_url( $user_id ) . WPSEO_Schema_IDs::AUTHOR_HASH;
}
}

View File

@ -0,0 +1,148 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema Breadcrumb data.
*
* @since 10.2
*/
class WPSEO_Schema_Breadcrumb implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* Current position in the List.
*
* @var int
*/
private $index;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
$this->context = $context;
}
/**
* Determine if we should add a breadcrumb attribute.
*
* @return bool
*/
public function is_needed() {
if ( is_404() ) {
return false;
}
if ( is_front_page() ) {
return false;
}
if ( $this->context->breadcrumbs_enabled ) {
return true;
}
return false;
}
/**
* Returns Schema breadcrumb data to allow recognition of page's position in the site hierarchy.
*
* @link https://developers.google.com/search/docs/data-types/breadcrumb
*
* @return bool|array Array on success, false on failure.
*/
public function generate() {
$breadcrumbs_instance = WPSEO_Breadcrumbs::get_instance();
$breadcrumbs = $breadcrumbs_instance->get_links();
$broken = false;
$list_elements = array();
foreach ( $breadcrumbs as $index => $breadcrumb ) {
if ( ! empty( $breadcrumb['hide_in_schema'] ) ) {
continue;
}
if ( ! array_key_exists( 'url', $breadcrumb ) || ! array_key_exists( 'text', $breadcrumb ) ) {
$broken = true;
break;
}
$list_elements[] = $this->add_breadcrumb( $index, $breadcrumb );
$this->index = $index;
}
if ( is_paged() ) {
$list_elements[] = $this->add_paginated_state();
}
$data = array(
'@type' => 'BreadcrumbList',
'@id' => $this->context->canonical . WPSEO_Schema_IDs::BREADCRUMB_HASH,
'itemListElement' => $list_elements,
);
// Only output if JSON is correctly formatted.
if ( ! $broken ) {
return $data;
}
return false;
}
/**
* Returns a breadcrumb array.
*
* @param int $index The position in the list.
* @param array $breadcrumb The breadcrumb array.
*
* @return array A breadcrumb listItem.
*/
private function add_breadcrumb( $index, $breadcrumb ) {
if ( empty( $breadcrumb['url'] ) ) {
if ( is_paged() ) {
// Retrieve the un-paginated state of the current page.
$breadcrumb['url'] = WPSEO_Frontend::get_instance()->canonical( false, true );
}
else {
$breadcrumb['url'] = $this->context->canonical;
}
}
if ( empty( $breadcrumb['text'] ) ) {
$breadcrumb['url'] = $this->context->title;
}
return array(
'@type' => 'ListItem',
'position' => ( $index + 1 ),
'item' => array(
'@type' => 'WebPage',
'@id' => $breadcrumb['url'],
'url' => $breadcrumb['url'], // For future proofing, we're trying to change the standard for this.
'name' => $breadcrumb['text'],
),
);
}
/**
* Adds the paginated state to the breadcrumb array.
*
* @return array A breadcrumb listItem.
*/
private function add_paginated_state() {
$this->index++;
return $this->add_breadcrumb( $this->index, array(
'url' => $this->context->canonical,
'text' => $this->context->title,
) );
}
}

View File

@ -0,0 +1,189 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Context variables for Schema generation.
*
* @property string $canonical The current page's canonical.
* @property string $company_name Holds the company name, if the site represents a company.
* @property int $id The post ID, if there is one.
* @property string $site_name The site's name.
* @property string $site_represents Whether this site represents a `company` or a `person`.
* @property string $site_url The site's URL.
* @property int $site_user_id The site's User ID if a site represents a `person`.
* @property string $title Page title.
* @property string $description Page description.
* @property bool $breadcrumbs_enabled Whether or not this site has breadcrumbs enabled.
*
* @since 10.2
*/
class WPSEO_Schema_Context {
/**
* The current page's canonical.
*
* @var string
*/
public $canonical;
/**
* Holds the company name, if the site represents a company.
*
* @var string
*/
public $company_name;
/**
* The queried object ID, if there is one.
*
* @var int
*/
public $id;
/**
* Whether this site represents a `company` or a `person`.
*
* @var string
*/
public $site_represents;
/**
* The site's Name.
*
* @var string
*/
public $site_name;
/**
* The site's URL.
*
* @var string
*/
public $site_url;
/**
* Page title.
*
* @var string
*/
public $title;
/**
* User ID when the site represents a Person.
*
* @var int
*/
public $site_user_id;
/**
* Page description.
*
* @var string
*/
public $description;
/**
* Whether or not this site has breadcrumbs enabled.
*
* @var bool
*/
public $breadcrumbs_enabled;
/**
* Hash used for the Author `@id`.
*/
const AUTHOR_HASH = '#author';
/**
* Hash used for the Author Logo's `@id`.
*/
const AUTHOR_LOGO_HASH = '#authorlogo';
/**
* Hash used for the Breadcrumb's `@id`.
*/
const BREADCRUMB_HASH = '#breadcrumb';
/**
* Hash used for the Person `@id`.
*/
const PERSON_HASH = '#person';
/**
* Hash used for the Article `@id`.
*/
const ARTICLE_HASH = '#article';
/**
* Hash used for the Organization `@id`.
*/
const ORGANIZATION_HASH = '#organization';
/**
* Hash used for the Organization `@id`.
*/
const ORGANIZATION_LOGO_HASH = '#logo';
/**
* Hash used for the logo `@id`.
*/
const PERSON_LOGO_HASH = '#personlogo';
/**
* Hash used for an Article's primary image `@id`.
*/
const PRIMARY_IMAGE_HASH = '#primaryimage';
/**
* Hash used for the WebPage's `@id`.
*/
const WEBPAGE_HASH = '#webpage';
/**
* Hash used for the Website's `@id`.
*/
const WEBSITE_HASH = '#website';
/**
* WPSEO_Schema_Context constructor.
*/
public function __construct() {
$this->build_data();
}
/**
* Builds all the required data for the context object.
*/
private function build_data() {
$this->breadcrumbs_enabled = current_theme_supports( 'yoast-seo-breadcrumbs' );
if ( ! $this->breadcrumbs_enabled ) {
$this->breadcrumbs_enabled = WPSEO_Options::get( 'breadcrumbs-enable', false );
}
$front = WPSEO_Frontend::get_instance();
$this->canonical = $front->canonical( false );
$this->title = $front->title( '' );
$this->description = $front->metadesc( false );
$this->site_name = $this->set_site_name();
$this->site_represents = WPSEO_Options::get( 'company_or_person', '' );
$this->site_url = trailingslashit( WPSEO_Utils::home_url() );
if ( $this->site_represents === 'company' ) {
$this->company_name = WPSEO_Options::get( 'company_name' );
}
if ( $this->site_represents === 'person' ) {
$this->site_user_id = WPSEO_Options::get( 'company_or_person_user_id', false );
}
$this->id = get_queried_object_id();
}
/**
* Retrieves the site's name from settings.
*
* @return string
*/
private function set_site_name() {
if ( '' !== WPSEO_Options::get( 'website_name', '' ) ) {
return WPSEO_Options::get( 'website_name' );
}
return get_bloginfo( 'name' );
}
}

View File

@ -0,0 +1,68 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Constants used for @id variables.
*
* @since 10.2
*/
class WPSEO_Schema_IDs {
/**
* Hash used for the Author `@id`.
*/
const AUTHOR_HASH = '#author';
/**
* Hash used for the Author Logo's `@id`.
*/
const AUTHOR_LOGO_HASH = '#authorlogo';
/**
* Hash used for the Breadcrumb's `@id`.
*/
const BREADCRUMB_HASH = '#breadcrumb';
/**
* Hash used for the Person `@id`.
*/
const PERSON_HASH = '#person';
/**
* Hash used for the Article `@id`.
*/
const ARTICLE_HASH = '#article';
/**
* Hash used for the Organization `@id`.
*/
const ORGANIZATION_HASH = '#organization';
/**
* Hash used for the Organization `@id`.
*/
const ORGANIZATION_LOGO_HASH = '#logo';
/**
* Hash used for the logo `@id`.
*/
const PERSON_LOGO_HASH = '#personlogo';
/**
* Hash used for an Article's primary image `@id`.
*/
const PRIMARY_IMAGE_HASH = '#primaryimage';
/**
* Hash used for the WebPage's `@id`.
*/
const WEBPAGE_HASH = '#webpage';
/**
* Hash used for the Website's `@id`.
*/
const WEBSITE_HASH = '#website';
}

View File

@ -0,0 +1,155 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema image data.
*
* @since 11.1
*
* @property string $schema_id The `@id` to use for the returned image.
* @property array $data The ImageObject Schema array.
* @property int $attachment_id The ID of the attachment used to generate the object.
*/
class WPSEO_Schema_Image {
/**
* The `@id` to use for the returned image.
*
* @var string
*/
private $schema_id;
/**
* The ImageObject Schema array.
*
* @var array
*/
private $data;
/**
* The ID of the attachment used to generate the object.
*
* @var int
*/
private $attachment_id;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param string $schema_id The string to use in an image's `@id`.
*/
public function __construct( $schema_id ) {
$this->schema_id = $schema_id;
$this->generate_object();
}
/**
* Find an image based on its URL and generate a Schema object for it.
*
* @param string $url The image URL to base our object on.
* @param string $caption An optional caption.
*
* @return array Schema ImageObject array.
*/
public function generate_from_url( $url, $caption = '' ) {
$attachment_id = WPSEO_Image_Utils::get_attachment_by_url( $url );
if ( $attachment_id > 0 ) {
return $this->generate_from_attachment_id( $attachment_id, $caption );
}
return $this->simple_image_object( $url, $caption );
}
/**
* Retrieve data about an image from the database and use it to generate a Schema object.
*
* @param int $attachment_id The attachment to retrieve data from.
* @param string $caption The caption string, if there is one.
*
* @return array Schema ImageObject array.
*/
public function generate_from_attachment_id( $attachment_id, $caption = '' ) {
$this->attachment_id = $attachment_id;
$this->data['url'] = wp_get_attachment_image_url( $this->attachment_id, 'full' );
$this->add_image_size();
$this->add_caption( $caption );
return $this->data;
}
/**
* If we can't find $url in our database, we output a simple ImageObject.
*
* @param string $url The image URL.
* @param string $caption A caption, if set.
*
* @return array $data Schema ImageObject array.
*/
public function simple_image_object( $url, $caption = '' ) {
$this->data['url'] = $url;
if ( ! empty( $caption ) ) {
$this->data['caption'] = $caption;
}
return $this->data;
}
/**
* Retrieves an image's caption if set, or uses the alt tag if that's set.
*
* @param string $caption The caption string, if there is one.
*
* @return void
*/
private function add_caption( $caption = '' ) {
if ( ! empty( $caption ) ) {
$this->data['caption'] = $caption;
return;
}
$caption = wp_get_attachment_caption();
if ( ! empty( $caption ) ) {
$this->data['caption'] = $caption;
return;
}
$caption = get_post_meta( $this->attachment_id, '_wp_attachment_image_alt', true );
if ( ! empty( $caption ) ) {
$this->data['caption'] = $caption;
}
return;
}
/**
* Generates our bare bone ImageObject.
*
* @return void
*/
private function generate_object() {
$this->data = array(
'@type' => 'ImageObject',
'@id' => $this->schema_id,
);
}
/**
* Adds image's width and height.
*
* @return void
*/
private function add_image_size() {
$image_meta = wp_get_attachment_metadata( $this->attachment_id );
if ( empty( $image_meta['width'] ) || empty( $image_meta['height'] ) ) {
return;
}
$this->data['width'] = $image_meta['width'];
$this->data['height'] = $image_meta['height'];
}
}

View File

@ -0,0 +1,109 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema Organization data.
*
* @since 10.2
*/
class WPSEO_Schema_Organization implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
$this->context = $context;
}
/**
* Determines whether an Organization graph piece should be added.
*
* @return bool
*/
public function is_needed() {
return ( $this->context->site_represents === 'company' );
}
/**
* Returns the Organization Schema data.
*
* @return array $data The Organization schema.
*/
public function generate() {
$data = array(
'@type' => 'Organization',
'@id' => $this->context->site_url . WPSEO_Schema_IDs::ORGANIZATION_HASH,
'name' => $this->context->company_name,
'url' => $this->context->site_url,
'sameAs' => $this->fetch_social_profiles(),
);
$data = $this->add_logo( $data );
return $data;
}
/**
* Adds a site's logo.
*
* @param array $data The Organization schema.
*
* @return array $data The Organization schema.
*/
private function add_logo( $data ) {
$logo = WPSEO_Options::get( 'company_logo', '' );
if ( empty( $logo ) ) {
return $data;
}
$id = $this->context->site_url . WPSEO_Schema_IDs::ORGANIZATION_LOGO_HASH;
$schema_image = new WPSEO_Schema_Image( $id );
$data['logo'] = $schema_image->generate_from_url( $logo, $this->context->company_name );
$data['image'] = array( '@id' => $id );
return $data;
}
/**
* Retrieve the social profiles to display in the organization schema.
*
* @since 1.8
*
* @link https://developers.google.com/webmasters/structured-data/customize/social-profiles
*
* @return array $profiles An array of social profiles.
*/
private function fetch_social_profiles() {
$profiles = array();
$social_profiles = array(
'facebook_site',
'instagram_url',
'linkedin_url',
'myspace_url',
'youtube_url',
'pinterest_url',
'wikipedia_url',
);
foreach ( $social_profiles as $profile ) {
if ( WPSEO_Options::get( $profile, '' ) !== '' ) {
$profiles[] = WPSEO_Options::get( $profile );
}
}
if ( WPSEO_Options::get( 'twitter_site', '' ) !== '' ) {
$profiles[] = 'https://twitter.com/' . WPSEO_Options::get( 'twitter_site' );
}
return $profiles;
}
}

View File

@ -0,0 +1,197 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema Person data.
*
* @since 10.2
*/
class WPSEO_Schema_Person implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
$this->context = $context;
}
/**
* Determine whether we should return Person schema.
*
* @return bool
*/
public function is_needed() {
if ( $this->context->site_represents === 'person' || is_author() ) {
return true;
}
return false;
}
/**
* Returns Person Schema data.
*
* @return bool|array Person data on success, false on failure.
*/
public function generate() {
$user_id = $this->determine_user_id();
if ( ! $user_id ) {
return false;
}
$data = $this->build_person_data( $user_id );
return $data;
}
/**
* Determines a User ID for the Person data.
*
* @return bool|int User ID or false upon return.
*/
protected function determine_user_id() {
$user_id = $this->context->site_user_id;
/**
* Filter: 'wpseo_schema_person_user_id' - Allows filtering of user ID used for person output.
*
* @api int|bool $user_id The user ID currently determined.
*/
return apply_filters( 'wpseo_schema_person_user_id', $user_id );
}
/**
* Retrieve a list of social profile URLs for Person.
*
* @param int $user_id User ID.
*
* @return array $output A list of social profiles.
*/
protected function get_social_profiles( $user_id ) {
$social_profiles = array(
'facebook',
'instagram',
'linkedin',
'pinterest',
'twitter',
'myspace',
'youtube',
'soundcloud',
'tumblr',
'wikipedia',
);
$output = array();
foreach ( $social_profiles as $profile ) {
$social_url = $this->url_social_site( $profile, $user_id );
if ( $social_url ) {
$output[] = $social_url;
}
}
return $output;
}
/**
* Builds our array of Schema Person data for a given user ID.
*
* @param int $user_id The user ID to use.
*
* @return array An array of Schema Person data.
*/
protected function build_person_data( $user_id ) {
$user_data = get_userdata( $user_id );
$data = array(
'@type' => 'Person',
'@id' => $this->determine_schema_id( $user_id ),
'name' => $user_data->display_name,
);
$data = $this->add_image( $data, $user_data );
if ( ! empty( $user_data->description ) ) {
$data['description'] = $user_data->description;
}
$social_profiles = $this->get_social_profiles( $user_id );
if ( is_array( $social_profiles ) ) {
$data['sameAs'] = $social_profiles;
}
return $data;
}
/**
* Returns an ImageObject for the persons avatar.
*
* @param array $data The Person schema.
* @param \WP_User $user_data User data.
*
* @return array $data The Person schema.
*/
protected function add_image( $data, $user_data ) {
$url = get_avatar_url( $user_data->user_email );
if ( empty( $url ) ) {
return $data;
}
$id = $this->context->site_url . WPSEO_Schema_IDs::PERSON_LOGO_HASH;
$schema_image = new WPSEO_Schema_Image( $id );
$data['image'] = $schema_image->simple_image_object( $url, $user_data->display_name );
return $data;
}
/**
* Returns the string to use in Schema's `@id`.
*
* @param int $user_id The user ID if we're on a user page.
*
* @return string The `@id` string value.
*/
protected function determine_schema_id( $user_id ) {
switch ( true ) {
case ( $this->context->site_represents === 'company' ):
$url = get_author_posts_url( $user_id );
break;
default:
$url = $this->context->site_url;
break;
}
return $url . WPSEO_Schema_IDs::PERSON_HASH;
}
/**
* Returns an author's social site URL.
*
* @param string $social_site The social site to retrieve the URL for.
* @param mixed $user_id The user ID to use function outside of the loop.
*
* @return string
*/
protected function url_social_site( $social_site, $user_id = false ) {
$url = get_the_author_meta( $social_site, $user_id );
if ( ! empty( $url ) ) {
switch ( $social_site ) {
case 'twitter':
$url = 'https://twitter.com/' . $url;
break;
}
}
return $url;
}
}

View File

@ -0,0 +1,158 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema WebPage data.
*
* @since 10.2
*/
class WPSEO_Schema_WebPage implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
$this->context = $context;
}
/**
* Determines whether or not a piece should be added to the graph.
*
* @return bool
*/
public function is_needed() {
if ( is_404() ) {
return false;
}
return true;
}
/**
* Returns WebPage schema data.
*
* @return array WebPage schema data.
*/
public function generate() {
$data = array(
'@type' => $this->determine_page_type(),
'@id' => $this->context->canonical . WPSEO_Schema_IDs::WEBPAGE_HASH,
'url' => $this->context->canonical,
'inLanguage' => get_bloginfo( 'language' ),
'name' => $this->context->title,
'isPartOf' => array(
'@id' => $this->context->site_url . WPSEO_Schema_IDs::WEBSITE_HASH,
),
);
if ( is_front_page() ) {
$about_id = WPSEO_Schema_IDs::ORGANIZATION_HASH;
if ( $this->context->site_represents === 'person' ) {
$about_id = WPSEO_Schema_IDs::PERSON_HASH;
}
$data['about'] = array(
'@id' => $this->context->site_url . $about_id,
);
}
if ( is_singular() ) {
$data = $this->add_featured_image( $data );
$post = get_post( $this->context->id );
$data['datePublished'] = mysql2date( DATE_W3C, $post->post_date_gmt, false );
$data['dateModified'] = mysql2date( DATE_W3C, $post->post_modified_gmt, false );
}
if ( ! empty( $this->context->description ) ) {
$data['description'] = $this->context->description;
}
if ( $this->add_breadcrumbs() ) {
$data['breadcrumb'] = array(
'@id' => $this->context->canonical . WPSEO_Schema_IDs::BREADCRUMB_HASH,
);
}
return $data;
}
/**
* Determine if we should add a breadcrumb attribute.
*
* @return bool
*/
private function add_breadcrumbs() {
if ( is_front_page() ) {
return false;
}
if ( $this->context->breadcrumbs_enabled ) {
return true;
}
return false;
}
/**
* Determine the page type for the current page.
*
* @return string
*/
private function determine_page_type() {
switch ( true ) {
case is_search():
$type = 'SearchResultsPage';
break;
case is_author():
$type = 'ProfilePage';
break;
case is_archive():
$type = 'CollectionPage';
break;
default:
$type = 'WebPage';
}
/**
* Filter: 'wpseo_schema_webpage_type' - Allow changing the WebPage type.
*
* @api string $type The WebPage type.
*/
return apply_filters( 'wpseo_schema_webpage_type', $type );
}
/**
* Adds a featured image to the schema if there is one.
*
* @param array $data WebPage Schema.
*
* @return array $data WebPage Schema.
*/
private function add_featured_image( $data ) {
if ( ! has_post_thumbnail( $this->context->id ) ) {
return $data;
}
$id = $this->context->canonical . WPSEO_Schema_IDs::PRIMARY_IMAGE_HASH;
$schema_image = new WPSEO_Schema_Image( $id );
$data['image'] = $schema_image->generate_from_attachment_id( get_post_thumbnail_id() );
$data['primaryImageOfPage'] = array(
'@id' => $id,
);
return $data;
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Returns schema Website data.
*
* @since 10.2
*/
class WPSEO_Schema_Website implements WPSEO_Graph_Piece {
/**
* A value object with context variables.
*
* @var WPSEO_Schema_Context
*/
private $context;
/**
* WPSEO_Schema_Breadcrumb constructor.
*
* @param WPSEO_Schema_Context $context A value object with context variables.
*/
public function __construct( WPSEO_Schema_Context $context ) {
$this->context = $context;
}
/**
* Determines whether or not a piece should be added to the graph.
*
* @return bool
*/
public function is_needed() {
return true;
}
/**
* Outputs code to allow recognition of the internal search engine.
*
* @since 1.5.7
*
* @link https://developers.google.com/structured-data/site-name
*
* @return array Website data blob.
*/
public function generate() {
$data = array(
'@type' => 'WebSite',
'@id' => $this->context->site_url . WPSEO_Schema_IDs::WEBSITE_HASH,
'url' => $this->context->site_url,
'name' => $this->context->site_name,
'publisher' => array(
'@id' => $this->get_publisher(),
),
);
$data = $this->add_alternate_name( $data );
$data = $this->internal_search_section( $data );
return $data;
}
/**
* Determine the ID based on Company Or Person settings.
*
* @return string
*/
private function get_publisher() {
if ( $this->context->site_represents === 'person' ) {
return $this->context->site_url . WPSEO_Schema_IDs::PERSON_HASH;
}
return $this->context->site_url . WPSEO_Schema_IDs::ORGANIZATION_HASH;
}
/**
* Returns an alternate name if one was specified in the Yoast SEO settings.
*
* @param array $data The website data array.
*
* @return array $data
*/
private function add_alternate_name( $data ) {
if ( '' !== WPSEO_Options::get( 'alternate_website_name', '' ) ) {
$data['alternateName'] = WPSEO_Options::get( 'alternate_website_name' );
}
return $data;
}
/**
* Adds the internal search JSON LD code to the homepage if it's not disabled.
*
* @link https://developers.google.com/structured-data/slsb-overview
*
* @param array $data The website data array.
*
* @return array $data
*/
private function internal_search_section( $data ) {
/**
* Filter: 'disable_wpseo_json_ld_search' - Allow disabling of the json+ld output.
*
* @api bool $display_search Whether or not to display json+ld search on the frontend.
*/
if ( ! apply_filters( 'disable_wpseo_json_ld_search', false ) ) {
/**
* Filter: 'wpseo_json_ld_search_url' - Allows filtering of the search URL for Yoast SEO.
*
* @api string $search_url The search URL for this site with a `{search_term_string}` variable.
*/
$search_url = apply_filters( 'wpseo_json_ld_search_url', $this->context->site_url . '?s={search_term_string}' );
$data['potentialAction'] = array(
'@type' => 'SearchAction',
'target' => $search_url,
'query-input' => 'required name=search_term_string',
);
}
return $data;
}
}

View File

@ -0,0 +1,106 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
/**
* Class WPSEO_Schema
*
* Outputs schema code specific for Google's JSON LD stuff.
*
* @since 1.8
*/
class WPSEO_Schema implements WPSEO_WordPress_Integration {
/**
* Registers the hooks.
*/
public function register_hooks() {
add_action( 'wpseo_head', array( $this, 'json_ld' ), 91 );
add_action( 'wpseo_json_ld', array( $this, 'generate' ), 1 );
}
/**
* JSON LD output function that the functions for specific code can hook into.
*
* @since 1.8
*/
public function json_ld() {
$deprecated_data = array(
'_deprecated' => 'Please use the "wpseo_schema_*" filters to extend the Yoast SEO schema data - see the WPSEO_Schema class.',
);
/**
* Filter: 'wpseo_json_ld_output' - Allows disabling Yoast's schema output entirely.
*
* @api mixed If false or an empty array is returned, disable our output.
*/
$return = apply_filters( 'wpseo_json_ld_output', $deprecated_data, '' );
if ( $return === array() || $return === false ) {
return;
}
do_action( 'wpseo_json_ld' );
}
/**
* Outputs the JSON LD code in a valid JSON+LD wrapper.
*
* @since 10.2
*
* @return void
*/
public function generate() {
$graph = array();
foreach ( $this->get_graph_pieces() as $piece ) {
if ( ! $piece->is_needed() ) {
continue;
}
$graph_piece = $piece->generate();
/**
* Filter: 'wpseo_schema_<class name>' - Allows changing graph piece output.
*
* @api array $graph_piece The graph piece to filter.
*/
$class = str_replace( 'wpseo_schema_', '', strtolower( get_class( $piece ) ) );
$graph_piece = apply_filters( 'wpseo_schema_' . $class, $graph_piece );
if ( is_array( $graph_piece ) ) {
$graph[] = $graph_piece;
}
}
WPSEO_Utils::schema_output( $graph, 'yoast-schema-graph yoast-schema-graph--main' );
}
/**
* Gets all the graph pieces we need.
*
* @return array A filtered array of graph pieces.
*/
private function get_graph_pieces() {
$context = new WPSEO_Schema_Context();
$pieces = array(
new WPSEO_Schema_Organization( $context ),
new WPSEO_Schema_Person( $context ),
new WPSEO_Schema_Website( $context ),
new WPSEO_Schema_WebPage( $context ),
new WPSEO_Schema_Breadcrumb( $context ),
new WPSEO_Schema_Article( $context ),
new WPSEO_Schema_Author( $context ),
);
/**
* Filter: 'wpseo_schema_graph_pieces' - Allows adding pieces to the graph.
*
* @param WPSEO_Schema_Context $context An object with context variables.
*
* @api array $pieces The schema pieces.
*/
return apply_filters( 'wpseo_schema_graph_pieces', $pieces, $context );
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* WPSEO plugin file.
*
* @package WPSEO\Frontend\Schema
*/
if ( ! interface_exists( 'WPSEO_Graph_Piece' ) ) {
/**
* An interface for registering Schema Graph Pieces.
*
* @since 10.2
*/
interface WPSEO_Graph_Piece {
/**
* Add your piece of the graph.
*
* @return array|bool $graph A graph piece on success, false on failure.
*/
public function generate();
/**
* Determines whether or not a piece should be added to the graph.
*
* @return bool
*/
public function is_needed();
}
}