795 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			795 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?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 ) );
 | 
						|
	}
 | 
						|
}
 |