<?php
defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );

class SQ_Models_Services_JsonLD extends SQ_Models_Abstract_Seo {

	private $_data = array();
	private $_types = array();
	private $_markups = array();

	public function __construct() {
		parent::__construct();

		if ( isset( $this->_post->sq->doseo ) && $this->_post->sq->doseo ) {
			if ( ! $this->_post->sq->do_jsonld ) {
				add_filter( 'sq_json_ld', array( $this, 'returnFalse' ) );

				return;
			}

			//decode the URL for json format
			if ( $this->_post->url ) {
				$this->_post->url = urldecode( esc_url( $this->_post->url ) );
			}

			//If not yet set
			$this->_post->sq->jsonld_types = array_filter( (array) $this->_post->sq->jsonld_types );
			if ( empty( $this->_post->sq->jsonld_types ) ) {
				//If not set, get the type saved by Open Graph
				if ( $this->_post->sq->og_type <> '' ) {
					$this->_post->sq->jsonld_types = array( $this->_post->sq->og_type );
				}
			}

			if ( ! empty( $this->_post->sq->jsonld_types ) ) {
				add_filter( 'sq_json_ld', array( $this, 'generateStructuredData' ) );

				add_filter( 'sq_json_ld', array( $this, 'generateJsonLd' ) );
				add_filter( 'sq_json_ld', array( $this, 'packJsonLd' ), 99 );

			}
		} else {
			add_filter( 'sq_json_ld', array( $this, 'returnFalse' ) );
		}

	}

	/**
	 * Sanitizes, encodes and outputs structured data.
	 *
	 * @return array|string
	 */
	public function generateJsonLd() {
		return $this->getStructuredData( $this->getDataTypes() ) ;
	}

	/**
	 * Pack the Structured Data
	 */
	public function packJsonLd( $data = array() ) {
		if ( ! empty( $data ) ) {
			//If custom JSON-LD and not Auto Draft
			if ( $this->_post->sq->jsonld && strpos( $this->_post->sq->jsonld, '"name":"Auto Draft"' ) === false ) {
				$safe = $this->renderCustomJsonLd( $this->_post->sq->jsonld );
				if ( $safe !== '' ) {
					return $safe;
				}

			} else {
				//Compatibility with ACF
				if ( SQ_Classes_Helpers_Tools::isPluginInstalled( 'advanced-custom-fields/acf.php' ) ) {
					if ( isset( $this->_post->ID ) && $this->_post->ID ) {
						if ( $_sq_jsonld_custom = get_post_meta( $this->_post->ID, '_sq_jsonld_custom', true ) ) {
							$safe = $this->renderCustomJsonLd( $_sq_jsonld_custom );
							if ( $safe !== '' ) {
								return $safe;
							}
						}
					}
				}

				return '<script type="application/ld+json">' . wp_json_encode( $data, SQ_DEBUG && ! SQ_Classes_Helpers_Tools::isAjax() ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES : JSON_UNESCAPED_SLASHES ) . '</script>';
			}
		}

		return false;
	}

	/**
	 * Render custom JSON-LD safely. Accepts either raw JSON or a pre-wrapped
	 * <script type="application/ld+json">...</script> block. Whatever's passed in
	 * is reduced to its JSON payload, validated, and re-emitted inside a fresh
	 * wrapper. Anything that can't be parsed as JSON is dropped - this is the
	 * choke point that blocks </script> XSS payloads.
	 */
	private function renderCustomJsonLd( $raw ) {
		if ( ! is_string( $raw ) || $raw === '' ) {
			return '';
		}

		//Strip all script wrappers, including malicious ones smuggled inside.
		$stripped = preg_replace( '#</?script[^>]*>#i', '', $raw );
		$decoded  = json_decode( trim( $stripped ), true );

		if ( ! is_array( $decoded ) ) {
			return '';
		}

		return '<script type="application/ld+json">'
			. wp_json_encode( $decoded, JSON_UNESCAPED_SLASHES )
			. '</script>';
	}

	/**
	 * Sets data.
	 *
	 * @param array $data Structured data.
	 * @param bool $reset Unset data (default: false).
	 *
	 * @return bool
	 */
	public function setData( $data, $reset = false ) {

		if ( ! isset( $data['@type'] ) || ! preg_match( '|^[a-zA-Z]{1,20}$|', $data['@type'] ) ) {
			return false;
		} else {
			if ( $data['@type'] <> 'Review' && in_array( strtolower( $data['@type'] ), $this->_types ) ) {
				return false;
			}

			$this->_types[] = strtolower( $data['@type'] );
		}

		if ( $reset && isset( $this->_data ) ) {
			unset( $this->_data );
		}

		$this->_data[] = $data;

		return true;
	}

	/**
	 * Gets data.
	 *
	 * @return array
	 */
	public function getData() {
		return apply_filters( 'sq_json_ld_data', $this->_data );
	}

	/**
	 * Structures and returns data.
	 *
	 * List of types available by default for specific request:
	 *
	 * 'product',
	 * 'review',
	 * 'breadcrumblist',
	 * 'website',
	 * 'order',
	 *
	 * @param array $types Structured data types.
	 *
	 * @return array
	 */
	public function getStructuredData( $types ) {
		$data = array();

		// Put together the values of same type of structured data.
		foreach ( $this->getData() as $type => $value ) {
			if ( is_numeric( $type ) ) {
				$data[ $value['@type'] ][]               = $value;
				$data[ strtolower( $value['@type'] ) ][] = $value;
			} else {
				$data[ $type ][] = $value;
			}
		}

		//mage array unique
		$types = array_unique( $types );

		// Wrap the multiple values of each type inside a graph... Then add context to each type.
		foreach ( $data as $type => $value ) {
			$data[ $type ] = count( (array) $value ) > 1 ? array( '@graph' => $value ) : $value[0];
		}

		// If requested types, pick them up... Finally, change the associative array to an indexed one.
		$data = $types ? array_values( array_intersect_key( $data, array_flip( $types ) ) ) : array_values( $data );

		if ( ! empty( $data ) ) {
			$data = array( '@context' => 'https://schema.org', '@graph' => $data );
		}

		return $data;
	}

