[WordPress Plugin] Nav Menu Exporter and Importer / Export and Import nav menus. Requires WordPress Importer plugin. Tested up to 4.7 - It works with, taxonomies, post_type and custom items.
<?php
/*
Plugin Name: Nav Menu Exporter and Importer
Description: Export and Import nav menus. Requires WordPress Importer plugin
Author: hissy, megumi themes
Version: 0.1
Text Domain: nav-menu-exporter-importer
License: GPL version 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
/**
* Original code https://gist.github.com/hissy/6739352
* Forked from https://gist.github.com/raynov/5171cfb2083f35fb4e051183b47808ce
*/
/**
* Exporter
*/
function add_mav_menu_to_export() {
$post_type = get_post_type_object('nav_menu_item');
?>
<p><label><input type="radio" name="content" value="<?php echo esc_attr( $post_type->name ); ?>" /> <?php echo esc_html( $post_type->label ); ?></label></p>
<?php
}
add_action('export_filters','add_mav_menu_to_export');
/**
* Importer
*/
if ( ! defined( 'WP_LOAD_IMPORTERS' ) )
return;
// Load Importer API
require_once ABSPATH . 'wp-admin/includes/import.php';
if ( ! class_exists( 'WP_Importer' ) ) {
$class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
if ( file_exists( $class_wp_importer ) )
require $class_wp_importer;
}
// include WXR file parsers
$wordpress_importer = ABSPATH . 'wp-content/plugins/wordpress-importer/wordpress-importer.php';
if ( file_exists( $wordpress_importer ) )
require_once $wordpress_importer;
/**
* Nav Menu Importer class
*/
if ( class_exists( 'WP_Import' ) ) {
class Nav_Menu_Importer extends WP_Import {
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();
}
function greet() {
$this->wp_import_upload_form( add_query_arg( 'step', 1 ) );
}
function import_end() {
wp_import_cleanup( $this->id );
wp_cache_flush();
printf(
'<p>%s <a href="%s">%s</a></p>',
__( 'All done.', 'wordpress-importer' ),
admin_url( 'nav-menus.php' ),
__( 'Have fun!', 'wordpress-importer' )
);
}
public function import( $file ) {
$this->import_start( $file );
wp_suspend_cache_invalidation( true );
// only processing nav menus
$this->process_menus();
wp_suspend_cache_invalidation( false );
$this->import_end();
}
/**
* Upload the nav menu item
*
* @todo This works only with 2 levels of hierarchy
* Needs a method to work with more than 2 levels.
*
* @todo Theme menu position, when loaded it is not appended to no
* theme menu positions. Maybe depends to the export functionality.
*
* @todo More test for multiple menu
*/
public function process_menus() {
$count = 0;
$parents = array();
foreach ( $this->posts as $item ) {
$count++;
// check the item is public nav item
if ( 'nav_menu_item' !== $item['post_type'] ) {
continue;
}
if ( 'draft' === $item['status'] ) {
continue;
}
$menu_slug = false;
if ( isset( $item['terms'] ) ) {
// loop through terms, assume first nav_menu term is correct menu
foreach ( $item['terms'] as $term ) {
if ( 'nav_menu' === $term['domain'] ) {
$menu_slug = $term['slug'];
break;
}
}
}
// no nav_menu term associated with this menu item
if ( ! $menu_slug ) {
printf(
__( 'Menu item <code>%s</code>" skipped due to missing menu slug. <br>', 'wordpress-importer' ),
esc_attr( $item['post_title'] )
);
continue;
}
$menu_exists = wp_get_nav_menu_object( $menu_slug );
if ( ! $menu_exists ) {
$menu_id = wp_create_nav_menu( $menu_slug );
}
// $menu_id = term_exists( $menu_slug, 'nav_menu' );
if ( ! $menu_id ) {
printf(
__( 'Menu item skipped due to invalid menu slug: %s %s', 'wordpress-importer' ),
esc_html( $menu_slug ),
'<br>'
);
continue;
}
// else {
// $menu_id = is_array( $menu_id ) ? $menu_id['term_id'] : $menu_id;
// }
// set postmeta
foreach ( (array) $item['postmeta'] as $meta ) {
/**
* This resolved PHP Notice: Array to string conversion
* I don't know way
*/
$_key = $meta['key'];
$_value = $meta['value'];
$$_key = $_value;
}
if ( ! isset( $_menu_item_type ) ) {
continue;
}
// skip nav item when menu item object is not exists
switch ($_menu_item_type) {
case 'taxonomy':
$_menu_item_object_id = get_term($_menu_item_object_id,$_menu_item_object);
if ($_menu_item_object_id == null || is_wp_error($_menu_item_object_id)) {
printf(
__( 'Menu item skipped due to %s is not exists', 'nav-menu-exporter-importer' ),
esc_html( $_menu_item_object )
);
echo '<br>';
}
break;
case 'post_type':
$_menu_item_object_id = get_post($_menu_item_object_id);
if ($_menu_item_object_id instanceof WP_Post) {
$_menu_item_object_id = $_menu_item_object_id->ID;
unset($_post);
} else {
printf(
__( 'Menu item skipped due to %s is not exists', 'nav-menu-exporter-importer' ),
esc_html( $_menu_item_object )
);
echo '<br>';
}
break;
case 'custom':
if ( isset( $_POST['new_url'] ) && '#' !== $_menu_item_url ) {
$_menu_item_url = preg_replace( '/https?:\/\/([\w]+\.{1}[\w]+\.?[\w]+)+/', esc_url( $_POST['new_url'] ), $_menu_item_url );
}
break;
}
if ( is_null( $_menu_item_object_id ) || is_wp_error( $_menu_item_object_id ) ) {
continue;
}
// wp_update_nav_menu_item expects CSS classes as a space separated string
$_menu_item_classes = maybe_unserialize( $_menu_item_classes );
if ( is_array( $_menu_item_classes ) ) {
$_menu_item_classes = implode( ' ', $_menu_item_classes );
}
$menu_item_parent_id = isset( $parents[ $_menu_item_menu_item_parent ] ) ? $parents[ $_menu_item_menu_item_parent ] : null;
/**
* This fix "Object of class WP_Term could not be converted to int"
* in "wp-includes/nav-menu.php:494"
*/
if ( $_menu_item_object_id instanceof WP_Term ) {
$item['post_title'] = $_menu_item_object_id->name;
/**
* This prevents 'Trying to get property of non-object'
*/
$_menu_item_object_id = $_menu_item_object_id->term_id;
}
$menu_item_data = array(
'menu-item-object-id' => $_menu_item_object_id,
'menu-item-object' => $_menu_item_object,
'menu-item-parent-id' => $menu_item_parent_id,
'menu-item-position' => absint( $item['menu_order'] ),
'menu-item-type' => $_menu_item_type,
'menu-item-title' => wp_slash( $item['post_title'] ),
// This is set maybe only for custom menu item.
'menu-item-url' => $_menu_item_url,
'menu-item-description' => wp_slash( $item['post_content'] ),
'menu-item-attr-title' => wp_slash( $item['post_excerpt'] ),
'menu-item-target' => $_menu_item_target,
'menu-item-classes' => $_menu_item_classes,
'menu-item-xfn' => $_menu_item_xfn,
'menu-item-status' => $item['status']
);
$r = wp_update_nav_menu_item( (int) $menu_id, 0, (array) $menu_item_data );
if ( $r && is_wp_error( $r ) ) {
echo $r->get_error_message();
echo '<br>';
} else {
$parents[ absint( $item['post_id'] ) ] = $r;
printf(
'<p>ID: <strong>%s</strong> - Title: <strong>%s</strong> - URL: <strong>%s</strong> - Type: <strong>%s</strong></p>',
$r,
$item['post_title'],
$_menu_item_url,
$_menu_item_type
);
}
}
echo '<br>';
printf( __( '%s items processed.', 'nav-menu-exporter-importer' ), esc_html( $count ) );
}
/**
* Outputs the form used by the importers to accept the data to be imported
*
* @see wp_import_upload_form( $action ) in 'https://core.trac.wordpress.org/browser/tags/4.7/src/wp-admin/includes/template.php#L846'
*
* @since 2.0.0
*
* @param string $action The action attribute for the form.
*/
public function wp_import_upload_form( $action ) {
/**
* Filters the maximum allowed upload size for import files.
*
* @since 2.3.0
*
* @see wp_max_upload_size()
*
* @param int $max_upload_size Allowed upload size. Default 1 MB.
*/
$bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
$size = size_format( $bytes );
$upload_dir = wp_upload_dir();
if ( ! empty( $upload_dir['error'] ) ) :
?><div class="error"><p><?php _e( 'Before you can upload your import file, you will need to fix the following error:' ); ?></p>
<p><strong><?php echo $upload_dir['error']; ?></strong></p></div><?php
else :
?>
<form enctype="multipart/form-data" id="import-upload-form" method="post" class="wp-upload-form" action="<?php echo esc_url( wp_nonce_url( $action, 'import-upload' ) ); ?>">
<p>
<label for="upload"><?php _e( 'Choose a file from your computer:' ); ?></label> (<?php printf( __( 'Maximum size: %s' ), $size ); ?>)
<input type="file" id="upload" name="import" size="25" />
<br>
<label for="new_url">
<?php
printf(
esc_html__( 'Use this field in case you are doing a migration from a domain to another, this works only for custom item menu url and not for custom menu with <code>#</code>.<br> <code>%s</code>' ),
esc_url( get_option( 'siteurl' ) )
);
?>
</label>
<input type="text" id="new_url" name="new_url" />
<input type="hidden" name="action" value="save" />
<input type="hidden" name="max_file_size" value="<?php echo $bytes; // XSS ok. ?>" />
</p>
<?php submit_button( __('Upload file and import' ), 'primary' ); ?>
</form>
<?php
endif;
}
}
// setup importer
$nav_menu_importer = new Nav_Menu_Importer();
register_importer(
'nav_menu',
__( 'Nav Menu', 'nav-menu-exporter-importer' ),
__('Export and Import nav menus. Requires WordPress Importer plugin', 'nav-menu-exporter-importer'),
array ( $nav_menu_importer, 'dispatch' )
);
} // class_exists( 'WP_Import' )