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.

362 lines
8.2 KiB
PHP

<?php
/**
* The AIO Schema Rich Snippets Import Class
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\Admin\Importers
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\Admin\Importers;
use RankMath\Helper;
use RankMath\Admin\Admin_Helper;
use RankMath\Helpers\DB;
use RankMath\Helpers\Str;
defined( 'ABSPATH' ) || exit;
/**
* Import_AIO_Rich_Snippet class.
*/
class AIO_Rich_Snippet extends Plugin_Importer {
/**
* The plugin name.
*
* @var string
*/
protected $plugin_name = 'AIO Schema Rich Snippet';
/**
* Plugin options meta key.
*
* @var string
*/
protected $meta_key = '_bsf_post_type';
/**
* Option keys to import and clean.
*
* @var array
*/
protected $option_keys = [ 'bsf_', 'bsf_%' ];
/**
* Choices keys to import.
*
* @var array
*/
protected $choices = [ 'postmeta' ];
/**
* Import post meta of plugin.
*
* @return array
*/
protected function postmeta() {
$this->set_pagination( $this->get_post_ids( true ) );
// Set Converter.
foreach ( $this->get_post_ids() as $snippet_post ) {
$type = $this->is_allowed_type( $snippet_post->meta_value );
$meta_keys = $this->get_metakeys( $type );
if ( false === $type || false === $meta_keys ) {
continue;
}
$this->set_postmeta( $snippet_post->post_id, $type, $meta_keys );
}
return $this->get_pagination_arg();
}
/**
* Set snippet meta.
*
* @param int $post_id Post ID.
* @param string $type Type to get keys for.
* @param array $meta_keys Array of meta keys to save.
*/
private function set_postmeta( $post_id, $type, $meta_keys ) {
$data = [];
foreach ( $meta_keys as $snippet_key => $snippet_value ) {
$value = get_post_meta( $post_id, '_bsf_' . $snippet_key, true );
$this->validate_schema_data( $data, $value, $snippet_value );
}
if ( empty( $data ) ) {
return;
}
// Convert post now.
$data['@type'] = $this->validate_type( $type );
$data['metadata'] = [
'title' => Helper::sanitize_schema_title( $data['@type'] ),
'type' => 'template',
'isPrimary' => 1,
'shortcode' => uniqid( 's-' ),
];
update_post_meta( $post_id, 'rank_math_schema_' . $data['@type'], $data );
}
/**
* Validate schema type.
*
* @param string $type Schema Type.
*/
private function validate_type( $type ) {
if ( 'software' === $type ) {
return 'SoftwareApplication';
}
if ( 'video' === $type ) {
return 'VideoObject';
}
return ucfirst( $type );
}
/**
* Validate schema data.
*
* @param array $data Schema entity data.
* @param string $value Entity value.
* @param string $key Entity key.
*/
private function validate_schema_data( &$data, $value, $key ) {
if ( ! Str::contains( '.', $key ) ) {
$data[ $key ] = $value;
return;
}
$element = explode( '.', $key );
if ( 2 === count( $element ) ) {
$this->add_type( $data[ $element[0] ], $element[0] );
$data[ $element[0] ][ $element[1] ] = $value;
return;
}
if ( count( $element ) > 2 ) {
$this->add_type( $data[ $element[0] ], $element[0] );
$this->add_type( $data[ $element[0] ][ $element[1] ], $element[1] );
$data[ $element[0] ][ $element[1] ][ $element[2] ] = $value;
}
}
/**
* Add property type.
*
* @param array $data Schema entity data.
* @param string $key Entity key.
*/
private function add_type( &$data, $key ) {
if ( 'location' === $key ) {
$data['@type'] = 'Place';
}
if ( 'address' === $key ) {
$data['@type'] = 'PostalAddress';
}
if ( 'offers' === $key ) {
$data['@type'] = 'Offer';
}
if ( 'brand' === $key ) {
$data['@type'] = 'Brand';
}
if ( 'review' === $key ) {
$data['@type'] = 'Review';
}
if ( 'reviewRating' === $key ) {
$data['@type'] = 'Rating';
}
if ( 'nutrition' === $key ) {
$data['@type'] = 'NutritionInformation';
}
}
/**
* Get the actions which can be performed for the plugin.
*
* @return array
*/
public function get_choices() {
return [
'postmeta' => esc_html__( 'Import Schemas', 'rank-math' ) . Admin_Helper::get_tooltip( esc_html__( 'Import all Schema data for Posts, Pages, and custom post types.', 'rank-math' ) ),
];
}
/**
* Get all post IDs of all allowed post types only.
*
* @param bool $count If we need count only for pagination purposes.
* @return int|array
*/
protected function get_post_ids( $count = false ) {
$paged = $this->get_pagination_arg( 'page' );
$table = DB::query_builder( 'postmeta' )->where( 'meta_key', '_bsf_post_type' );
return $count ? absint( $table->selectCount( 'meta_id' )->getVar() ) :
$table->page( $paged - 1, $this->items_per_page )->get();
}
/**
* Get snippet types.
*
* @return array
*/
private function get_types() {
return [
'2' => 'event',
'5' => 'person',
'6' => 'product',
'7' => 'recipe',
'8' => 'software',
'9' => 'video',
'10' => 'article',
'11' => 'service',
];
}
/**
* Is snippet type allowed.
*
* @param string $type Type to check.
*
* @return bool
*/
private function is_allowed_type( $type ) {
$types = $this->get_types();
return isset( $types[ $type ] ) ? $types[ $type ] : false;
}
/**
* Get meta keys hash to import.
*
* @param string $type Type to get keys for.
*
* @return array
*/
private function get_metakeys( $type ) {
$hash = [
'event' => $this->get_event_fields(),
'product' => $this->get_product_fields(),
'recipe' => $this->get_recipe_fields(),
'software' => $this->get_software_fields(),
'video' => $this->get_video_fields(),
'article' => [
'article_name' => 'headline',
'article_desc' => 'description',
],
'person' => [
'people_fn' => 'name',
'people_nickname' => 'description',
'people_job_title' => 'jobTitle',
'people_street' => 'address.streetAddress',
'people_local' => 'address.addressLocality',
'people_region' => 'address.addressRegion',
'people_postal' => 'address.postalCode',
],
'service' => [
'service_type' => 'serviceType',
'service_desc' => 'description',
],
];
return isset( $hash[ $type ] ) ? $hash[ $type ] : false;
}
/**
* Get event fields.
*
* @return array
*/
private function get_event_fields() {
return [
'event_title' => 'name',
'event_desc' => 'description',
'event_organization' => 'location.name',
'event_street' => 'location.address.streetAddress',
'event_local' => 'location.address.addressLocality',
'event_region' => 'location.address.addressRegion',
'event_postal_code' => 'location.address.postalCode',
'event_start_date' => 'startDate',
'event_end_date' => 'endDate',
'event_price' => 'offers.price',
'event_cur' => 'offers.priceCurrency',
'event_ticket_url' => 'offers.url',
];
}
/**
* Get product fields.
*
* @return array
*/
private function get_product_fields() {
return [
'product_brand' => 'brand.name',
'product_name' => 'name',
'product_price' => 'offers.price',
'product_cur' => 'offers.priceCurrency',
'product_status' => 'offers.availability',
];
}
/**
* Get recipe fields.
*
* @return array
*/
private function get_recipe_fields() {
return [
'recipes_name' => 'name',
'recipes_desc' => 'description',
'recipes_preptime' => 'prepTime',
'recipes_cooktime' => 'cookTime',
'recipes_totaltime' => 'totalTime',
'recipes_ingredient' => 'recipeIngredient',
'recipes_nutrition' => 'nutrition.calories',
];
}
/**
* Get software fields.
*
* @return array
*/
private function get_software_fields() {
return [
'software_rating' => 'review.reviewRating.ratingValue',
'software_price' => 'offers.price',
'software_cur' => 'offers.priceCurrency',
'software_name' => 'name',
'software_os' => 'operatingSystem',
'software_cat' => 'applicationCategory',
];
}
/**
* Get video fields.
*
* @return array
*/
private function get_video_fields() {
return [
'video_title' => 'name',
'video_desc' => 'description',
'video_thumb' => 'thumbnailUrl',
'video_url' => 'contentUrl',
'video_emb_url' => 'embedUrl',
'video_duration' => 'duration',
];
}
}