	/**
	 * Get data types for pages.
	 *
	 * @return array
	 */
	public function getDataTypes() {
		return array_filter( apply_filters( 'sq_structured_data_type_for_page', $this->_types ) );
	}

	/**
	 * Make sure the data is sanitized
	 *
	 * @param  $var
	 *
	 * @return array|string
	 */
	public function clean( $var ) {
		if ( is_array( $var ) ) {
			return array_map( array( $this, 'clean' ), $var );
		} else {
			return is_scalar( $var ) ? sanitize_text_field( $var ) : $var;
		}
	}

	/**
	 * Set custom JsonLD from database postmeta
	 *
	 * @param  $markup
	 * @param  $post
	 * @param  $type
	 *
	 * @return mixed
	 */
	public function setCustomJsonLd( $markup, $post, $type ) {
		//Custom jsonld for this post id
		$sq_jsonld = get_post_meta( $post->ID, '_sq_jsonld_builder', true );

		if ( is_string( $sq_jsonld ) && ! empty( $sq_jsonld ) ) {
			$sq_jsonld = json_decode( $sq_jsonld, true );
		}

		if ( ! empty( $sq_jsonld ) && isset( $sq_jsonld[ strtolower( $type ) ] ) ) {
			foreach ( $sq_jsonld[ strtolower( $type ) ] as $key => $value ) {

				if ( is_array( $value ) ) {
					$value = @array_filter( $value );

					if ( empty( $value ) ) {
						continue;
					}

					foreach ( $value as $index => $value1 ) {
						if ( is_array( $value1 ) ) {
							$value1 = @array_filter( $value1 );
							if ( empty( $value1 ) ) {
								unset( $value[ $index ] );
							}
						}
					}

					if ( count( $value ) == 1 ) {
						continue;
					}

					//don't let numeric indexing
					if ( current( array_keys( $value ) ) == 0 ) {
						$value = array_values( $value );
					}
				}

				if ( $value ) {
					$markup[ $key ] = $value;
				}
			}
		}

		return $markup;

	}

	/**
	 * Generate JSON-LD Structured data based on jsonld types and post type
	 */
	public function generateStructuredData() {

		if ( $this->_post->post_type == 'home' ) {
			$jsonld_type      = SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_type' );
			$this->_markups[] = $this->getPublisherMarkup( $jsonld_type );
		}

		if ( in_array( 'website', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getWebsiteMarkup();
		}

		if ( in_array( 'newsarticle', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getArticleMarkup( 'NewsArticle' );
		}

		if ( in_array( 'article', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getArticleMarkup( 'Article' );
		}

		if ( in_array( 'QA Page', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getQAMarkup();
		}

		if ( in_array( 'question', $this->_post->sq->jsonld_types ) || in_array( 'FAQ page', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getQuestionMarkup();
		}

		if ( in_array( 'movie', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getMovieMarkup();
		}

		if ( in_array( 'video', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getVideoMarkup();
		}

		if ( in_array( 'recipe', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getRecipeMarkup();
		}

		if ( in_array( 'review', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getReviewMarkup();
		}

		if ( in_array( 'local store', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getLocalBusinessMarkup( 'Store' );
		}

		if ( in_array( 'local restaurant', $this->_post->sq->jsonld_types ) ) {
			$this->_markups[] = $this->getLocalBusinessMarkup( 'Restaurant' );
		}

		if ( $this->_post->post_type == 'profile' || in_array( 'profile', $this->_post->sq->jsonld_types ) ) {
			if ( $this->_post->post_author <> '' ) {
				$author_url = $this->getAuthorUrl();

				$markup['@type'] = 'Person';
				$markup['@id']   = $author_url . '#person';
				$markup['url']   = $author_url;
				$markup['name']  = $this->cleanText( $this->getAuthor( 'display_name' ) );

				//keep the author's personal website as sameAs only when it's a valid absolute URL
				$website = $this->getAuthor( 'user_url' );
				if ( $website <> '' && $website <> $author_url && filter_var( $website, FILTER_VALIDATE_URL ) ) {
					$markup['sameAs'] = esc_url( $website );
				}

				$this->_markups[] = $markup;
			}
		}

		if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_woocommerce' ) ) {
			if ( SQ_Classes_Helpers_Tools::isPluginInstalled( 'woocommerce/woocommerce.php' ) ) {
				// Generate structured data for Woocommerce 3+.
				if ( $this->_post->post_type == 'product' || in_array( 'product', $this->_post->sq->jsonld_types ) ) {
					$this->_markups[] = $this->getWoocommerceProductMarkup();
				}
			}
		}

		if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_breadcrumbs' ) ) {
			$this->_markups[] = $this->getBreadcrumbsMarkup();
		}

		//register the markup
		foreach ( $this->_markups as $markup ) {
			$this->setData( $markup );
		}
	}

	public function cleanText( $text ) {
		$text = str_replace( array( '&#034;', '&#8220;', '&#8221;' ), '"', $text );

		return $text;
	}

	/**
	 * Set the markup for the Article, NewsArticle Schema
	 *
	 * @param  $post_type
	 *
	 * @return mixed
	 */
	public function getArticleMarkup( $post_type ) {
		$markup          = array();
		$markup['@type'] = $post_type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $post_type );
		$markup['url']   = $this->_post->url;

		if ( isset( $this->_post->sq->title ) ) {
			$markup['headline'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
		}

		$markup['mainEntityOfPage'] = array(
			'@type' => 'WebPage',
			'url'   => $this->_post->url
		);

		if ( $this->_post->sq->og_media <> '' ) {
			$markup['thumbnailUrl'] = $this->_post->sq->og_media;
		}
		if ( isset( $this->_post->post_date ) ) {
			$markup['datePublished'] = wp_date( 'c', strtotime( $this->_post->post_date ) );
		}
		if ( isset( $this->_post->post_modified ) ) {
			$markup['dateModified'] = wp_date( 'c', strtotime( $this->_post->post_modified ) );
		}

		if ( $this->_post->sq->og_media <> '' ) {
			$markup['image'] = array(
				"@type"  => "ImageObject",
				"url"    => $this->_post->sq->og_media,
				"height" => 500,
				"width"  => 500,
			);
		} else {
			$this->_setMedia( $markup );
		}

		$markup['author'] = $this->getAuthorMarkup();

		if ( $publisher = $this->getPublisherMarkup( 'Organization' ) ) {
			$markup['publisher'] = $publisher;
		}

		if ( $this->_post->sq->keywords <> '' ) {
			$markup['keywords'] = $this->_post->sq->keywords;
		}

		return apply_filters( 'sq_jsonld_article_markup', $markup, $this->_post, $post_type );
	}

	/**
	 * Get Question Markup
	 *
	 * @return mixed
	 */
	public function getQAMarkup() {
		$type = 'QAPage';

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;

		if ( isset( $this->_post->sq->title ) && isset( $this->_post->sq->description ) ) {
			$markup['mainEntity'][] = array(
				"@type"          => "Question",
				"name"           => $this->cleanText( $this->_post->sq->title ),
				"text"           => $this->cleanText( $this->_post->sq->title ),
				"answerCount"    => 1,
				"upvoteCount"    => 0,
				"dateCreated"    => wp_date( 'c', strtotime( $this->_post->post_date ) ),
				"author"         => $this->getAuthorMarkup(),
				"acceptedAnswer" => array(
					"@type"       => "Answer",
					"text"        => $this->cleanText( $this->_post->sq->description ),
					"upvoteCount" => 0,
					"url"         => $this->_post->url,
					"author"      => $this->getAuthorMarkup(),
					"dateCreated" => wp_date( 'c', strtotime( $this->_post->post_date ) ),
				)
			);
		}

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get Question Markup
	 *
	 * @return mixed
	 */
	public function getQuestionMarkup() {
		$type = 'FAQPage';

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;

		if ( isset( $this->_post->sq->title ) && isset( $this->_post->sq->description ) ) {
			$markup['mainEntity'][] = array(
				"@type"          => "Question",
				"name"           => $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) ),
				"acceptedAnswer" => array(
					"@type" => "Answer",
					"text"  => $this->cleanText( $this->truncate( $this->_post->sq->description, 0, $this->_post->sq->jsonld_description_maxlength ) ),
				)
			);
		}

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get Movie Markup
	 *
	 * @return mixed
	 */
	public function getVideoMarkup() {
		$markup = array();
		$type   = 'VideoObject';

		if ( $videos = $this->getPostVideos() ) {
			$markup['@type'] = $type;
			$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
			$markup['url']   = $this->_post->url;

			if ( isset( $this->_post->sq->title ) ) {
				$markup['name'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
			}

			if ( isset( $this->_post->sq->description ) ) {
				$markup['description'] = $this->cleanText( $this->truncate( $this->_post->sq->description, 0, $this->_post->sq->jsonld_description_maxlength ) );
			}

			if ( isset( $this->_post->post_date ) ) {
				$markup['uploadDate'] = wp_date( 'c', strtotime( $this->_post->post_date ) );
			}

			if ( ! empty( $videos ) ) {
				foreach ( $videos as $index => $video ) {
					if ( $video['src'] <> '' ) {
						$markup['thumbnailUrl'] = array(
							$video['thumbnail']
						);

						$markup['embedUrl'] = $video['src'];
					}
				}

			}

			$markup['publisher'] = $this->getPublisherMarkup( 'Organization' );
		}

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get Movie Markup
	 *
	 * @return mixed
	 */
	public function getMovieMarkup() {
		$type = 'Movie';

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;

		if ( isset( $this->_post->sq->title ) ) {
			$markup['name'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
		}

		if ( isset( $this->_post->sq->description ) ) {
			$markup['description'] = $this->cleanText( $this->truncate( $this->_post->sq->description, 0, $this->_post->sq->jsonld_description_maxlength ) );
		}

		if ( isset( $this->_post->post_date ) ) {
			$markup['dateCreated'] = wp_date( 'c', strtotime( $this->_post->post_date ) );
		}

		if ( $this->_post->sq->og_media <> '' ) {
			$markup['image'] = array(
				"@type"  => "ImageObject",
				"url"    => $this->_post->sq->og_media,
				"height" => 500,
				"width"  => 500,
			);
		} else {
			$this->_setMedia( $markup );
		}

		$markup['director']  = $this->getAuthorMarkup();
		$markup['publisher'] = $this->getPublisherMarkup( 'Organization' );

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get Recipe Markup
	 *
	 * @return mixed
	 */
	public function getRecipeMarkup() {
		$type = 'Recipe';

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;

		if ( isset( $this->_post->sq->title ) ) {
			$markup['name']     = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
			$markup['headline'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
			$markup['keywords'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
		}

		if ( isset( $this->_post->sq->description ) ) {
			$markup['description'] = $this->cleanText( $this->truncate( $this->_post->sq->description, 0, $this->_post->sq->jsonld_description_maxlength ) );
		}

		if ( isset( $this->_post->post_date ) ) {
			$markup['datePublished'] = wp_date( 'c', strtotime( $this->_post->post_date ) );
		}

		if ( $this->_post->sq->og_media <> '' ) {
			$markup['image'] = array(
				"@type"  => "ImageObject",
				"url"    => $this->_post->sq->og_media,
				"height" => 500,
				"width"  => 500,
			);
		} else {
			$this->_setMedia( $markup );
		}

		$markup['mainEntityOfPage'] = array(
			'@type' => 'WebPage',
			'url'   => $this->_post->url
		);

		$markup['prepTime']  = 'PT10M';
		$markup['cookTime']  = 'PT20M';
		$markup['totalTime'] = 'PT30M';
		$markup['nutrition'] = array(
			'@type'    => 'NutritionInformation',
			'calories' => '0 calories'
		);

		if ( $this->_post->sq->keywords <> '' ) {
			$markup['keywords'] = $this->_post->sq->keywords;
		}

		if ( $videos = $this->getPostVideos() ) {
			foreach ( $videos as $index => $video ) {
				if ( $video['src'] <> '' ) {
					$markup['video'] = array(
						"@type"        => "VideoObject",
						"embedUrl"     => $videos['src'],
						"name"         => ( isset( $markup['name'] ) ? $markup['name'] : '' ),
						"description"  => ( isset( $markup['description'] ) ? $markup['description'] : '' ),
						"thumbnailUrl" => array(
							$video['thumbnail']
						),
						"height"       => 500,
						"width"        => 500,
					);
					break;
				}
			}
		}

		$markup['author']    = $this->getAuthorMarkup();
		$markup['publisher'] = $this->getPublisherMarkup( 'Organization' );

		if ( $this->_post->sq->keywords <> '' ) {
			$markup['keywords'] = $this->_post->sq->keywords;
		}

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get Review Markup
	 *
	 * @param array $reviewObject
	 *
	 * @return mixed
	 */
	public function getReviewMarkup( $reviewObject = false ) {
		$type = 'Review';

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;

		if ( isset( $this->_post->sq->title ) ) {
			$markup['name'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
		}

		if ( isset( $this->_post->sq->description ) ) {
			$markup['reviewBody'] = $this->cleanText( $this->truncate( $this->_post->sq->description, 0, $this->_post->sq->jsonld_description_maxlength ) );
		}

		$markup['author']    = $this->getAuthorMarkup();
		$markup['publisher'] = $this->getPublisherMarkup( 'Organization' );

		if ( ! $reviewObject ) {
			$reviewObject = $this->getPublisherMarkup( 'Organization' );
		}

		$markup['itemReviewed'] = $reviewObject;

		$markup['reviewRating'] = array(
			'@type'       => 'Rating',
			'ratingValue' => 5,
		);

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get Local Business Markup
	 *
	 * @param string $type
	 *
	 * @return mixed
	 */
	public function getLocalBusinessMarkup( $type = 'Store' ) {
		$organization = $this->getPublisherMarkup( 'Organization' );

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;


		if ( isset( $organization['name'] ) && $organization['name'] ) {
			$markup['name'] = $organization['name'];
		} elseif ( isset( $this->_post->sq->title ) ) {
			$markup['name'] = $this->_post->sq->title;
			$markup['name'] = str_replace( '"', '\"', $markup['name'] );
		}

		if ( isset( $organization['logo'] ) && ! empty( $organization['logo'] ) ) {
			$markup['image'] = $organization['logo'];
		}
		if ( isset( $organization['address'] ) && ! empty( $organization['address'] ) ) {
			$markup['address'] = $organization['address'];
		}
		if ( isset( $organization['place']['geo'] ) && ! empty( $organization['place']['geo'] ) ) {
			$markup['geo'] = $organization['place']['geo'];
		}
		if ( isset( $organization['contactPoint']['telephone'] ) && ! empty( $organization['contactPoint']['telephone'] ) ) {
			$markup['telephone'] = $organization['contactPoint']['telephone'];
		}

		$markup['priceRange'] = "$";
		$markup['menu']       = $this->_post->url;

		//Set default local SEO data
		$jsonldLocal = SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_local' );
		if ( ! empty( $jsonldLocal ) ) {
			foreach ( $jsonldLocal as $key => $value ) {

				if ( is_array( $value ) ) {
					$value = @array_filter( $value );

					if ( empty( $value ) ) {
						continue;
					}

					foreach ( $value as $index => $value1 ) {
						if ( is_array( $value1 ) ) {
							$value1 = @array_filter( $value1 );
							if ( $key == 'openingHoursSpecification' ) {
								if ( count( $value1 ) < 3 ) {
									unset( $value[ $index ] );
								}
							} elseif ( count( $value1 ) < 2 ) {
								unset( $value[ $index ] );
							}

						}
					}

					if ( count( $value ) == 1 ) {
						continue;
					}

					if ( current( array_keys( $value ) ) == 0 ) {
						$value = array_values( $value );
					}
				}

				if ( $value ) {
					//create cuisine array
					if ( $key == 'servesCuisine' && is_string( $value ) ) {
						$value = explode( ',', $value );
					}
					$markup[ $key ] = $value;
				}
			}
		}

		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get the markup for the website Schema
	 *
	 * @return mixed
	 */
	public function getWebsiteMarkup() {
		$jsonld      = SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld' );
		$jsonld_type = SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_type' );
		$type        = 'WebSite';

		$markup          = array();
		$markup['@type'] = $type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $type );
		$markup['url']   = $this->_post->url;

		if ( $jsonld[ $jsonld_type ]['name'] ) {
			$markup['name'] = $jsonld[ $jsonld_type ]['name'];
		} elseif ( isset( $this->_post->sq->title ) ) {
			$markup['name'] = $this->cleanText( $this->truncate( $this->_post->sq->title, 0, $this->_post->sq->jsonld_title_maxlength ) );
		}

		if ( isset( $this->_post->sq->description ) ) {
			$markup['headline'] = $this->cleanText( $this->truncate( $this->_post->sq->description, 0, $this->_post->sq->jsonld_description_maxlength ) );
		}

		$markup['mainEntityOfPage'] = array(
			'@type' => 'WebPage',
			'url'   => $this->_post->url
		);

		if ( $this->_post->sq->og_media <> '' ) {
			$markup['thumbnailUrl'] = $this->_post->sq->og_media;
		}
		if ( isset( $this->_post->post_date ) ) {
			$markup['datePublished'] = wp_date( 'c', strtotime( $this->_post->post_date ) );
		}
		if ( isset( $this->_post->post_modified ) ) {
			$markup['dateModified'] = wp_date( 'c', strtotime( $this->_post->post_modified ) );
		}

		if ( $this->_post->sq->og_media <> '' ) {
			$markup['image'] = array(
				"@type"  => "ImageObject",
				"url"    => $this->_post->sq->og_media,
				"height" => 500,
				"width"  => 700,
			);
		} else {
			$this->_setMedia( $markup );
		}

		//Show search bar for products and shops
		//the str_replace is added for compatibility with WPML plugin
		if ( $this->_post->post_type == 'product' || $this->_post->post_type == 'shop' ) {
			$markup['potentialAction'] = array(
				'@type'       => 'SearchAction',
				'target'      => str_replace( 'search_term_string', '{search_term_string}', home_url( '?s=search_term_string&post_type=product' ) ),
				'query-input' => 'required name=search_term_string',
			);
		} else {
			$markup['potentialAction'] = array(
				'@type'       => 'SearchAction',
				'target'      => str_replace( 'search_term_string', '{search_term_string}', home_url( '?s=search_term_string' ) ),
				'query-input' => 'required name=search_term_string',
			);
		}

		if ( $author = $this->getAuthorMarkup() ) {
			$markup['author'] = $author;
		}

		if ( $publisher = $this->getPublisherMarkup( $jsonld_type ) ) {
			$markup['publisher'] = $publisher;
		}

		if ( $this->_post->sq->keywords <> '' ) {
			$markup['keywords'] = $this->_post->sq->keywords;
		}


		return apply_filters( 'sq_jsonld_' . strtolower( $type ) . '_markup', $markup, $this->_post, $type );
	}

	/**
	 * Get the markup for the Author Schema
	 */
	public function getAuthorMarkup() {
		$markup = array();

		if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_global_person' ) ) {
			$markup = $this->getPublisherMarkup( 'Person' );
		} else {

			$author_url   = $this->getAuthorUrl();
			$display_name = $this->getAuthor( 'display_name' );

			if ( $author_url <> '' && $display_name <> '' ) {
				$markup = array(
					"@type" => "Person",
					"@id"   => $author_url . "#person",
					"url"   => $author_url,
					"name"  => $this->cleanText( $display_name ),
				);

				//keep the author's personal website as sameAs only when it's a valid absolute URL
				$website = $this->getAuthor( 'user_url' );
				if ( $website <> '' && $website <> $author_url && filter_var( $website, FILTER_VALIDATE_URL ) ) {
					$markup['sameAs'] = esc_url( $website );
				}
			}
		}

		return apply_filters( 'sq_jsonld_author_markup', $markup, $this->_post, 'author', $this->_author );

	}

	/**
	 * Get the publisher markup
	 *
	 * @param  $jsonld_type
	 *
	 * @return array|bool
	 */
	public function getPublisherMarkup( $jsonld_type ) {
		$jsonld = SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld' );

		$markup['@type'] = $jsonld_type;
		$markup['@id']   = $this->_post->url . '#' . strtolower( $jsonld_type );
		$markup['url']   = $this->_post->url;

		if ( isset( $jsonld[ $jsonld_type ] ) ) {
			$markup = array(
				"@type" => $jsonld_type,
				"@id"   => $this->_post->url . "#$jsonld_type",
				"url"   => $this->_post->url,
				"name"  => ( $jsonld[ $jsonld_type ]['name'] ? $jsonld[ $jsonld_type ]['name'] : get_bloginfo( 'title' ) ),
			);

			foreach ( $jsonld[ $jsonld_type ] as $key => $value ) {
				if ( $key == 'place' ) {
					continue;
				} //don't show geo for the organization schema
				if ( is_array( $value ) ) {
					$value = @array_filter( $value );

					if ( empty( $value ) ) {
						continue;
					}

					foreach ( $value as $key1 => $value1 ) {
						if ( is_array( $value1 ) ) {
							$value1 = @array_filter( $value1 );
							if ( count( $value1 ) == 1 ) {
								unset( $value[ $key1 ] );
							}
						}
					}

					if ( count( $value ) == 1 ) {
						continue;
					}

					$value['@id'] = $this->_post->url . "#" . strtolower( $key );
				}

				if ( $value ) {

					if ( $key == 'logo' && isset( $value['url'] ) && $value['url'] <> '' ) {
						if ( defined( 'WP_CONTENT_DIR' ) && $imagepath = str_replace( content_url(), WP_CONTENT_DIR, $value['url'] ) ) {
							if ( file_exists( $imagepath ) ) {
								list( $width, $height ) = @getimagesize( $imagepath );
								$value['width']  = $width;
								$value['height'] = $height;
							}
						}

						if ( $jsonld[ $jsonld_type ]['name'] ) {
							$value['caption'] = $jsonld[ $jsonld_type ]['name'];
						}
					}

					$markup[ $key ] = $value;

				}
			}

			$socials = SQ_Classes_Helpers_Tools::getOption( 'socials' );

			//Load the social media
			$jsonld_socials = array();
			if ( isset( $socials['facebook_site'] ) && $socials['facebook_site'] <> '' ) {
				$jsonld_socials[] = $socials['facebook_site'];
			}
			if ( isset( $socials['twitter_site'] ) && $socials['twitter_site'] <> '' ) {
				$jsonld_socials[] = $socials['twitter_site'];
			}
			if ( isset( $socials['instagram_url'] ) && $socials['instagram_url'] <> '' ) {
				$jsonld_socials[] = $socials['instagram_url'];
			}
			if ( isset( $socials['linkedin_url'] ) && $socials['linkedin_url'] <> '' ) {
				$jsonld_socials[] = $socials['linkedin_url'];
			}
			if ( isset( $socials['pinterest_url'] ) && $socials['pinterest_url'] <> '' ) {
				$jsonld_socials[] = $socials['pinterest_url'];
			}
			if ( isset( $socials['youtube_url'] ) && $socials['youtube_url'] <> '' ) {
				$jsonld_socials[] = $socials['youtube_url'];
			}

			if ( ! empty( $jsonld_socials ) ) {
				$markup['sameAs'] = $jsonld_socials;
			}
		}

		return apply_filters( 'sq_jsonld_publisher_markup', $markup, $this->_post, $jsonld_type );
	}

	/**
	 * Generates BreadcrumbList structured data.
	 */
	public function getBreadcrumbsMarkup() {
		$root = $crumbs = $lists = $markup = array();

		//show the breadcrumbs
		if ( $this->_post->post_type <> 'home' ) {
			///////////////////////////// Home Page
			$post = SQ_Classes_ObjController::getClass( 'SQ_Models_Snippet' )->setHomePage();

			if ( $post->ID == 0 || $this->_post->ID <> $post->ID ) {
				$root[] = array(
					( $post->sq->title <> '' ? $post->sq->title : $post->post_title ),
					$post->url,
				);
			}

			if ( $this->_post->post_type == 'category' && isset( $this->_post->term_id ) && isset( $this->_post->taxonomy ) ) {
				$parents = get_ancestors( $this->_post->term_id, $this->_post->taxonomy );
				if ( ! empty( $parents ) ) {
					$parents = array_reverse( $parents );

					foreach ( $parents as $parent ) {
						$parent = get_term( $parent );
						if ( ! is_wp_error( $parent ) ) {
							$crumbs[] = array(
								$parent->name,
								get_term_link( $parent->term_id, $this->_post->taxonomy ),
							);
						}
					}

					$lists[] = $crumbs;
				}
			} elseif ( $this->_post->post_type == 'product' ) {
				if ( class_exists( 'WC_Product' ) ) {
					$product = new WC_Product( $this->_post->ID );

					//Get all categories
					if ( $product instanceof WC_Product ) {
						$taxonomy = 'product_cat';

						if ( (int) $this->_post->sq->primary_category > 0 ) {
							//check if the primary category was selected by the client
							$category_ids = array( (int) $this->_post->sq->primary_category );
						} else {
							$category_ids = $product->get_category_ids();
						}

						if ( ! empty( $category_ids ) ) {
							foreach ( $category_ids as $category ) {
								$parents = get_ancestors( $category, $taxonomy );

								if ( ! empty( $parents ) ) {

									foreach ( $parents as $parent ) {
										$parent = get_term( $parent );
										if ( ! is_wp_error( $parent ) ) {
											$crumbs[] = array(
												$parent->name,
												get_term_link( $parent->term_id, $taxonomy ),
											);
										}
									}

									$category = get_term( $category, $taxonomy );
									if ( isset( $category->name ) && $category->name <> '' ) {
										$crumbs[] = array(
											$category->name,
											get_term_link( $category->term_id, $taxonomy ),
										);
									}

									$lists[] = $crumbs;
								} else {

									$category = get_term( $category, $taxonomy );
									if ( isset( $category->name ) && $category->name <> '' ) {
										$crumbs[] = array(
											$category->name,
											get_term_link( $category->term_id, $taxonomy ),
										);
									}

									$lists[] = $crumbs;
								}
							}
						}
					}
				}
			} else {
				/////////////////////// Parent Categories
				if ( (int) $this->_post->sq->primary_category > 0 ) {
					$categories = array( get_category( (int) $this->_post->sq->primary_category ) );
				} else {
					$categories = get_the_category( $this->_post->ID );
				}

				if ( ! empty( $categories ) ) {
					foreach ( $categories as $category ) {
						if ( isset( $category->term_id ) && isset( $category->taxonomy ) ) {
							$crumbs  = [];
							$parents = get_ancestors( $category->term_id, $category->taxonomy );

							if ( ! empty( $parents ) ) {
								$parents = array_reverse( $parents );

								foreach ( $parents as $parent ) {
									$parent = get_term( $parent );
									if ( isset( $parent->name ) && $parent->name <> '' ) {
										$crumbs[] = array(
											$parent->name,
											get_term_link( $parent->term_id, $category->taxonomy ),
										);
									}
								}

								if ( isset( $category->name ) && $category->name <> '' ) {
									$crumbs[] = array(
										$category->name,
										get_term_link( $category->term_id, $category->taxonomy ),
									);
								}

								$lists[] = $crumbs;
							} elseif ( isset( $category->name ) && $category->name <> '' ) {
								$crumbs[] = array(
									$category->name,
									get_term_link( $category->term_id, $category->taxonomy ),
								);

								$lists[] = $crumbs;
							}
						}
					}
				}
			}


			if ( ! empty( $crumbs ) ) {
				$markup['@type']           = 'BreadcrumbList';
				$markup['@id']             = $this->_post->url . '#' . 'breadcrumblist';
				$markup['itemListElement'] = array();

				foreach ( $lists as $list ) {
					//merge and reset the keys
					$list = array_merge( $root, $list );
					$list = array_values( $list );

					////////////////////// Current post
					$list[] = array(
						( $this->_post->sq->title <> '' ? $this->_post->sq->title : $this->_post->post_title ),
						$this->_post->url,
					);

					$itemListElement = array();
					foreach ( $list as $key => $crumb ) {
						$itemListElement[ $key ] = array(
							'@type'    => 'ListItem',
							'position' => $key + 1,
							'item'     => array(
								'@id'  => $crumb[1],
								'name' => $this->cleanText( $crumb[0] )
							),
						);
					}

					$markup['itemListElement'][] = $itemListElement;
				}
			}
		}

		return apply_filters( 'sq_jsonld_breadcrumbs_markup', $markup, $this->_post, 'breadcrumbs' );

	}

	/**
	 *
	 * Set the Image from Feature image
	 *
	 * @param $markup
	 */
	public function _setMedia( &$markup ) {
		$images = $this->getPostImages();
		if ( ! empty( $images ) ) {
			$image = current( $images );
			if ( isset( $image['src'] ) ) {
				$markup['image'] = array(
					"@type"  => "ImageObject",
					"url"    => $image['src'],
					"height" => 500,
					"width"  => 500,
				);
				if ( isset( $image['width'] ) ) {
					$markup['image']["width"] = $image['width'];
				}
				if ( isset( $image['height'] ) ) {
					$markup['image']["height"] = $image['height'];
				}
			}
		}
	}

	/**
	 * Generates Product structured data.
	 *
	 * @return array|void
	 */
	public function getWoocommerceProductMarkup() {
		$markup = array();

		try {

			if ( ! function_exists( 'wc_get_product' ) ) {
				return $markup;
			}

			if ( ! $product = wc_get_product( $this->_post->ID ) ) {
				return $markup;
			}

			if ( ! method_exists( $product, 'get_id' ) || ! method_exists( $product, 'get_price' ) ) {
				return $markup;
			}

			$shop_name = get_bloginfo( 'name' );
			$shop_url  = home_url();

			if ( function_exists( 'get_woocommerce_currency' ) ) {
				$currency = get_woocommerce_currency();
			}

			$sq_woocommerce = get_post_meta( $this->_post->ID, '_sq_woocommerce', true );
			$wc_fields      = array( 'mpn'  => 'mpn',
			                         'gtin' => 'gtin',
			                         'ean'  => 'gtin13',
			                         'upc'  => 'gtin12',
			                         'isbn' => 'isbn'
			);

			$markup['@type'] = 'Product';
			$markup['url']   = get_permalink( $product->get_id() );
			$markup['@id']   = $markup['url'] . '#' . 'product';

			if ( method_exists( $product, 'get_name' ) ) {
				$markup['name'] = $this->cleanText( $product->get_name() );
			} else {
				$markup['name'] = $this->cleanText( $product->get_title() );
			}

			if ( method_exists( $product, 'get_short_description' ) ) {
				$markup['description'] = $this->cleanText( wp_strip_all_tags( $product->get_short_description() ? $product->get_short_description() : $product->get_description() ) );
			}

			if ( method_exists( $product, 'get_image_id' ) ) {
				if ( $image = wp_get_attachment_url( $product->get_image_id() ) ) {
					$markup['image'] = $image;
				}
			}

			//By default, set the price available for 1 year
			$price_valid_until = gmdate( 'Y-m-d', strtotime( '+12 Month' ) );
			if ( method_exists( $product, 'get_date_on_sale_to' ) && method_exists( $product, 'get_date_on_sale_from' ) && is_object( $product->get_date_on_sale_to() ) && is_object( $product->get_date_on_sale_from() ) ) {
				if ( $product->get_date_on_sale_from() && method_exists( $product->get_date_on_sale_from(), 'getTimestamp' ) ) {
					if ( $product->get_date_on_sale_from() && gmdate( 'Y-m-d', $product->get_date_on_sale_from()->getTimestamp() ) <= gmdate( 'Y-m-d' ) ) {
						if ( method_exists( $product->get_date_on_sale_to(), 'getTimestamp' ) ) {
							//Set the price available until the offer ends
							$price_valid_until = gmdate( 'Y-m-d', $product->get_date_on_sale_to()->getTimestamp() );
						}
					}
				}
			}

			//Get the product price
			$price = $product->get_price();

			//Get the price with VAT if exists
			if ( function_exists( 'wc_get_price_including_tax' ) && function_exists( 'wc_tax_enabled' ) && function_exists( 'wc_prices_include_tax' ) ) {
				if ( wc_tax_enabled() && ! wc_prices_include_tax() && 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) {
					$price = wc_get_price_including_tax( $product, array( 'price' => $price ) );
				}
			}

			$offer_price = 0;
			if ( function_exists( 'wc_format_decimal' ) && function_exists( 'wc_get_price_decimals' ) ) {
				$offer_price = wc_format_decimal( $price, wc_get_price_decimals() );
			}

			$markup_offer = array(
				'@type'           => 'Offer',
				'price'           => $offer_price,
				'priceValidUntil' => $price_valid_until,
				'url'             => get_permalink( $product->get_id() ),
				'priceCurrency'   => $currency,
				'availability'    => 'https://schema.org/' . $stock = ( $product->is_in_stock() ? 'InStock' : 'OutOfStock' ),
				'sku'             => ( method_exists( $product, 'get_sku' ) ) ? ( $product->get_sku() <> '' ? $product->get_sku() : '' ) : '',
				'image'           => ( method_exists( $product, 'get_image_id' ) ) ? wp_get_attachment_url( $product->get_image_id() ) : '-',
				'description'     => ( method_exists( $product, 'get_description' ) ? $this->cleanText( $product->get_description() ) : $this->cleanText( $product->get_title() ) ),
				'seller'          => array(
					'@type' => 'Organization',
					'name'  => $shop_name,
					'url'   => $shop_url,
				),
			);

			//Get the variation prices
			if ( $product->is_type( 'variable' ) && method_exists( $product, 'get_variation_prices' ) && method_exists( $product, 'get_variation_regular_price' ) && method_exists( $product, 'get_variation_sale_price' ) ) {
				$prices = $product->get_variation_prices();

				if ( isset( $prices['price'] ) ) {
					//Get the product price
					$price    = $product->get_price();
					$minprice = $product->get_variation_price();
					$maxprice = $product->get_variation_price( 'max' );

					//Get the price with VAT if exists
					if ( function_exists( 'wc_get_price_including_tax' ) && function_exists( 'wc_tax_enabled' ) && function_exists( 'wc_prices_include_tax' ) ) {
						if ( wc_tax_enabled() && ! wc_prices_include_tax() && 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) {
							$price    = wc_get_price_including_tax( $product, array( 'price' => $price ) );
							$minprice = wc_get_price_including_tax( $product, array( 'price' => $minprice ) );
							$maxprice = wc_get_price_including_tax( $product, array( 'price' => $maxprice ) );
						}
					}

					if ( function_exists( 'wc_format_decimal' ) && function_exists( 'wc_get_price_decimals' ) ) {
						$markup_offer['priceSpecification'] = array(
							'@type'                 => 'UnitPriceSpecification',
							'price'                 => wc_format_decimal( $price, wc_get_price_decimals() ),
							'minPrice'              => wc_format_decimal( $minprice, wc_get_price_decimals() ),
							'maxPrice'              => wc_format_decimal( $maxprice, wc_get_price_decimals() ),
							'priceCurrency'         => $currency,
							'valueAddedTaxIncluded' => wc_prices_include_tax() ? 'true' : 'false'
						);
					}

					unset( $markup_offer['price'] );
					unset( $markup_offer['priceCurrency'] );
				}
			} elseif ( function_exists( 'wc_prices_include_tax' ) && function_exists( 'wc_format_decimal' ) && function_exists( 'wc_get_price_decimals' ) ) {
				$markup_offer['priceSpecification'] = array(
					'@type'                 => 'UnitPriceSpecification',
					'price'                 => wc_format_decimal( $product->get_price(), wc_get_price_decimals() ),
					'priceCurrency'         => $currency,
					'valueAddedTaxIncluded' => wc_prices_include_tax() ? 'true' : 'false'
				);

				unset( $markup_offer['price'] );
				unset( $markup_offer['priceCurrency'] );
			}

			$markup['sku'] = ( method_exists( $product, 'get_sku' ) ) ? ( $product->get_sku() <> '' ? $product->get_sku() : '' ) : '';


			//Set default values if WooCommerce default is active
			if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_product_defaults' ) ) {
				//Get all categories
				$categories = $product->get_category_ids();
				if ( ! empty( $categories ) ) {
					foreach ( $categories as $category ) {
						$category = get_term( $category, 'product_cat' );
						if ( isset( $category->name ) && $category->name <> '' ) {
							$markup['brand'] = array(
								'@type' => 'Brand',
								'name'  => $category->name,
							);
						}
					}
				}

				if ( $markup['sku'] == '' ) {
					$markup['sku'] = '-';
				}
				if ( $markup_offer['sku'] == '' ) {
					$markup_offer['sku'] = '-';
				}
				$markup['mpn']       = '-';
				$markup_offer['mpn'] = '-';
			}

			//Set custom values if WooCommerce custom is active
			if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_product_custom' ) ) {

				foreach ( $wc_fields as $field => $jsonkey ) {
					if ( isset( $sq_woocommerce[ $field ] ) && $sq_woocommerce[ $field ] <> '' ) {
						$markup[ $jsonkey ]       = $sq_woocommerce[ $field ];
						$markup_offer[ $jsonkey ] = $sq_woocommerce[ $field ];
					}
				}

				if ( isset( $sq_woocommerce['brand'] ) && $sq_woocommerce['brand'] <> '' ) {
					$markup['brand'] = array(
						'@type' => 'Brand',
						'name'  => $sq_woocommerce['brand'],
					);
				}
			}


			//Set the offer
			$markup['offers'] = $markup_offer;

			if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_product_defaults' ) ) {
				//If rating and reviews
				if ( method_exists( $product, 'get_rating_count' ) && $product->get_rating_count() ) {

					//Only if it's set in Squirrly to remove duplicates
					//otherwise let Woocommerce show the reviews
					if ( SQ_Classes_Helpers_Tools::getOption( 'sq_jsonld_clearcode' ) ) {
						$markup['aggregateRating'] = array(
							'@type'       => 'AggregateRating',
							'ratingValue' => $product->get_average_rating(),
							'ratingCount' => $product->get_rating_count(),
							'reviewCount' => $product->get_review_count(),
						);

						//Set the reviews
						$markup['review'] = $this->getProductReviewMarkup( $product );
					}

				} else { //add default data?

					//Add data if no reviews for Google validation
					$markup['aggregateRating'] = array(
						'@type'       => 'AggregateRating',
						'ratingValue' => 5,
						'ratingCount' => 1,
						'reviewCount' => 1,
					);

					$markup['review'][] = array(
						'@type'         => 'Review',
						'reviewRating'  => array(
							'@type'       => 'Rating',
							'ratingValue' => 5,
						),
						'author'        => array(
							'@type' => 'Person',
							'name'  => ( isset( $markup['name'] ) ? $markup['name'] : 'John Doe' ),
						),
						'reviewBody'    => ( isset( $markup['description'] ) ? $markup['description'] : '-' ),
						'datePublished' => ( method_exists( $product, 'get_date_created' ) && $product->get_date_created() && method_exists( $product->get_date_created(), 'getTimestamp' ) ) ? gmdate( 'Y-m-d', $product->get_date_created()->getTimestamp() ) : '',
					);
				}
			}

			$otherbrands = array();

			//compatible with Perfect Woocommerce Brands
			if ( SQ_Classes_Helpers_Tools::isPluginInstalled( 'perfect-woocommerce-brands/perfect-woocommerce-brands.php' ) ) {
				$brands = wp_get_post_terms( $product->get_id(), 'pwb-brand' );
				foreach ( $brands as $brand ) {
					$otherbrands[] = $brand->name;
				}
			}

			//compatible with YITH WooCommerce Brands Add-on
			if ( SQ_Classes_Helpers_Tools::isPluginInstalled( 'yith-woocommerce-brands-add-on/init.php' ) ) {
				$brands = wp_get_post_terms( $product->get_id(), 'yith_product_brand' );
				foreach ( $brands as $brand ) {
					$otherbrands[] = $brand->name;
				}
			}

			if ( ! empty( $otherbrands ) ) {
				$markup['brand'] = $otherbrands;
			}


			return $markup;

		} catch ( Exception $e ) {

		}
	}

	/**
	 * Generates Review structured data.
	 *
	 * @param  $product
	 *
	 * @return array | false
	 */
	public function getProductReviewMarkup( $product ) {
		$markup = array();

		if ( ! method_exists( $product, 'get_id' ) ) {
			return false;
		}

		if ( function_exists( 'wc_review_ratings_enabled' ) && wc_review_ratings_enabled() && function_exists( 'get_comments' ) && function_exists( 'get_comment_meta' ) ) {

			$reviews = get_comments( array(
					'number'      => 10,
					'post_id'     => $product->get_id(),
					'status'      => 'approve',
					'post_status' => 'publish',
					'post_type'   => 'product',
					'parent'      => 0,
					'meta_query'  => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
						array(
							'key'     => 'rating',
							'type'    => 'NUMERIC',
							'compare' => '>',
							'value'   => 0,
						),
					),
				) );

			if ( ! empty( $reviews ) ) {
				foreach ( $reviews as $review ) {
					if ( method_exists( $review, 'comment_ID' ) ) {
						$markup[] = array(
							'@type'         => 'Review',
							'reviewRating'  => array(
								'@type'       => 'Rating',
								'bestRating'  => '5',
								'ratingValue' => get_comment_meta( $review->comment_ID, 'rating', true ),
								'worstRating' => '1',
							),
							'author'        => array(
								'@type' => 'Person',
								'name'  => get_comment_author( $review ),
							),
							'reviewBody'    => get_comment_text( $review ),
							'datePublished' => get_comment_date( 'c', $review ),
						);
					}


				}
			}
		}

		return $markup;

	}

}
