Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
[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' )
@renegithub

This comment has been minimized.

Copy link

@renegithub renegithub commented Jan 30, 2017

Hi,
this looks like a very useful tool, but where do I have to insert the code / file nav-menu-exporter-importer.php?
Shall I copy the nav-menu-exporter-importer.php into the folder /httpdocs/wp-content/plugins/wordpress-importer?

Best regards,

Rene

@kraftstudio

This comment has been minimized.

Copy link

@kraftstudio kraftstudio commented Feb 6, 2017

Do not work with sub-menus items! Please fix it!

@edmovius

This comment has been minimized.

Copy link

@edmovius edmovius commented Jun 15, 2017

Hi Rene, what I did was to copy the php file above into Notepad++ and saved it as "Nav_Menu_Exporter_and_Importer_Plugin.php" Then I zipped or compressed the file so that its now: "Nav_Menu_Exporter_and_Importer_Plugin.zip". Then I logged into my WordPress site and installed it like you would any WordPress plugin

@primedime

This comment has been minimized.

Copy link

@primedime primedime commented Jun 27, 2017

This is not working at all for me on WordPress 4.8. Export and Import seem to run fine but nothing shows up on the menus.
Attempting to export from a local build to import to a live server and just does not work. Is this not something that can be done? Only from a live site to a live site?

@krishaamer

This comment has been minimized.

Copy link

@krishaamer krishaamer commented Aug 30, 2017

I'm getting the following error when trying to import the the export file generated by this plugin in WP 4.8

( ! ) Notice: Array to string conversion in /srv/www/eka.dev/current/web/app/plugins/wordpress-importer/wordpress-importer.php on line 884
--
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment