WPML Compatible WP Ultimate Recipe to WP Recipe Maker Migration Script

The importer script supplied with WP Recipe Maker to convert WP Ultimate Recipe posts into WP Recipe Maker recipes does not work with multilingual posts managed with WPML. The importer works properly for posts written in the default site language but does not work for posts written in other languages used by the site.

The WP Recipe Maker importer fails in a WPML site because the supplied import script converts the WP Ultimate Recipe posts into regular post types before the importer runs. This recipe post type conversion only happens for posts written in the default language of the site. After posts written in the default language are converted to WP Recipe Maker recipe cards the importer is no longer able to work with posts written in a site’s other languages.

If you have 100 recipe posts in English [default language] and 100 recipe posts in Spanish [alternate language] the importer will convert the English recipe posts to regular post types before it converts the recipe meta in the post into a WP Recipe Maker recipe card and shortcode. The Spanish WP Ultimate Recipe posts are not translated and the site can no longer manage the Spanish recipe posts.

How to Recover Those Recipes

You have two options:

  1. Restore a database backup then disable WPML and rerun the WP Ultimate Recipe Maker importer script. This may or may not work badly.
  2. Do not restore a database backup. Use the alternate WPML compatible recipe importer script shown below here.

You can skip the rest of this post if option 1 worked for you.

An Alternate Recipe Importer Script

This guide shows you how to convert WP Ultimate Recipe posts into WP Recipe Maker recipe cards in WPML site.

For future reference, the fix so far has been to:

Move the recipes rendered invisible by the importer. To do this,

Create a database backup

Use a backup plugin like Updraft Plus or download a database backup in cPanel by going to Backup > Download a MySQL Database Backup and clicking the name of the database to download.

Install the WPML compatible recipe importer script

Replace the WP Ultimate Recipe to WP Recipe Maker import script code with the script code shown further down in this post.

Use the plugin editor by going to

  • WP Dashbaord > Plugins > Editor
  • Select WP Recipe Maker using the file selector on the top right of the plugin editor screen
  • Choose Includes > Admin > Import > class-wprm-import-wpultimaterecipe.php
  • Select all the code in the file
  • Replace the code with the custom WP Ultimate Recipe Maker import script shown below here.
  • Save the new custom import script

Alternatively, if the plugin editor is disabled, login to cPanel and use the file manager (or use an FTP program) to edit the file found at wp-content/plugins/wp-recipe-maker/includes/admin/import/class-wprm-import-wpultimaterecipe.php.

Install Post Type Switcher

Install Post Type Switcher from the WordPress repository. Go to

  • WP Dashboard > Plugins > Add New
  • Search for Post Type Switcher
  • Install and activate the plugin

Disable WPML

Disable WPML to make the hidden WP Ultimate Recipe posts visible

Switch Post Types

Switch the recipe post type to regular post type.

  • View the WP Ultimate Recipe Maker posts list page at WP Dashboard > Recipes. The recipe posts will show now that WPML is disabled.
  • Select all the recipe posts
  • Click Quick Edit
  • Switch the post type to just plain old ‘post’

Because WPML is disabled the WP Ultimate Recipe ‘recipe’ posts will become regular posts in the default site language. Their post language version associations are maintained and will each post will restore to its proper site language version when WPML is reactivated. If the WP Ultimate Recipe posts were written in Spanish, when WPML is reactivated they will be restored to the Spanish version of the site as regular posts.

Run the recipe importer script

Convert the old recipe posts into WP Recipe Maker recipe cards.

  • Go to WP Dashbaord > WP Recipe Maker > Import Posts
  • Run the available importers. One of these Importers will be the new WP Ultimate Recipe Maker importer.

The recipe information in a WP Ultimate Recipe post is stored in each recipe post’s meta fields. This recipe data is preserved when the WP Ultimate Recipe post type is switched to the standard WordPress post type.

The import script converts any WPUR recipe meta data found in a post into a WPRM recipe card post type. The script only converts the meta data into a new recipe card if a shortcode for a WP Ultimate Recipe recipe card is found within a post. Any WP Ultimate Recipe shortcode found in a post is converted to a WP Recipe Maker shortcode. This prevents recipes being imported multiple times.

Activate WPML

Reload the recipe importer page to confirm all recipes have been imported and reactivate WPML when the recipe import is complete.

