Shoora
12/30/2016 - 7:12 PM

[WordPress Plugin] Nav Menu Exporter and Importer / Export and Import nav menus. Requires WordPress Importer plugin. Tested up to 4.7 - It w

[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' )