You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
795 lines
22 KiB
PHTML
795 lines
22 KiB
PHTML
8 months ago
|
<?php
|
||
|
/**
|
||
|
* Output the Schema.org markup in JSON-LD format.
|
||
|
*
|
||
|
* @since 0.9.0
|
||
|
* @package RankMath
|
||
|
* @subpackage RankMath\Schema
|
||
|
* @author Rank Math <support@rankmath.com>
|
||
|
*/
|
||
|
|
||
|
namespace RankMath\Schema;
|
||
|
|
||
|
use RankMath\Helper;
|
||
|
use RankMath\Helpers\Url;
|
||
|
use RankMath\Helpers\Str;
|
||
|
use RankMath\Helpers\Arr;
|
||
|
use RankMath\Paper\Paper;
|
||
|
use RankMath\Traits\Hooker;
|
||
|
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* JsonLD class.
|
||
|
*/
|
||
|
class JsonLD {
|
||
|
|
||
|
use Hooker;
|
||
|
|
||
|
/**
|
||
|
* Hold post object.
|
||
|
*
|
||
|
* @var WP_Post
|
||
|
*/
|
||
|
public $post = null;
|
||
|
|
||
|
/**
|
||
|
* Hold post ID.
|
||
|
*
|
||
|
* @var ID
|
||
|
*/
|
||
|
public $post_id = 0;
|
||
|
|
||
|
/**
|
||
|
* Hold post parts.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
public $parts = [];
|
||
|
|
||
|
/**
|
||
|
* The Constructor.
|
||
|
*/
|
||
|
public function setup() {
|
||
|
$this->action( 'rank_math/head', 'json_ld', 90 );
|
||
|
$this->action( 'rank_math/json_ld', 'add_context_data' );
|
||
|
$this->action( 'rank_math/json_ld/preview', 'generate_preview' );
|
||
|
new Block_Parser();
|
||
|
new Frontend();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Schema Preview. Used in the Code Validation in Pro.
|
||
|
*/
|
||
|
public function generate_preview() {
|
||
|
global $post;
|
||
|
|
||
|
if ( is_singular() ) {
|
||
|
$this->post = $post;
|
||
|
$this->post_id = $post->ID;
|
||
|
$this->get_parts();
|
||
|
}
|
||
|
|
||
|
$data = $this->do_filter( 'json_ld', [], $this );
|
||
|
unset( $data['BreadcrumbList'] );
|
||
|
|
||
|
// Preview schema.
|
||
|
$schema = \json_decode( file_get_contents( 'php://input' ), true );
|
||
|
$schema_id = $schema['schemaID'];
|
||
|
if ( isset( $data[ $schema_id ] ) ) {
|
||
|
$current_data = $data[ $schema_id ];
|
||
|
unset( $data[ $schema_id ] );
|
||
|
} else {
|
||
|
$current_data = array_pop( $data );
|
||
|
}
|
||
|
unset( $schema['schemaID'] );
|
||
|
|
||
|
$schema = $this->replace_variables( $schema );
|
||
|
$schema = $this->filter( $schema, $this, $data );
|
||
|
$schema = wp_parse_args( $schema['schema'], $current_data );
|
||
|
if ( ! empty( $schema['@type'] ) && in_array( $schema['@type'], [ 'WooCommerceProduct', 'EDDProduct' ], true ) ) {
|
||
|
$schema['@type'] = 'Product';
|
||
|
}
|
||
|
|
||
|
// Merge.
|
||
|
$data = array_merge( $data, [ $schema_id => $schema ] );
|
||
|
|
||
|
/**
|
||
|
* Filter to change the Code validation data..
|
||
|
*
|
||
|
* @param array $unsigned An array of data to output in JSON-LD.
|
||
|
*/
|
||
|
$data = $this->do_filter( 'schema/preview/validate', $this->do_filter( 'schema/validated_data', $this->validate_schema( $data ) ) );
|
||
|
|
||
|
echo wp_json_encode( array_values( $data ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function to get Old schema data. This code is used in the Schema_Converter to convert old schema data.
|
||
|
*
|
||
|
* @param int $post_id Post id for conversion.
|
||
|
* @param mixed $class Class instance of snippet.
|
||
|
* @return array
|
||
|
*/
|
||
|
public function get_old_schema( $post_id, $class ) {
|
||
|
global $post;
|
||
|
$post = get_post( $post_id ); // phpcs:ignore
|
||
|
$this->post = $post;
|
||
|
$this->post_id = $post_id;
|
||
|
setup_postdata( $post );
|
||
|
$this->get_parts();
|
||
|
|
||
|
/**
|
||
|
* Collect data to output in JSON-LD.
|
||
|
*
|
||
|
* @param array $unsigned An array of data to output in json-ld.
|
||
|
* @param JsonLD $unsigned JsonLD instance.
|
||
|
*/
|
||
|
return $class->process( [], $this );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Output the ld+json tag with the generated schema data.
|
||
|
*/
|
||
|
public function json_ld() {
|
||
|
global $post;
|
||
|
|
||
|
if ( is_singular() ) {
|
||
|
$this->post = $post;
|
||
|
$this->post_id = $post->ID;
|
||
|
$this->get_parts();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filter to collect data to output in JSON-LD.
|
||
|
*
|
||
|
* @param array $unsigned An array of data to output in JSON-LD.
|
||
|
* @param JsonLD $unsigned JsonLD instance.
|
||
|
*/
|
||
|
$data = $this->do_filter( 'json_ld', [], $this );
|
||
|
$data = $this->do_filter( 'schema/validated_data', $this->validate_schema( $data ) );
|
||
|
if ( is_array( $data ) && ! empty( $data ) ) {
|
||
|
|
||
|
$class = defined( 'RANK_MATH_PRO_FILE' ) ? 'schema-pro' : 'schema';
|
||
|
$json = [
|
||
|
'@context' => 'https://schema.org',
|
||
|
'@graph' => array_values( $data ),
|
||
|
];
|
||
|
|
||
|
$options = defined( 'RANKMATH_DEBUG' ) && RANKMATH_DEBUG ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES : JSON_UNESCAPED_SLASHES;
|
||
|
|
||
|
echo '<script type="application/ld+json" class="rank-math-' . esc_attr( $class ) . '">' . wp_json_encode( wp_kses_post_deep( $json ), $options ) . '</script>' . "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validate schema data. Removes invalid and empty values from the schema data.
|
||
|
*
|
||
|
* @param array $data Array of JSON-LD data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private function validate_schema( $data ) {
|
||
|
if ( ! is_array( $data ) || empty( $data ) ) {
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
foreach ( $data as $id => $value ) {
|
||
|
if ( is_array( $value ) ) {
|
||
|
// Remove aline @type.
|
||
|
if ( isset( $value['@type'] ) && 1 === count( $value ) ) {
|
||
|
unset( $data[ $id ] );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Remove empty review.
|
||
|
if ( 'review' === $id && isset( $value['@type'] ) ) {
|
||
|
if ( ! isset( $value['reviewRating'] ) || ! isset( $value['reviewRating']['ratingValue'] ) ) {
|
||
|
unset( $data[ $id ] );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Recursive.
|
||
|
$data[ $id ] = $this->validate_schema( $value );
|
||
|
}
|
||
|
|
||
|
// Remove empty values.
|
||
|
// Remove need of array_filter as this will go recursive.
|
||
|
if ( '' === $value ) {
|
||
|
unset( $data[ $id ] );
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Default Schema Data.
|
||
|
*
|
||
|
* @param array $data Array of JSON-LD data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function add_schema( $data ) {
|
||
|
global $post;
|
||
|
|
||
|
$schema = DB::get_schemas( $post->ID );
|
||
|
|
||
|
return array_merge( $data, $schema );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Default Schema Data.
|
||
|
*
|
||
|
* @param array $data Array of json-ld data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function add_context_data( $data ) {
|
||
|
$is_product_archive = $this->is_product_archive_page();
|
||
|
$can_add_global = $this->can_add_global_entities( $data, $is_product_archive );
|
||
|
$snippets = [
|
||
|
'\\RankMath\\Schema\\Publisher' => ! isset( $data['publisher'] ) && $can_add_global,
|
||
|
'\\RankMath\\Schema\\Website' => $can_add_global,
|
||
|
'\\RankMath\\Schema\\PrimaryImage' => is_singular() && ! post_password_required() && $can_add_global,
|
||
|
'\\RankMath\\Schema\\Breadcrumbs' => $this->can_add_breadcrumb(),
|
||
|
'\\RankMath\\Schema\\Webpage' => $can_add_global,
|
||
|
'\\RankMath\\Schema\\Author' => is_author() || ( is_singular() && $can_add_global ),
|
||
|
'\\RankMath\\Schema\\Products_Page' => $is_product_archive,
|
||
|
'\\RankMath\\Schema\\Singular' => ! post_password_required() && is_singular(),
|
||
|
];
|
||
|
|
||
|
foreach ( $snippets as $class => $can_run ) {
|
||
|
if ( $can_run ) {
|
||
|
$class = new $class();
|
||
|
$data = $class->process( $data, $this );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function to replace variables used in Schema fields.
|
||
|
*
|
||
|
* @param array $schemas Schema to replace.
|
||
|
* @param object $object Current Object.
|
||
|
* @param array $data Array of json-ld data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function replace_variables( $schemas, $object = [], $data = [] ) {
|
||
|
$new_schemas = [];
|
||
|
$object = empty( $object ) ? get_queried_object() : $object;
|
||
|
foreach ( $schemas as $key => $schema ) {
|
||
|
if ( 'metadata' === $key ) {
|
||
|
$new_schemas['isPrimary'] = ! empty( $schema['isPrimary'] );
|
||
|
|
||
|
if ( ! empty( $schema['type'] ) && 'custom' === $schema['type'] ) {
|
||
|
$new_schemas['isCustom'] = true;
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$this->replace_author( $schema, $data );
|
||
|
if ( is_array( $schema ) ) {
|
||
|
$new_schemas[ $key ] = $this->replace_variables( $schema, $object, $data );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Need this conditions to convert date to valid ISO 8601 format.
|
||
|
if ( in_array( $key, [ 'datePublished', 'uploadDate' ], true ) && '%date(Y-m-dTH:i:sP)%' === $schema ) {
|
||
|
$schema = '%date(Y-m-d\TH:i:sP)%';
|
||
|
}
|
||
|
if ( 'dateModified' === $key && '%modified(Y-m-dTH:i:sP)%' === $schema ) {
|
||
|
$schema = '%modified(Y-m-d\TH:i:sP)%';
|
||
|
}
|
||
|
|
||
|
$new_schemas[ $key ] = is_string( $schema ) && Str::contains( '%', $schema ) && ! filter_var( $schema, FILTER_VALIDATE_URL )
|
||
|
? Helper::replace_vars( $schema, $object ) : $schema;
|
||
|
if ( '' === $new_schemas[ $key ] ) {
|
||
|
unset( $new_schemas[ $key ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $new_schemas;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function to replace %author% with Author entity @id.
|
||
|
*
|
||
|
* @param array $schema Schema to replace.
|
||
|
* @param array $data Array of json-ld data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private function replace_author( &$schema, $data ) {
|
||
|
if ( empty( $data['ProfilePage'] ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( empty( $schema['author'] ) || ! isset( $schema['author']['name'] ) || ! in_array( $schema['author']['name'], [ '%name%', '%post_author%' ], true ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$schema['author'] = [
|
||
|
'@id' => $data['ProfilePage']['@id'],
|
||
|
'name' => get_the_author(),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filter schema data before adding it to ld+json.
|
||
|
*
|
||
|
* @param array $schemas Schema to replace.
|
||
|
* @param JsonLD $jsonld Instance of jsonld.
|
||
|
* @param array $data Array of json-ld data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function filter( $schemas, $jsonld, $data ) {
|
||
|
$new_schemas = [];
|
||
|
|
||
|
foreach ( $schemas as $key => $schema ) {
|
||
|
$type = is_array( $schema['@type'] ) ? $schema['@type'][0] : $schema['@type'];
|
||
|
$type = strtolower( $type );
|
||
|
$type = in_array( $type, [ 'musicgroup', 'musicalbum' ], true )
|
||
|
? 'music'
|
||
|
: ( in_array( $type, [ 'blogposting', 'newsarticle' ], true ) ? 'article' : $type );
|
||
|
$type = Str::contains( 'event', $type ) ? 'event' : $type;
|
||
|
$hook = 'snippet/rich_snippet_' . $type;
|
||
|
|
||
|
/**
|
||
|
* Short-circuit if 3rd party is interested generating his own data.
|
||
|
*/
|
||
|
$pre = $this->do_filter( $hook, false, $jsonld->parts, $data );
|
||
|
if ( false !== $pre ) {
|
||
|
$new_schemas[ $key ] = $this->do_filter( $hook . '_entity', $pre );
|
||
|
$new_schemas[ $key ] = $this->do_filter( 'snippet/rich_snippet_entity', $new_schemas[ $key ] );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$new_schemas[ $key ] = $this->do_filter( $hook . '_entity', $schema );
|
||
|
$new_schemas[ $key ] = $this->do_filter( 'snippet/rich_snippet_entity', $new_schemas[ $key ] );
|
||
|
}
|
||
|
|
||
|
return $new_schemas;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Whether to add global schema entities.
|
||
|
*
|
||
|
* @param array $data Array of json-ld data.
|
||
|
* @param bool $is_product_archive Whether the current page is a Product archive.
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function can_add_global_entities( $data = [], $is_product_archive = false ) {
|
||
|
if ( ! $is_product_archive && ( is_category() || is_tag() || is_tax() ) ) {
|
||
|
$queried_object = get_queried_object();
|
||
|
return ! Helper::get_settings( 'titles.remove_' . $queried_object->taxonomy . '_snippet_data' ) && ! $this->do_filter( 'snippet/remove_taxonomy_data', false, $queried_object->taxonomy );
|
||
|
}
|
||
|
|
||
|
if ( is_front_page() || ! is_singular() || ! Helper::can_use_default_schema( $this->post_id ) || ! empty( $data ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
$schemas = DB::get_schemas( $this->post_id );
|
||
|
if ( ! empty( $schemas ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Allow developer to remove global schema entities.
|
||
|
*
|
||
|
* @param bool $can_add
|
||
|
* @param JsonLD $unsigned JsonLD instance.
|
||
|
*/
|
||
|
return $this->do_filter( 'schema/add_global_entities', Helper::get_default_schema_type( $this->post_id, true ), $this );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Can add breadcrumb schema.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private function can_add_breadcrumb() {
|
||
|
/**
|
||
|
* Allow developer to disable the breadcrumb JSON-LD output.
|
||
|
*
|
||
|
* @param bool $unsigned Default: true
|
||
|
*/
|
||
|
return ! is_front_page() && Helper::is_breadcrumbs_enabled() && $this->do_filter( 'json_ld/breadcrumbs_enabled', true );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if current page is a WooCommerce archive page.
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
private function is_product_archive_page() {
|
||
|
return Helper::is_woocommerce_active() && ( ( is_tax() && in_array( get_query_var( 'taxonomy' ), get_object_taxonomies( 'product' ), true ) ) || is_shop() );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add property to entity.
|
||
|
*
|
||
|
* @param string $prop Name of the property to add into entity.
|
||
|
* @param array $entity Array of json-ld entity.
|
||
|
* @param string $key Property key to add into entity.
|
||
|
* @param array $data Array of json-ld data.
|
||
|
*/
|
||
|
public function add_prop( $prop, &$entity, $key = '', $data = [] ) {
|
||
|
if ( empty( $prop ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$hash = [
|
||
|
'email' => [ 'titles.email', 'email' ],
|
||
|
'phone' => [ 'titles.phone', 'telephone' ],
|
||
|
];
|
||
|
|
||
|
if ( isset( $hash[ $prop ] ) && $value = Helper::get_settings( $hash[ $prop ][0] ) ) { // phpcs:ignore
|
||
|
$entity[ $hash[ $prop ][1] ] = $value;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$perform = "add_prop_{$prop}";
|
||
|
if ( method_exists( $this, $perform ) ) {
|
||
|
$this->$perform( $entity, $key, $data );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add logo property to the entity.
|
||
|
*
|
||
|
* @param array $entity Array of JSON-LD entity.
|
||
|
*/
|
||
|
private function add_prop_image( &$entity ) {
|
||
|
$logo = Helper::get_settings( 'titles.knowledgegraph_logo' );
|
||
|
if ( ! $logo ) {
|
||
|
$logo_id = \get_option( 'site_logo' );
|
||
|
$logo = $logo_id ? wp_get_attachment_image_url( $logo_id ) : '';
|
||
|
}
|
||
|
|
||
|
if ( ! $logo ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$entity['logo'] = [
|
||
|
'@type' => 'ImageObject',
|
||
|
'@id' => home_url( '/#logo' ),
|
||
|
'url' => $logo,
|
||
|
'contentUrl' => $logo,
|
||
|
'caption' => $this->get_website_name(),
|
||
|
];
|
||
|
$this->add_prop_language( $entity['logo'] );
|
||
|
|
||
|
$attachment = wp_get_attachment_metadata( Helper::get_settings( 'titles.knowledgegraph_logo_id' ), true );
|
||
|
if ( ! $attachment ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$entity['logo']['width'] = $attachment['width'];
|
||
|
$entity['logo']['height'] = $attachment['height'];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add Language property to the entity.
|
||
|
*
|
||
|
* @param array $entity Array of JSON-LD entity.
|
||
|
*/
|
||
|
private function add_prop_language( &$entity ) {
|
||
|
$entity['inLanguage'] = $this->do_filter( 'schema/language', get_bloginfo( 'language' ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add Image property to entity.
|
||
|
*
|
||
|
* @param array $entity Array of json-ld entity.
|
||
|
* @param string $key Entity Key.
|
||
|
* @param array $data Schema Data.
|
||
|
*/
|
||
|
private function add_prop_thumbnail( &$entity, $key, $data ) {
|
||
|
if ( ! empty( $data['primaryImage'] ) ) {
|
||
|
$entity[ $key ] = [ '@id' => $data['primaryImage']['@id'] ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add isPartOf property to entity.
|
||
|
*
|
||
|
* @param array $entity Array of json-ld entity.
|
||
|
* @param string $key Entity Key.
|
||
|
*/
|
||
|
private function add_prop_is_part_of( &$entity, $key ) {
|
||
|
$hash = [
|
||
|
'website' => home_url( '/#website' ),
|
||
|
'webpage' => Paper::get()->get_canonical() . '#webpage',
|
||
|
];
|
||
|
|
||
|
if ( ! empty( $hash[ $key ] ) ) {
|
||
|
$entity['isPartOf'] = [ '@id' => $hash[ $key ] ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add publisher property to entity
|
||
|
*
|
||
|
* @param array $entity Entity.
|
||
|
* @param string $key Entity Key.
|
||
|
* @param array $data Schema Data.
|
||
|
*/
|
||
|
public function add_prop_publisher( &$entity, $key, $data ) {
|
||
|
if ( empty( $data['publisher'] ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$entity[ $key ] = [ '@id' => $data['publisher']['@id'] ];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add url property to entity.
|
||
|
*
|
||
|
* @param array $entity Array of JSON-LD entity.
|
||
|
*/
|
||
|
private function add_prop_url( &$entity ) {
|
||
|
if ( $url = Helper::get_settings( 'titles.url' ) ) { // phpcs:ignore
|
||
|
$entity['url'] = ! Url::is_relative( $url ) ? $url : 'http://' . $url;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add address property to entity.
|
||
|
*
|
||
|
* @param array $entity Array of JSON-LD entity.
|
||
|
*/
|
||
|
private function add_prop_address( &$entity ) {
|
||
|
if ( $address = Helper::get_settings( 'titles.local_address' ) ) { // phpcs:ignore
|
||
|
$entity['address'] = [ '@type' => 'PostalAddress' ] + $address;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add aggregateratings to entity.
|
||
|
*
|
||
|
* @param string $schema Schema to get data for.
|
||
|
* @param array $entity Array of JSON-LD entity to attach data to.
|
||
|
*/
|
||
|
public function add_ratings( $schema, &$entity ) {
|
||
|
$rating = Helper::get_post_meta( "snippet_{$schema}_rating" );
|
||
|
|
||
|
// Early Bail!
|
||
|
if ( ! $rating ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$entity['review'] = [
|
||
|
'author' => [
|
||
|
'@type' => 'Person',
|
||
|
'name' => get_the_author_meta( 'display_name' ),
|
||
|
],
|
||
|
'datePublished' => get_post_time( 'Y-m-d\TH:i:sP', false ),
|
||
|
'dateModified' => get_post_modified_time( 'Y-m-d\TH:i:sP', false ),
|
||
|
'reviewRating' => [
|
||
|
'@type' => 'Rating',
|
||
|
'ratingValue' => $rating,
|
||
|
'bestRating' => Helper::get_post_meta( "snippet_{$schema}_rating_max" ) ? Helper::get_post_meta( "snippet_{$schema}_rating_max" ) : 5,
|
||
|
'worstRating' => Helper::get_post_meta( "snippet_{$schema}_rating_min" ) ? Helper::get_post_meta( "snippet_{$schema}_rating_min" ) : 1,
|
||
|
],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get website name with a fallback to bloginfo( 'name' ).
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_website_name() {
|
||
|
return Helper::get_settings( 'titles.website_name', $this->get_organization_name() );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get website name with a fallback to bloginfo( 'name' ).
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_organization_name() {
|
||
|
$name = Helper::get_settings( 'titles.knowledgegraph_name' );
|
||
|
return $name ? $name : get_bloginfo( 'name' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set publisher/provider data for JSON-LD.
|
||
|
*
|
||
|
* @param array $entity Array of JSON-LD entity.
|
||
|
* @param array $organization Organization data.
|
||
|
* @param string $type Type data set to. Default: 'publisher'.
|
||
|
*/
|
||
|
public function set_publisher( &$entity, $organization, $type = 'publisher' ) {
|
||
|
$keys = [ '@context', '@type', 'url', 'name', 'logo', 'image', 'contactPoint', 'sameAs' ];
|
||
|
foreach ( $keys as $key ) {
|
||
|
if ( ! isset( $organization[ $key ] ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$entity[ $type ][ $key ] = 'logo' !== $key ? $organization[ $key ] : [
|
||
|
'@type' => 'ImageObject',
|
||
|
'url' => $organization[ $key ],
|
||
|
];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set address for JSON-LD.
|
||
|
*
|
||
|
* @param string $schema Schema to get data for.
|
||
|
* @param array $entity Array of JSON-LD entity to attach data to.
|
||
|
*/
|
||
|
public function set_address( $schema, &$entity ) {
|
||
|
$address = Helper::get_post_meta( "snippet_{$schema}_address" );
|
||
|
|
||
|
// Early Bail!
|
||
|
if ( ! is_array( $address ) || empty( $address ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$entity['address'] = [ '@type' => 'PostalAddress' ];
|
||
|
foreach ( $address as $key => $value ) {
|
||
|
$entity['address'][ $key ] = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set data to entity.
|
||
|
*
|
||
|
* Loop through post meta value grab data and attache it to the entity.
|
||
|
*
|
||
|
* @param array $hash Key to get data and Value to save as.
|
||
|
* @param array $entity Array of JSON-LD entity to attach data to.
|
||
|
*/
|
||
|
public function set_data( $hash, &$entity ) {
|
||
|
foreach ( $hash as $metakey => $dest ) {
|
||
|
$entity[ $dest ] = Helper::get_post_meta( $metakey, $this->post_id );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get post title.
|
||
|
*
|
||
|
* Retrieves the title in this order.
|
||
|
* 1. Custom post meta set in rich snippet
|
||
|
* 2. Headline template set in Titles & Meta
|
||
|
*
|
||
|
* @param int $post_id Post ID to get title for.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_post_title( $post_id = 0 ) {
|
||
|
$title = Helper::get_post_meta( 'snippet_name', $post_id );
|
||
|
|
||
|
if ( ! $title && ! empty( $this->post ) ) {
|
||
|
$title = Helper::replace_vars( Helper::get_settings( "titles.pt_{$this->post->post_type}_default_snippet_name", '%seo_title%' ), $this->post );
|
||
|
}
|
||
|
|
||
|
$title = $title ? $title : Paper::get()->get_title();
|
||
|
|
||
|
return Str::truncate( $title );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get post url.
|
||
|
*
|
||
|
* @param int $post_id Post ID to get URL for.
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_post_url( $post_id = 0 ) {
|
||
|
$url = Helper::get_post_meta( 'snippet_url', $post_id );
|
||
|
|
||
|
return $url ? $url : ( 0 === $post_id ? Paper::get()->get_canonical() : get_the_permalink( $post_id ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get product description.
|
||
|
*
|
||
|
* @param object $product Product Object.
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_product_desc( $product = [] ) {
|
||
|
if ( empty( $product ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( $description = Helper::get_post_meta( 'description', $product->get_id() ) ) { //phpcs:ignore
|
||
|
return $description;
|
||
|
}
|
||
|
|
||
|
$product_object = get_post( $product->get_id() );
|
||
|
$description = Paper::get_from_options( 'pt_product_description', $product_object, '%excerpt%' );
|
||
|
|
||
|
if ( ! $description ) {
|
||
|
$description = $product->get_short_description() ? $product->get_short_description() : $product->get_description();
|
||
|
}
|
||
|
|
||
|
$description = $this->do_filter( 'product_description/apply_shortcode', false ) ? do_shortcode( $description ) : Helper::strip_shortcodes( $description );
|
||
|
return wp_strip_all_tags( $description, true );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get product title.
|
||
|
*
|
||
|
* @param object $product Product Object.
|
||
|
* @return string
|
||
|
*/
|
||
|
public function get_product_title( $product = [] ) {
|
||
|
if ( empty( $product ) ) {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
if ( $title = Helper::get_post_meta( 'title', $product->get_id() ) ) { //phpcs:ignore
|
||
|
return $title;
|
||
|
}
|
||
|
|
||
|
$product_object = get_post( $product->get_id() );
|
||
|
|
||
|
$title = Paper::get_from_options( 'pt_product_title', $product_object, '%title% %sep% %sitename%' );
|
||
|
return $title ? $title : $product->get_name();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get post parts.
|
||
|
*/
|
||
|
private function get_parts() {
|
||
|
$parts = [
|
||
|
'title' => $this->get_post_title(),
|
||
|
'url' => $this->get_post_url(),
|
||
|
'canonical' => Paper::get()->get_canonical(),
|
||
|
'modified' => mysql2date( DATE_W3C, $this->post->post_modified, false ),
|
||
|
'published' => mysql2date( DATE_W3C, $this->post->post_date, false ),
|
||
|
'excerpt' => Helper::replace_vars( '%excerpt%', $this->post ),
|
||
|
];
|
||
|
|
||
|
// Description.
|
||
|
$desc = Helper::get_post_meta( 'snippet_desc' );
|
||
|
|
||
|
if ( ! $desc ) {
|
||
|
$desc = Helper::replace_vars( Helper::get_settings( "titles.pt_{$this->post->post_type}_default_snippet_desc" ), $this->post );
|
||
|
}
|
||
|
$parts['desc'] = $desc ? $desc : ( Helper::get_post_meta( 'description' ) ? Helper::get_post_meta( 'description' ) : $parts['excerpt'] );
|
||
|
|
||
|
// Author.
|
||
|
$author = Helper::get_post_meta( 'snippet_author' );
|
||
|
$parts['author'] = $author ? $author : get_the_author_meta( 'display_name', $this->post->post_author );
|
||
|
|
||
|
// Modified date cannot be before publish date.
|
||
|
if ( strtotime( $this->post->post_modified ) < strtotime( $this->post->post_date ) ) {
|
||
|
$parts['modified'] = $parts['published'];
|
||
|
}
|
||
|
|
||
|
$this->parts = $parts;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get global social profile URLs, to use in the `sameAs` property.
|
||
|
*
|
||
|
* @link https://developers.google.com/webmasters/structured-data/customize/social-profiles
|
||
|
*/
|
||
|
public function get_social_profiles() {
|
||
|
$profiles = [ Helper::get_settings( 'titles.social_url_facebook' ) ];
|
||
|
|
||
|
$twitter = Helper::get_settings( 'titles.twitter_author_names' );
|
||
|
if ( $twitter ) {
|
||
|
$profiles[] = "https://twitter.com/$twitter";
|
||
|
}
|
||
|
|
||
|
$addional_profiles = Helper::get_settings( 'titles.social_additional_profiles' );
|
||
|
if ( ! empty( $addional_profiles ) ) {
|
||
|
$profiles = array_merge( $profiles, Arr::from_string( $addional_profiles, "\n" ) );
|
||
|
}
|
||
|
|
||
|
return array_values( array_filter( $profiles ) );
|
||
|
}
|
||
|
}
|