When WPML is reactivated all posts will restore to their proper language version of the site.

The Custom WP Ultimate Recipe Importer Script

This custom import script replaces the file found at wp-content/plugins/wp-recipe-maker/includes/admin/import/class-wprm-import-wpultimaterecipe.php.

Replace the full contents of class-wprm-import-wpultimaterecipe.php with the custom code.

<?php
/**
 * Responsible for importing WP Ultimate Recipe recipes.
 *
 * @link       https://journalxtra.com/wordpress/quicksnips/wpml-compatible-wp-ultimate-recipe-to-wp-recipe-maker-migration-script/
 * @since      1.3.0
 *
 * @package    WP_Recipe_Maker
 * @subpackage WP_Recipe_Maker/includes/admin/import
 */

/**
 * Responsible for importing WP Ultimate Recipe recipes.
 *
 * @since      1.3.0
 * @package    WP_Recipe_Maker
 * @subpackage WP_Recipe_Maker/includes/admin/import
 * @author     Brecht Vandersmissen / Lee Hodson
 */
class WPRM_Import_Wpultimaterecipe extends WPRM_Import {
    /**
     * Get the UID of this import source.
     *
     * @since    1.3.0
     */
    public function get_uid() {
        return 'wpultimaterecipe';
    }

    /**
     * Wether or not this importer requires a manual search for recipes.
     *
     * @since    1.10.0
     */
    public function requires_search() {
        return false;
    }

    /**
     * Get the name of this import source.
     *
     * @since    1.3.0
     */
    public function get_name() {
        return 'WP Ultimate Recipe';
    }

    /**
     * Get HTML for the import settings.
     *
     * @since    1.3.0
     */
    public function get_settings_html() {
        $html = '<h4>Recipe Tags</h4>';

        $wpurp_taxonomies = get_option( 'wpurp_taxonomies', array() );
        unset( $wpurp_taxonomies['ingredient'] );

        $wprm_taxonomies = WPRM_Taxonomies::get_taxonomies();

        foreach ( $wprm_taxonomies as $wprm_taxonomy => $options ) {
            $wprm_key = substr( $wprm_taxonomy, 5 );

            $html .= '<label for="wpurp-tags-' . $wprm_key . '">' . $options['name'] . ':</label> ';
            $html .= '<select name="wpurp-tags-' . $wprm_key . '" id="wpurp-tags-' . $wprm_key . '">';
            $html .= "<option value=\"\">Don't import anything for this tag</option>";
            foreach ( $wpurp_taxonomies as $name => $options ) {
                $selected = $wprm_key === $name ? ' selected="selected"' : '';
                $html .= '<option value="' . esc_attr( $name ) . '"' . esc_html( $selected ) . '>' . esc_html( $options['labels']['name'] ) . '</option>';
            }
            $html .= '</select>';
            $html .= '<br />';
        }

        return $html;
    }

    /**
     * Get the total number of recipes to import.
     *
     * @since    1.10.0
     */
    public function get_recipe_count() {
        return count( $this->get_recipes() );
    }

    /**
     * Get a list of recipes that are available to import.
     *
     * @since    1.3.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' => array( 'post', 'page' ),
                'post_status' => 'any',
                'orderby' => 'date',
                'order' => 'DESC',
                'posts_per_page' => $limit,
                'offset' => $offset,
                's' => '[recipe]',
        );

        $query = new WP_Query( $args );

        if ( $query->have_posts() ) {
            $posts = $query->posts;
            
            foreach ( $posts as $post ) {
                
                $content = $post->post_content;
                
                $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.3.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' => 0,
            'import_backup' => array(),
        );

        $post_meta = get_post_custom( $id );

        $post = get_post( $id );

        $alternate_image = isset( $post_meta['recipe_alternate_image'] ) ? $post_meta['recipe_alternate_image'][0] : false;
        $recipe['image_id'] = $alternate_image ? $alternate_image : get_post_thumbnail_id( $id );

        $recipe_title = isset( $post_meta['recipe_title'] ) ? $post_meta['recipe_title'][0] : false;
        $recipe['name'] = $recipe_title ? $recipe_title : $post->post_title;

        $recipe['summary'] = isset( $post_meta['recipe_description'] ) ? $post_meta['recipe_description'][0] : '';
        $recipe['servings'] = isset( $post_meta['recipe_servings'] ) ? $post_meta['recipe_servings'][0] : '';
        $recipe['servings_unit'] = isset( $post_meta['recipe_servings_type'] ) ? $post_meta['recipe_servings_type'][0] : '';
        $recipe['notes'] = isset( $post_meta['recipe_notes'] ) ? $post_meta['recipe_notes'][0] : '';

        // Recipe Times.
        $prep_time = isset( $post_meta['recipe_prep_time'] ) ? $post_meta['recipe_prep_time'][0] : '';
        $prep_time_unit = isset( $post_meta['recipe_prep_time_text'] ) ? $post_meta['recipe_prep_time_text'][0] : '';
        $recipe['prep_time'] = self::get_time_in_minutes( $prep_time, $prep_time_unit );

        $cook_time = isset( $post_meta['recipe_cook_time'] ) ? $post_meta['recipe_cook_time'][0] : '';
        $cook_time_unit = isset( $post_meta['recipe_cook_time_text'] ) ? $post_meta['recipe_cook_time_text'][0] : '';
        $recipe['cook_time'] = self::get_time_in_minutes( $cook_time, $cook_time_unit );

        $passive_time = isset( $post_meta['recipe_passive_time'] ) ? $post_meta['recipe_passive_time'][0] : '';
        $passive_time_unit = isset( $post_meta['recipe_passive_time_text'] ) ? $post_meta['recipe_passive_time_text'][0] : '';
        $passive_time_minutes = self::get_time_in_minutes( $passive_time, $passive_time_unit );

        $recipe['total_time'] = $recipe['prep_time'] + $recipe['cook_time'] + $passive_time_minutes;

        // Recipe Tags.
        $recipe['tags'] = array();

        $wprm_taxonomies = WPRM_Taxonomies::get_taxonomies();
        foreach ( $wprm_taxonomies as $wprm_taxonomy => $options ) {
            $wprm_key = substr( $wprm_taxonomy, 5 );
            $tag = isset( $post_data[ 'wpurp-tags-' . $wprm_key ] ) ? $post_data[ 'wpurp-tags-' . $wprm_key ] : false;

            if ( $tag ) {
                $terms = get_the_terms( $id, $tag );
                if ( $terms && ! is_wp_error( $terms ) ) {
                    foreach ( $terms as $term ) {
                        $recipe['tags'][ $wprm_key ][] = $term->name;
                    }
                }
            }
        }

        // Recipe Ingredients.
        $ingredients = isset( $post_meta['recipe_ingredients'] ) ? maybe_unserialize( $post_meta['recipe_ingredients'][0] ) : array();
        $recipe['ingredients'] = array();

        $current_group = array(
            'name' => '',
            'ingredients' => array(),
        );
        
        foreach ( $ingredients as $ingredient ) {
            if ( isset( $ingredient['group'] ) && $ingredient['group'] !== $current_group['name'] ) {
                $recipe['ingredients'][] = $current_group;
                $current_group = array(
                    'name' => $ingredient['group'],
                    'ingredients' => array(),
                );
            }

            $current_group['ingredients'][] = array(
                'amount' => $ingredient['amount'],
                'unit' => $ingredient['unit'],
                'name' => $ingredient['ingredient'],
                'notes' => $ingredient['notes'],
            );
        }
        $recipe['ingredients'][] = $current_group;

        // Recipe Instructions.
        $instructions = isset( $post_meta['recipe_instructions'] ) ? maybe_unserialize( $post_meta['recipe_instructions'][0] ) : array();
        $recipe['instructions'] = array();

        $current_group = array(
            'name' => '',
            'instructions' => array(),
        );
        
        foreach ( $instructions as $instruction ) {
            if ( isset( $instruction['group'] ) && $instruction['group'] !== $current_group['name'] ) {
                $recipe['instructions'][] = $current_group;
                $current_group = array(
                    'name' => $instruction['group'],
                    'instructions' => array(),
                );
            }

            $current_group['instructions'][] = array(
                'text' => $instruction['description'],
                'image' => $instruction['image'],
            );
        }
        $recipe['instructions'][] = $current_group;

        // Recipe Nutrition.
        $recipe['nutrition'] = array();

        $nutrition_mapping = array(
            'serving_size'          => 'serving_size',
            'calories'              => 'calories',
            'carbohydrate'          => 'carbohydrates',
            'protein'               => 'protein',
            'fat'                   => 'fat',
            'saturated_fat'         => 'saturated_fat',
            'polyunsaturated_fat'   => 'polyunsaturated_fat',
            'monounsaturated_fat'   => 'monounsaturated_fat',
            'trans_fat'             => 'trans_fat',
            'cholesterol'           => 'cholesterol',
            'sodium'                => 'sodium',
            'potassium'             => 'potassium',
            'fiber'                 => 'fiber',
            'sugar'                 => 'sugar',
            'vitamin_a'             => 'vitamin_a',
            'vitamin_c'             => 'vitamin_c',
            'calcium'               => 'calcium',
            'iron'                  => 'iron',
        );

        $nutrition = isset( $post_meta['recipe_nutritional'] ) ? maybe_unserialize( $post_meta['recipe_nutritional'][0] ) : array();

        foreach ( $nutrition_mapping as $wpurp_field => $wprm_field ) {
            $recipe['nutrition'][ $wprm_field ] = isset( $nutrition[ $wpurp_field ] ) ? $nutrition[ $wpurp_field ] : '';
        }

        return $recipe;
    }

    /**
     * Replace the original recipe with the newly imported WPRM one.
     *
     * @since    1.3.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.

        // Migrate user ratings.
        $user_ratings = get_post_meta( $id, 'recipe_user_ratings' );

        foreach ( $user_ratings as $wpurp_rating ) {
            $user_rating = array(
                'recipe_id' => $wprm_id,
                'user_id' => $wpurp_rating['user'],
                'ip' => $wpurp_rating['ip'],
                'rating' => $wpurp_rating['rating'],
            );

            WPRM_Rating_Database::add_or_update_rating( $user_rating );
        }

        $post = get_post( $id );
        $content = $post->post_content;
        
        if ( 0 === substr_count( $content, '[recipe]' ) ) {
            $content .= ' [wprm-recipe id="' . $wprm_id . '"]';
        } else {
            $content = str_ireplace( '[recipe]', '[wprm-recipe id="' . $wprm_id . '"]', $content );
        }
        
        $content = preg_replace( "/\[ultimate-recipe\s.*?id='?\"?" . $id . '.*?]/im', '[wprm-recipe id="' . $wprm_id . '"]', $content );

        // Remove searchable recipe part
        $content = preg_replace( '/\[wpurp-searchable-recipe\][^\[]*\[\/wpurp-searchable-recipe\]/', '', $content );

        $update_content = array(
            'ID' => $id,
            'post_type' => 'post',
            'post_content' => $content,
        );
        wp_update_post( $update_content );

        // Migrate potential ER comment ratings.
        $comments = get_comments( array( 'post_id' => $id ) );

        foreach ( $comments as $comment ) {
            $comment_rating = intval( get_comment_meta( $comment->comment_ID, 'ERRating', true ) );
            if ( $comment_rating ) {
                WPRM_Comment_Rating::add_or_update_rating_for( $comment->comment_ID, $comment_rating );
            }
        }
    }

    /**
     * Convert time string to minutes.
     *
     * @since    1.3.0
     * @param        mixed $time Time string to convert to minutes.
     * @param        mixed $unit Unit of the time string.
     */
    private function get_time_in_minutes( $time, $unit ) {
        $minutes = intval( $time );

        if ( strtolower( $unit ) === strtolower( __( 'hour', 'wp-ultimate-recipe' ) )
                || strtolower( $unit ) === strtolower( __( 'hours', 'wp-ultimate-recipe' ) )
                || strtolower( $unit ) === 'h'
                || strtolower( $unit ) === 'hr'
                || strtolower( $unit ) === 'hrs' ) {
                $minutes = $minutes * 60;
        }

        return $minutes;
    }
}

That’s it!

There you have it. A lot of words just to say replace the official WP Ultimate Recipe importer script with the custom importer code found above, disable WPML, switch the recipe post type to regular post type, run the importer and reactivate WPML.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of

Free to your inbox

Join our mailing list to receive the latest news and updates from JournalXtra.

You have Successfully Subscribed!