File: /var/www/html/wp-content/plugins/wp-recipe-maker/includes/admin/import/class-wprm-import-tasty.php
<?php
/**
* Responsible for importing Tasty recipes.
*
* @link https://bootstrapped.ventures
* @since 1.23.0
*
* @package WP_Recipe_Maker
* @subpackage WP_Recipe_Maker/includes/admin/import
*/
/**
* Responsible for importing Tasty recipes.
*
* @since 1.23.0
* @package WP_Recipe_Maker
* @subpackage WP_Recipe_Maker/includes/admin/import
* @author Brecht Vandersmissen <brecht@bootstrapped.ventures>
*/
class WPRM_Import_Tasty extends WPRM_Import {
/**
* Get the UID of this import source.
*
* @since 1.23.0
*/
public function get_uid() {
return 'tasty';
}
/**
* Whether or not this importer requires a manual search for recipes.
*
* @since 1.23.0
*/
public function requires_search() {
return false;
}
/**
* Get the name of this import source.
*
* @since 1.23.0
*/
public function get_name() {
return 'Tasty Recipes';
}
/**
* Get HTML for the import settings.
*
* @since 1.23.0
*/
public function get_settings_html() {
$html = '<h4>Recipe Tags</h4>';
$tasty_taxonomies = array(
'method' => 'Method',
);
$wprm_taxonomies = WPRM_Taxonomies::get_taxonomies();
foreach ( $wprm_taxonomies as $wprm_taxonomy => $options ) {
$wprm_key = substr( $wprm_taxonomy, 5 );
$html .= '<label for="tasty-tags-' . $wprm_key . '">' . $options['name'] . ':</label> ';
$html .= '<select name="tasty-tags-' . $wprm_key . '" id="tasty-tags-' . $wprm_key . '">';
$html .= "<option value=\"\">Don't import anything for this tag</option>";
foreach ( $tasty_taxonomies as $name => $label ) {
$selected = $wprm_key === $name ? ' selected="selected"' : '';
$html .= '<option value="' . esc_attr( $name ) . '"' . esc_html( $selected ) . '>' . esc_html( $label ) . '</option>';
}
$html .= '</select>';
$html .= '<br />';
}
return $html;
}
/**
* Get the total number of recipes to import.
*
* @since 1.23.0
*/
public function get_recipe_count() {
$args = array(
'post_type' => 'tasty_recipe',
'post_status' => 'any',
'posts_per_page' => 1,
);
$query = new WP_Query( $args );
return $query->found_posts;
}
/**
* Get a list of recipes that are available to import.
*
* @since 1.23.0
* @param int $page Page of recipes to get.
*/
public function get_recipes( $page = 0 ) {
$recipes = array();
$limit = 100;
$offset = $limit * $page;
$args = array(
'post_type' => 'tasty_recipe',
'post_status' => 'any',
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => $limit,
'offset' => $offset,
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
$posts = $query->posts;
foreach ( $posts as $post ) {
$recipes[ $post->ID ] = array(
'name' => $post->post_title,
'url' => get_edit_post_link( $post->ID ),
);
}
}
return $recipes;
}
/**
* Search the list of recipes that are available to import.
*
* @since 8.2.0
* @param string $search Search term to use.
*/
public function get_recipes_search( $search ) {
$recipes = array();
$args = array(
'post_type' => 'tasty_recipe',
'post_status' => 'any',
'orderby' => 'relevance',
'posts_per_page' => -1,
's' => $search,
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
$posts = $query->posts;
foreach ( $posts as $post ) {
$recipes[ $post->ID ] = array(
'name' => $post->post_title,
'url' => get_edit_post_link( $post->ID ),
);
}
}
return $recipes;
}
/**
* Get recipe with the specified ID in the import format.
*
* @since 1.23.0
* @param mixed $id ID of the recipe we want to import.
* @param array $post_data POST data passed along when submitting the form.
*/
public function get_recipe( $id, $post_data ) {
$recipe = array(
'import_id' => $id,
'import_backup' => array(),
);
$post = get_post( $id );
// Featured Image.
$recipe['image_id'] = get_post_thumbnail_id( $id );
// Simple Matching.
$recipe['name'] = $post->post_title;
$recipe['summary'] = $this->strip_caption_shortcode( get_post_meta( $id, 'description', true ) );
$recipe['notes'] = $this->strip_caption_shortcode( get_post_meta( $id, 'notes', true ) );
$recipe['author_name'] = get_post_meta( $id, 'author_name', true );
if ( $recipe['author_name'] ) {
$recipe['author_display'] = 'custom';
}
// Servings.
$tasty_yield = get_post_meta( $id, 'yield', true );
$match = preg_match( '/^\s*\d+/', $tasty_yield, $servings_array );
if ( 1 === $match ) {
$servings = str_replace( ' ','', $servings_array[0] );
} else {
$servings = '';
}
$servings_unit = preg_replace( '/^\s*\d+\s*/', '', $tasty_yield );
$recipe['servings'] = $servings;
$recipe['servings_unit'] = $servings_unit;
// Recipe times.
$recipe['prep_time'] = $this->get_minutes_for_time( $this->get_tasty_time_value( $id, array( 'prep_time' ) ) );
$recipe['cook_time'] = $this->get_minutes_for_time( $this->get_tasty_time_value( $id, array( 'cook_time' ) ) );
$recipe['total_time'] = $this->get_minutes_for_time( $this->get_tasty_time_value( $id, array( 'total_time' ) ) );
$custom_time = $this->get_minutes_for_time( $this->get_tasty_time_value( $id, array( 'additional_time_value', 'additional_time' ) ) );
if ( $custom_time > 0 ) {
$recipe['custom_time'] = $custom_time;
$custom_time_label = trim( get_post_meta( $id, 'additional_time_label', true ) );
if ( '' !== $custom_time_label ) {
$recipe['custom_time_label'] = $custom_time_label;
}
}
if ( ! $recipe['total_time'] && ( $recipe['prep_time'] || $recipe['cook_time'] || $custom_time ) ) {
$recipe['total_time'] = intval( $recipe['prep_time'] ) + intval( $recipe['cook_time'] ) + intval( $custom_time );
}
// Recipe tags.
$recipe['tags'] = array();
$recipe['tags']['course'] = $this->get_tasty_tag_values( $id, 'category', 'tasty_recipe_category' );
$recipe['tags']['cuisine'] = $this->get_tasty_tag_values( $id, 'cuisine', 'tasty_recipe_cuisine' );
$recipe['tags']['keyword'] = $this->parse_comma_separated_values( get_post_meta( $id, 'keywords', true ) );
// Custom tags.
$wprm_taxonomies = WPRM_Taxonomies::get_taxonomies();
foreach ( $wprm_taxonomies as $wprm_taxonomy => $options ) {
$wprm_key = substr( $wprm_taxonomy, 5 );
$tag = isset( $post_data[ 'tasty-tags-' . $wprm_key ] ) ? $post_data[ 'tasty-tags-' . $wprm_key ] : false;
if ( $tag ) {
if ( 'method' === $tag ) {
$recipe['tags'][ $wprm_key ] = $this->get_tasty_tag_values( $id, 'method', 'tasty_recipe_method' );
} else {
$recipe['tags'][ $wprm_key ] = $this->parse_comma_separated_values( get_post_meta( $id, $tag, true ) );
}
}
}
// Diet tag.
$recipe['tags']['suitablefordiet'] = $this->get_tasty_tag_values( $id, 'diet', 'tasty_recipe_diet' );
$tasty_to_wprm = array(
'Diabetic' => 'DiabeticDiet',
'Gluten Free' => 'GlutenFreeDiet',
'Halal' => 'HalalDiet',
'Hindu' => 'HinduDiet',
'Kosher' => 'KosherDiet',
'Low Calorie' => 'LowCalorieDiet',
'Low Fat' => 'LowFatDiet',
'Low Lactose' => 'LowLactoseDiet',
'Low Salt' => 'LowSaltDiet',
'Vegan' => 'VeganDiet',
'Vegetarian' => 'VegetarianDiet',
);
foreach ( $recipe['tags']['suitablefordiet'] as $index => $tasty_diet ) {
if ( isset( $tasty_to_wprm[ $tasty_diet ] ) ) {
$recipe['tags']['suitablefordiet'][ $index ] = $tasty_to_wprm[ $tasty_diet ];
}
}
// Equipment.
$tasty_links_ids = get_post_meta( $id, 'tasty_links_ids', true );
$equipment = array();
if ( $tasty_links_ids && is_array( $tasty_links_ids ) ) {
foreach( $tasty_links_ids as $tasty_link_id ) {
$link = self::get_tasty_link( $tasty_link_id );
if ( $link && $link['name'] ) {
$equipment[] = array(
'name' => $link['name'],
);
// Import link and image if not already set.
$equipment_id = WPRM_Recipe_Sanitizer::get_equipment_id( $link['name'] );
if ( $equipment_id ) {
$existing_link = get_term_meta( $equipment_id, 'wprmp_equipment_link', true );
if ( ! $existing_link && $link['url'] ) {
update_term_meta( $equipment_id, 'wprmp_equipment_link', $link['url'] );
}
$existing_image_id = get_term_meta( $equipment_id, 'wprmp_equipment_image_id', true );
if ( ! $existing_image_id ) {
if ( $link['image_id'] ) {
update_term_meta( $equipment_id, 'wprmp_equipment_image_id', $link['image_id'] );
} elseif ( $link['image_url'] ) {
$image_id = WPRM_Import_Helper::get_or_upload_attachment( $id, $link['image_url'] );
if ( $image_id ) {
update_term_meta( $equipment_id, 'wprmp_equipment_image_id', $image_id );
}
}
}
}
}
}
}
$recipe['equipment'] = $equipment;
// Ingredients.
$tasty_ingredients = $this->parse_recipe_component_list( get_post_meta( $id, 'ingredients', true ) );
$ingredients = array();
foreach ( $tasty_ingredients as $tasty_group ) {
$group = array(
'name' => $tasty_group['name'],
'ingredients' => array(),
);
foreach ( $tasty_group['items'] as $tasty_item ) {
$text = trim( strip_tags( $tasty_item, '<a>' ) );
if ( ! empty( $text ) ) {
$group['ingredients'][] = array(
'raw' => $text,
);
}
}
$ingredients[] = $group;
}
$recipe['ingredients'] = $ingredients;
// Instructions.
$tasty_instructions = $this->parse_recipe_component_list( get_post_meta( $id, 'instructions', true ) );
$instructions = array();
foreach ( $tasty_instructions as $tasty_group ) {
$group = array(
'name' => $tasty_group['name'],
'instructions' => array(),
);
foreach ( $tasty_group['items'] as $tasty_item ) {
$text = trim( strip_tags( $tasty_item, '<a><strong><b><em><i><u><sub><sup>' ) );
// Find any images.
preg_match_all( '/<img[^>]+>/i', $tasty_item, $img_tags );
$image_ids = array();
foreach ( $img_tags[0] as $img_tag ) {
preg_match_all( '/src="([^"]*)"/i', $img_tag, $img );
if ( $img[1] ) {
$img_src = $img[1][0];
$image_id = WPRM_Import_Helper::get_or_upload_attachment( $id, $img_src );
if ( $image_id ) {
$image_ids[] = $image_id;
}
}
}
// If we have text, create instruction with text and first image
if ( ! empty( $text ) ) {
$instruction = array( 'text' => $text );
if ( ! empty( $image_ids ) ) {
$instruction['image'] = $image_ids[0];
}
$group['instructions'][] = $instruction;
// Add remaining images as separate instructions
for ( $i = 1; $i < count( $image_ids ); $i++ ) {
$group['instructions'][] = array( 'text' => '', 'image' => $image_ids[ $i ] );
}
} else if ( ! empty( $image_ids ) ) {
// No text, try to add first image to previous instruction
$last_instruction_index = count( $group['instructions'] ) - 1;
if ( $last_instruction_index >= 0 && ! isset( $group['instructions'][ $last_instruction_index ]['image'] ) ) {
// Add first image to previous instruction
$group['instructions'][ $last_instruction_index ]['image'] = $image_ids[0];
// Add remaining images as separate instructions
for ( $i = 1; $i < count( $image_ids ); $i++ ) {
$group['instructions'][] = array( 'text' => '', 'image' => $image_ids[ $i ] );
}
} else {
// Create new instructions for all images
foreach ( $image_ids as $image_id ) {
$group['instructions'][] = array( 'text' => '', 'image' => $image_id );
}
}
}
}
$instructions[] = $group;
}
$recipe['instructions'] = $instructions;
// Video.
$recipe['video_embed'] = get_post_meta( $id, 'video_url', true );
// Nutrition Facts.
$recipe['nutrition'] = array();
// Serving size.
$tasty_serving_size = get_post_meta( $id, 'serving_size', true );
$match = preg_match( '/^\s*\d+/', $tasty_serving_size, $servings_array );
if ( 1 === $match ) {
$servings = str_replace( ' ','', $servings_array[0] );
} else {
$servings = '';
}
$servings_unit = preg_replace( '/^\s*\d+\s*/', '', $tasty_serving_size );
$recipe['nutrition']['serving_size'] = $servings;
$recipe['nutrition']['serving_unit'] = $servings_unit;
$recipe['nutrition']['calories'] = get_post_meta( $id, 'calories', true );
$recipe['nutrition']['sugar'] = get_post_meta( $id, 'sugar', true );
$recipe['nutrition']['sodium'] = get_post_meta( $id, 'sodium', true );
$recipe['nutrition']['fat'] = get_post_meta( $id, 'fat', true );
$recipe['nutrition']['saturated_fat'] = get_post_meta( $id, 'saturated_fat', true );
$recipe['nutrition']['polyunsaturated_fat'] = get_post_meta( $id, 'unsaturated_fat', true );
$recipe['nutrition']['trans_fat'] = get_post_meta( $id, 'trans_fat', true );
$recipe['nutrition']['carbohydrates'] = get_post_meta( $id, 'carbohydrates', true );
$recipe['nutrition']['fiber'] = get_post_meta( $id, 'fiber', true );
$recipe['nutrition']['protein'] = get_post_meta( $id, 'protein', true );
$recipe['nutrition']['cholesterol'] = get_post_meta( $id, 'cholesterol', true );
return $recipe;
}
/**
* Replace the original recipe with the newly imported WPRM one.
*
* @since 1.23.0
* @param mixed $id ID of the recipe we want replace.
* @param mixed $wprm_id ID of the WPRM recipe to replace with.
* @param array $post_data POST data passed along when submitting the form.
*/
public function replace_recipe( $id, $wprm_id, $post_data ) {
// We don't know which posts use this recipe so we rely on the fallback shortcode.
}
/**
* Custom strtotime function for Tasty format.
*
* @since 1.23.0
* @param mixed $time Time to get in minutes.
* @param mixed $now Time now.
*/
public static function strtotime( $time, $now = null ) {
if ( null === $now ) {
$now = time();
}
// Parse string to remove any info in parentheses.
$time = preg_replace( '/\([^\)]+\)/' , '' , $time );
return strtotime( $time, $now );
}
/**
* Get the time in minutes.
*
* @since 1.23.0
* @param mixed $time Time to get in minutes.
*/
private function get_minutes_for_time( $time ) {
if ( ! $time ) {
return 0;
}
// Special case "00:15".
$first_five_characters = substr( $time, 0, 5 );
preg_match( '/(\d\d):([0-6]\d)/', $first_five_characters, $match );
if ( $match && isset( $match[2] ) ) {
$hours = intval( $match[1] );
$minutes = intval( $match[2] );
$time = 60 * $hours + $minutes;
}
// Assume a number is minutes.
if ( is_numeric( $time ) ) {
$time = "{$time} minutes";
}
$now = time();
$time = $this->strtotime( $time, $now );
return ( $time - $now ) / 60;
}
/**
* Get the first non-empty value for one or more Tasty time keys.
*
* @since 10.4.0
* @param int $id Recipe ID.
* @param string|array $meta_keys Meta key or array of keys to check.
*/
private function get_tasty_time_value( $id, $meta_keys ) {
$meta_keys = is_array( $meta_keys ) ? $meta_keys : array( $meta_keys );
foreach ( $meta_keys as $meta_key ) {
$value = get_post_meta( $id, $meta_key, true );
if ( is_array( $value ) ) {
$value = reset( $value );
}
if ( is_string( $value ) ) {
$value = trim( $value );
}
if ( is_scalar( $value ) && '' !== strval( $value ) ) {
return $value;
}
}
return '';
}
/**
* Parse comma separated values.
*
* @since 10.4.0
* @param string|array $value Value to parse.
*/
private function parse_comma_separated_values( $value ) {
$values = array();
$lookup = array();
$raw_values = is_array( $value ) ? $value : array( $value );
foreach ( $raw_values as $raw_value ) {
if ( is_array( $raw_value ) ) {
$raw_value = implode( ',', $raw_value );
}
if ( ! is_scalar( $raw_value ) ) {
continue;
}
$parts = explode( ',', strval( $raw_value ) );
foreach ( $parts as $part ) {
$part = trim( $part );
if ( '' !== $part && ! isset( $lookup[ $part ] ) ) {
$values[] = $part;
$lookup[ $part ] = true;
}
}
}
return $values;
}
/**
* Get Tasty tag values with taxonomy fallback support.
*
* @since 10.4.0
* @param int $id Recipe ID.
* @param string $legacy_meta_key Legacy meta key.
* @param string $taxonomy Optional taxonomy name.
*/
private function get_tasty_tag_values( $id, $legacy_meta_key, $taxonomy = '' ) {
if ( $taxonomy ) {
$terms = get_the_terms( $id, $taxonomy );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
$values = $this->parse_comma_separated_values( wp_list_pluck( $terms, 'name' ) );
if ( ! empty( $values ) ) {
return $values;
}
}
}
if ( ! $legacy_meta_key ) {
return array();
}
return $this->parse_comma_separated_values( get_post_meta( $id, $legacy_meta_key, true ) );
}
/**
* Blob to array.
*
* @since 1.23.0
* @param mixed $component Component to parse.
*/
private function parse_recipe_component_list( $component ) {
$component_list = array();
$component_group = array(
'name' => '',
'items' => array(),
);
// Strip caption shortcodes from the component
$component = $this->strip_caption_shortcode( $component );
// Make sure to split list items.
$component = str_ireplace( '</p>', '</p>' . PHP_EOL, $component );
$component = str_ireplace( '</li>', '</li>' . PHP_EOL, $component );
$component = str_ireplace( '</ul>', '</ul>' . PHP_EOL, $component );
$component = str_ireplace( '</ol>', '</ol>' . PHP_EOL, $component );
$bits = explode( PHP_EOL, $component );
foreach ( $bits as $bit ) {
$test_bit = trim( $bit );
if ( empty( $test_bit ) ) {
continue;
}
if ( WPRM_Import_Helper::is_heading( $bit ) ) {
$component_list[] = $component_group;
$component_group = array(
'name' => strip_tags( trim( $bit ) ),
'items' => array(),
);
} else {
$component_group['items'][] = trim( $bit );
}
}
$component_list[] = $component_group;
return $component_list;
}
/**
* Strip [caption] shortcode from text.
*
* @since 1.23.0
* @param string $text Text to strip caption shortcode from.
*/
private function strip_caption_shortcode( $text ) {
if ( empty( $text ) ) {
return $text;
}
// Remove [caption] shortcode wrapper but keep the image, remove caption text
$text = preg_replace( '/\[caption[^\]]*\](<img[^>]*>)\s*[^<]*\[\/caption\]/s', '$1', $text );
return $text;
}
/**
* Get Tasty Link data from ID.
*
* @since 8.7.0
* @param int $id ID of the Tasty Link.
*/
private function get_tasty_link( $id ) {
$link = false;
$post = get_post( $id );
if ( $post && 'tasty_link' === $post->post_type ) {
$link = array(
'id' => $id,
'name' => trim( $post->post_title ),
'url' => get_post_meta( $id, 'ta_link', true ),
'nofollow' => 'on' === get_post_meta( $id, 'tasty_links_rel_nofollow', true ) ? true : false,
'image_id' => false,
'image_url' => false,
);
if ( has_post_thumbnail( $post ) ) {
$image_id = get_post_thumbnail_id( $post );
if ( $image_id ) {
$link['image_id'] = intval( $image_id );
} else {
$image_url = get_post_meta( $id, 'tasty_links_image_url', true );
if ( $image_url ) {
$link['image_url'] = $image_url;
}
}
}
}
return $link;
}
}