Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm the author of the Nav Menu Roles plugin that lets you display/hide menu items based on the user's role. It has come to my attention that the menu item meta does not import when using the regular WordPress plugin/tool for importing.

Each menu item is essentially just a post in the database, and I've been saving the data as post meta. It appears in the exported XML. Here's a sample from a test export I just did locally.

<item>
    <title>Art &amp; Design</title>
    <link>http://localhost/blog/2013/01/13/263/</link>
    <pubDate>Mon, 14 Jan 2013 03:36:16 +0000</pubDate>
    <dc:creator>helga</dc:creator>
    <guid isPermaLink="false">http://localhost/blog/2013/01/13/263/</guid>
    <description></description>
    <content:encoded><![CDATA[]]></content:encoded>
    <excerpt:encoded><![CDATA[]]></excerpt:encoded>
    <wp:post_id>263</wp:post_id>
    <wp:post_date>2013-01-13 22:36:16</wp:post_date>
    <wp:post_date_gmt>2013-01-14 03:36:16</wp:post_date_gmt>
    <wp:comment_status>open</wp:comment_status>
    <wp:ping_status>open</wp:ping_status>
    <wp:post_name>263</wp:post_name>
    <wp:status>publish</wp:status>
    <wp:post_parent>0</wp:post_parent>
    <wp:menu_order>1</wp:menu_order>
    <wp:post_type>nav_menu_item</wp:post_type>
    <wp:post_password></wp:post_password>
    <wp:is_sticky>0</wp:is_sticky>
    <category domain="nav_menu" nicename="main-menu"><![CDATA[Main Menu]]></category>
    <wp:postmeta>
        <wp:meta_key>_menu_item_type</wp:meta_key>
        <wp:meta_value><![CDATA[taxonomy]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_menu_item_parent</wp:meta_key>
        <wp:meta_value><![CDATA[0]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_object_id</wp:meta_key>
        <wp:meta_value><![CDATA[6]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_object</wp:meta_key>
        <wp:meta_value><![CDATA[category]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_target</wp:meta_key>
        <wp:meta_value><![CDATA[]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_classes</wp:meta_key>
        <wp:meta_value><![CDATA[a:1:{i:0;s:6:"design";}]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_xfn</wp:meta_key>
        <wp:meta_value><![CDATA[]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_menu_item_url</wp:meta_key>
        <wp:meta_value><![CDATA[]]></wp:meta_value>
    </wp:postmeta>
    <wp:postmeta>
        <wp:meta_key>_nav_menu_role</wp:meta_key>
        <wp:meta_value><![CDATA[a:2:{i:0;s:13:"administrator";i:1;s:12:"shop_manager";}]]></wp:meta_value>
    </wp:postmeta>
</item>

But then on import, the meta key _nav_menu_role doesn't even appear in the database. I'm running a multisite locally, but the user who brought this issue to my attention is not. I'd like to fix this issue for everyone who uses this plugin, but I am so stumped about where to even begin looking. I thought it might be a problem with serialization/non-serialization, but the key isn't corrupt, it isn't even there after the import.

UPDATE: I've investigated the importer plugin and for menu items, the plugin only imports the traditional, pre-defined meta.. such as 'class'.

share|improve this question

1 Answer 1

up vote 0 down vote accepted

The problem was ultimately with the WordPress Importer plugin. I could hack at it (and have suggested an improvement to the developers) but I am going to got around this by writing a custom Importer of my own. It isn't the most convenient (to re-upload the .XML file) but in cases where users have complex menus it is better than losing the meta values.

It needs more testing, but I should be adding it to my plugin in the next version.

Custom Importer for Menu Item Meta

The problem was ultimately with the WordPress Importer plugin. I could hack at it (and have suggested an improvement to the developers) but I am going to got around this by writing a custom Importer of my own. It isn't the most convenient (to re-upload the .XML file) but in cases where users have complex menus it is better than losing the meta values.

It needs more testing, but I should be adding it to my plugin in the next version.

Custom Importer for Menu Item Meta

<?php
/**
 * Nav Menu Roles Importer - import menu item meta
 *
 */

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

if ( class_exists( 'WP_Importer' ) ) {
    class Nav_Menu_Roles_Import extends WP_Importer {

    var $max_wxr_version = 1.2; // max. supported WXR version

    var $id; // WXR attachment ID

    // information to import from WXR file
    var $version;
    var $posts = array();
    var $base_url = '';


        /**
         * __construct function.
         *
         * @access public
         * @return void
         */
        public function __construct() {
            $this->import_page = 'nav_menu_roles';
        }

    /**
     * Registered callback function for the WordPress Importer
     *
     * Manages the three separate stages of the WXR import process
     */
    function dispatch() {
        $this->header();

        $step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step'];
        switch ( $step ) {
            case 0:
                $this->greet();
                break;
            case 1:
                check_admin_referer( 'import-upload' );
                if ( $this->handle_upload() ) {
                    $file = get_attached_file( $this->id );
                    set_time_limit(0);
                    $this->import( $file );
                }
                break;
        }

        $this->footer();
    }

    /**
     * The main controller for the actual import stage.
     *
     * @param string $file Path to the WXR file for importing
     */
    function import( $file ) {
        add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
        add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );

        $this->import_start( $file );

        wp_suspend_cache_invalidation( true );
        $this->process_nav_menu_meta();
        wp_suspend_cache_invalidation( false );

        $this->import_end();
    }

    /**
     * Parses the WXR file and prepares us for the task of processing parsed data
     *
     * @param string $file Path to the WXR file for importing
     */
    function import_start( $file ) {
        if ( ! is_file($file) ) {
            echo '<p><strong>' . __( 'Sorry, there has been an error.', 'nav-menu-roles' ) . '</strong><br />';
            echo __( 'The file does not exist, please try again.', 'nav-menu-roles' ) . '</p>';
            $this->footer();
            die();
        }

        $import_data = $this->parse( $file );

        if ( is_wp_error( $import_data ) ) {
            echo '<p><strong>' . __( 'Sorry, there has been an error.', 'nav-menu-roles' ) . '</strong><br />';
            echo esc_html( $import_data->get_error_message() ) . '</p>';
            $this->footer();
            die();
        }

        $this->version = $import_data['version'];
        $this->posts = $import_data['posts'];
        $this->base_url = esc_url( $import_data['base_url'] );

        wp_defer_term_counting( true );
        wp_defer_comment_counting( true );

        do_action( 'import_start' );
    }

    /**
     * Performs post-import cleanup of files and the cache
     */
    function import_end() {
        wp_import_cleanup( $this->id );

        wp_cache_flush();
        foreach ( get_taxonomies() as $tax ) {
            delete_option( "{$tax}_children" );
            _get_term_hierarchy( $tax );
        }

        wp_defer_term_counting( false );
        wp_defer_comment_counting( false );

        echo '<p>' . __( 'All done.', 'nav-menu-roles' ) . ' <a href="' . admin_url() . '">' . __( 'Have fun!', 'nav-menu-roles' ) . '</a>' . '</p>';

        do_action( 'import_end' );
    }

    /**
     * Handles the WXR upload and initial parsing of the file to prepare for
     * displaying author import options
     *
     * @return bool False if error uploading or invalid file, true otherwise
     */
    function handle_upload() {
        $file = wp_import_handle_upload();

        if ( isset( $file['error'] ) ) {
            echo '<p><strong>' . __( 'Sorry, there has been an error.', 'nav-menu-roles' ) . '</strong><br />';
            echo esc_html( $file['error'] ) . '</p>';
            return false;
        } else if ( ! file_exists( $file['file'] ) ) {
            echo '<p><strong>' . __( 'Sorry, there has been an error.', 'nav-menu-roles' ) . '</strong><br />';
            printf( __( 'The export file could not be found at <code>%s</code>. It is likely that this was caused by a permissions problem.', 'nav-menu-roles' ), esc_html( $file['file'] ) );
            echo '</p>';
            return false;
        }

        $this->id = (int) $file['id'];
        $import_data = $this->parse( $file['file'] );
        if ( is_wp_error( $import_data ) ) {
            echo '<p><strong>' . __( 'Sorry, there has been an error.', 'nav-menu-roles' ) . '</strong><br />';
            echo esc_html( $import_data->get_error_message() ) . '</p>';
            return false;
        }

        $this->version = $import_data['version'];
        if ( $this->version > $this->max_wxr_version ) {
            echo '<div class="error"><p><strong>';
            printf( __( 'This WXR file (version %s) may not be supported by this version of the importer. Please consider updating.', 'nav-menu-roles' ), esc_html($import_data['version']) );
            echo '</strong></p></div>';
        }

        return true;
    }



    /**
     * Create new posts based on import information
     *
     * Posts marked as having a parent which doesn't exist will become top level items.
     * Doesn't create a new post if: the post type doesn't exist, the given post ID
     * is already noted as imported or a post with the same title and date already exists.
     * Note that new/updated terms, comments and meta are imported for the last of the above.
     */
    function process_nav_menu_meta() {
        foreach ( $this->posts as $post ) {

            // we only want to deal with the nav_menu_item posts
            if ( 'nav_menu_item' != $post['post_type'] || ! empty( $post['post_id'] ) )
                continue;

            // ok we've got a nav_menu_item
            $post_id = (int) $post['post_id'];

            // add/update post meta
            if ( isset( $post['postmeta'] ) ) {
                foreach ( $post['postmeta'] as $meta ) {
                    $key = apply_filters( 'import_post_meta_key', $meta['key'] );
                    $value = false;


                    if ( $key ) {
                        // export gets meta straight from the DB so could have a serialized string
                        if ( ! $value )
                            $value = maybe_unserialize( $meta['value'] );

                        update_post_meta( $post_id, $key, $value );
                        do_action( 'import_post_meta', $post_id, $key, $value );

                    }
                }
            }
        }

        unset( $this->posts );
    }




    /**
     * Parse a WXR file
     *
     * @param string $file Path to WXR file for parsing
     * @return array Information gathered from the WXR file
     */
    function parse( $file ) {
        $parser = new WXR_Parser();
        return $parser->parse( $file );
    }

    // Display import page title
    function header() {
        echo '<div class="wrap">';
        screen_icon();
        echo '<h2>' . __( 'Import Nav Menu Roles', 'nav-menu-roles' ) . '</h2>';

        $updates = get_plugin_updates();
        $basename = plugin_basename(__FILE__);
        if ( isset( $updates[$basename] ) ) {
            $update = $updates[$basename];
            echo '<div class="error"><p><strong>';
            printf( __( 'A new version of this importer is available. Please update to version %s to ensure compatibility with newer export files.', 'nav-menu-roles' ), $update->update->new_version );
            echo '</strong></p></div>';
        }
    }

    // Close div.wrap
    function footer() {
        echo '</div>';
    }

    /**
     * Display introductory text and file upload form
     */
    function greet() {
        echo '<div class="narrow">';
        echo '<p>'.__( 'Re-Upload your normal WordPress eXtended RSS (WXR) file and we&#8217;ll import the Nav Menu Roles and any other missing post meta for the Nav Menu items.', 'nav-menu-roles' ).'</p>';
        echo '<p>'.__( 'Choose a WXR (.xml) file to upload, then click Upload file and import.', 'nav-menu-roles' ).'</p>';
        wp_import_upload_form( 'admin.php?import=nav_menu_roles&amp;step=1' );
        echo '</div>';
    }

    /**
     * Decide if the given meta key maps to information we will want to import
     *
     * @param string $key The meta key to check
     * @return string|bool The key if we do want to import, false if not
     */
    function is_valid_meta_key( $key ) {
        // skip attachment metadata since we'll regenerate it from scratch
        // skip _edit_lock as not relevant for import
        if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) )
            return false;
        return $key;
    }


    /**
     * Added to http_request_timeout filter to force timeout at 60 seconds during import
     * @return int 60
     */
    function bump_request_timeout() {
        return 60;
    }

    // return the difference in length between two strings
    function cmpr_strlen( $a, $b ) {
        return strlen($b) - strlen($a);
    }


    } // end class
} // end if
share|improve this answer
    
As of version 1.3 an importer is included in the plugin. –  helgatheviking Apr 4 '13 at 20:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.