Multisite Taxonomy Sync – Updates To My Multisite Category Sync Plugin

A few weeks ago I had developed a plugin which synced categories across a WordPress network. Here’s how you can do the same thing for any taxonomy. Let me know what you think.

<?php
/*
Plugin Name: Taxonomy Sync
Version: 0.1
*/

// Taxonomies
$taxonomies_to_sync = array( 'an', 'array', 'of', 'taxonomies', 'to', 'sync' ); // Change These!

function ms_taxonomy_sync_add_menu() {
	add_submenu_page( 'index.php', __('Taxonomy Sync'), __('Taxonomy Sync'), 'manage-options', 'ms-taxonomy-sync-'.basename(__FILE__), 'ms_taxonomy_sync_page');
}
add_action('network_admin_menu', 'ms_taxonomy_sync_add_menu');

function ms_taxonomy_sync_page() {
	
	if ( !current_user_can('manage_options') )
    	wp_die( __('You do not have sufficient permissions to access this page.') );

	$ms_taxonomy_sync = 'ms-taxonomy-sync';
	
	if ( isset($_POST[ $ms_taxonomy_sync ]) && $_POST[ $ms_taxonomy_sync ] == '1' ) { 
		ms_taxonomy_sync(); ?>
		<div class="updated"><p><strong><?php _e('Taxonomy synchronization completed.'); ?></strong></p></div>
	<?php }
	
	echo '<div class="wrap">';
    echo '<h2>' . __( 'Sync Taxonomies Across Network') . '</h2>';
	?>
	
	<form name="ms-taxonomy-sync" method="post" action="">
	<input type="hidden" name="<?php echo $ms_taxonomy_sync; ?>" value="1">
	<p class="submit">
	<input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e('Sync Taxonomies') ?>" />
	</p>
	</form>
	
	<?php
	echo '</div>';
}

// Taxonomy Sync
function ms_taxonomy_sync() {
	
	// Globals
	global $wpdb;
	global $switched;
	global $current_site;
	global $taxonomies_to_sync;
	
	// Master Site?
	if ($current_site->id == 1) :
	
		foreach ($taxonomies_to_sync as $taxonomy_to_sync) :
	
			// Get Master Taxonomies
			$query = "SELECT * FROM ".$wpdb->prefix."terms a, ".$wpdb->prefix."term_taxonomy b where a.term_id = b.term_id and b.taxonomy = '".$taxonomy_to_sync."'";
			$mastertaxonomies = $wpdb->get_results($query);
			//print_r($mastertaxonomies); echo '<br /><br />'; // For Debugging
		
			// Get An Array of Slugs
			$masterslugs = array();
			foreach ($mastertaxonomies as $mastertaxonomy) :
				$masterslugs[] = $mastertaxonomy->slug;
			endforeach;
	
			// Query Sites in Network
			$query = $wpdb->prepare("select blog_id from $wpdb->blogs");
			$sites = $wpdb->get_results($query);
		
			// Use This Later
			$childrentaxonomies = array();

			// For Each Site
			foreach ($sites as $site) :
				// Skip Master Site
				if ($site->blog_id != 1) :				
					// Switch
					switch_to_blog($site->blog_id);
					// Get Taxonomies
					$query = "SELECT * FROM ".$wpdb->prefix."terms a, ".$wpdb->prefix."term_taxonomy b where a.term_id = b.term_id and b.taxonomy = '".$taxonomy_to_sync."'";
					$taxonomies = $wpdb->get_results($query);
					//print_r($taxonomies); echo '<br /><br />'; // For Debugging
					// If a Taxonomy shouldn't be here, delete it
					foreach ($taxonomies as $key => $taxonomy) :
						if (!in_array($taxonomy->slug, $masterslugs)) :
							wp_delete_term( $taxonomy->term_id, $taxonomy_to_sync );
						endif;
					endforeach;
					$query = "SELECT * FROM ".$wpdb->prefix."terms a, ".$wpdb->prefix."term_taxonomy b where a.term_id = b.term_id and b.taxonomy = '".$taxonomy_to_sync."'";
					$taxonomies = $wpdb->get_results($query);
					//print_r($taxonomies); echo '<br /><br />'; // For Debugging
					// Updated Array of Slugs
					$updatedslugs = array();
					foreach ($taxonomies as $taxonomy) :
						$updatedslugs[] = $taxonomy->slug;
					endforeach;
					// If a Taxonomy needs to be here, add it
					foreach ($mastertaxonomies as $key => $mastertaxonomy) :
						if (!in_array($mastertaxonomy->slug, $updatedslugs)) :
							// Does this Taxonomy have a parent?
							if ($mastertaxonomy->parent == 0) :
								$term = wp_insert_term($mastertaxonomy->name, $taxonomy_to_sync, array('description' => $mastertaxonomy->taxonomy_description, 'slug' => $mastertaxonomy->slug, 'parent' => '0'));
							// Let's come back to this and go through the rest so we get ALL parent ID's
							else :
								foreach ($mastertaxonomies as $i => $tax) :
									if ($mastertaxonomy->parent == $tax->term_id) :
										$parenttaxslug = $mastertaxonomies[$i]->slug;
										$childrentaxonomies[] = array('name' => $mastertaxonomy->name, 'description' => $mastertaxonomy->taxonomy_description, 'slug' => $mastertaxonomy->slug, 'parent_slug' => $parenttaxslug);
									endif;
								endforeach;
							endif;
						endif;
					endforeach;
					$query = "SELECT * FROM ".$wpdb->prefix."terms a, ".$wpdb->prefix."term_taxonomy b where a.term_id = b.term_id and b.taxonomy = '".$taxonomy_to_sync."'";
					$taxonomies = $wpdb->get_results($query);
					//print_r($taxonomies); echo '<br /><br />'; // For Debugging
					// Now we can deal with the child taxonomies
					if ($childrentaxonomies) :
						foreach ($childrentaxonomies as $key => $childtaxonomy) :
							foreach ($taxonomies as $i => $tax) :
								if ($childtaxonomy['parent_slug'] == $tax->slug) :
									$childtaxonomyparent = $taxonomies[$i]->term_id;
									if ($childtaxonomyparent) :
										// Insert Child Taxonomy
										$term = wp_insert_term($childtaxonomy['name'], $taxonomy_to_sync, array('description' => $childtaxonomy['description'], 'slug' => $childtaxonomy['slug'], 'parent' => $childtaxonomyparent));
									endif;
								endif;
							endforeach;
						endforeach;
					endif;
					// Restore
					restore_current_blog();
				endif;
			endforeach;
			// Clear Term Cache For Each Site
			foreach ($sites as $site) :
				// Skip Master Site
				if ($site->blog_id != 1) :				
					// Switch
					switch_to_blog($site->blog_id);
					$query = "SELECT * FROM ".$wpdb->prefix."terms a, ".$wpdb->prefix."term_taxonomy b where a.term_id = b.term_id and b.taxonomy = '".$taxonomy_to_sync."'";
					$taxonomies = $wpdb->get_results($query);
					foreach ($taxonomies as $tax) :
						ms_taxonomy_sync_clean_term_cache($tax->term_id, $taxonomy_to_sync, true, true);
					endforeach;
					restore_current_blog();
				endif;
			endforeach;	
		
		// End For Each Taxonomy
		endforeach;
		
	endif;
}

// Debug
function ms_taxonomy_sync_debug() {
	if (isset($_GET['ms_taxonomy_sync_debug'])) :
		ms_taxonomy_sync();
	endif;
}
add_action( 'admin_init', 'ms_taxonomy_sync_debug' );

function ms_taxonomy_sync_clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true, $force_clean_taxonomy = false) {
	global $wpdb;
	static $cleaned = array();

	if ( !is_array($ids) )
		$ids = array($ids);

	$taxonomies = array();
	// If no taxonomy, assume tt_ids.
	if ( empty($taxonomy) ) {
		$tt_ids = array_map('intval', $ids);
		$tt_ids = implode(', ', $tt_ids);
		$terms = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)");
		$ids = array();
		foreach ( (array) $terms as $term ) {
			$taxonomies[] = $term->taxonomy;
			$ids[] = $term->term_id;
			wp_cache_delete($term->term_id, $term->taxonomy);
		}
		$taxonomies = array_unique($taxonomies);
	} else {
		$taxonomies = array($taxonomy);
		foreach ( $taxonomies as $taxonomy ) {
			foreach ( $ids as $id ) {
				wp_cache_delete($id, $taxonomy);
			}
		}
	}

	foreach ( $taxonomies as $taxonomy ) {
		if ( isset($cleaned[$taxonomy]) && ! $force_clean_taxonomy )
			continue;
		$cleaned[$taxonomy] = true;

		if ( $clean_taxonomy ) {
			wp_cache_delete('all_ids', $taxonomy);
			wp_cache_delete('get', $taxonomy);
			
			// clear get_terms cache
			$cache_keys = wp_cache_get( 'get_terms:cache_keys', 'terms' );
			if ( ! empty( $cache_keys ) ) {
				foreach ( $cache_keys as $key => $cache_taxonomies ) {
					if ( in_array( $taxonomy, $cache_taxonomies ) ) {
						wp_cache_delete( "get_terms:{$key}", 'terms' );
						unset( $cache_keys[$key] );
					}
				}
			} else {
				$cache_keys = array();
			}
			wp_cache_set( 'get_terms:cache_keys', $cache_keys, 'terms' );
			
			delete_option("{$taxonomy}_children");
			// Regenerate {$taxonomy}_children
			_get_term_hierarchy($taxonomy);
		}

		do_action('clean_term_cache', $ids, $taxonomy);
	}
}

Tags: , , , , , , , ,

7 Responses to “Multisite Taxonomy Sync – Updates To My Multisite Category Sync Plugin”

  1. Parker February 11, 2012 at 8:16 pm #

    If tweaked, could this work to sync my navigation menu?

  2. Kyle March 18, 2012 at 1:02 am #

    This is exactly what I need! It all seems to work great but the descriptions aren’t changing. Would love to use this.

    Thanks.

    • Scott March 18, 2012 at 3:49 pm #

      I really need to revisit this. Looks like its been helpful for you guys and there might be a few bugs after testing on different environments. Thanks for all the feedback!

  3. Aris April 10, 2012 at 1:26 am #

    This plugin has been pretty helpful but I’ve run into some problems using it so I’ll provide some info to help debug this and perhaps improve on it.
    it mostly works… BUT (unfortunately there is this word…) I’ve got a pretty large hierarchical taxonomy (about 1000 terms, 7 levels deep).
    When a taxonomy is synced, the 1st level is fine and on the 2nd level almost all terms are present multiple times. 3rd level doesn’t exist.
    When I sync a second time, 1st and 2nd levels are ok and 3rd level is full of duplicate terms.
    This goes on until all levels are done. and the vocabulary is finally ok.
    Unfortunately I haven’t been able to check if all terms have been synced (due to their number) but I just suppose they are.

    I’ve tried to find the cause of this but can’t seem to find it, any help is greatly appreciated!

  4. Corey May 2, 2012 at 12:54 am #

    Thank you so much for this plugin Scott! It’s really helpful for the project I’m currently working on. I’m setting up a MarketPress site where people can register and create a store. These people become admin after sign up and they can add taxonomy categories from their site’s dashboard.

    So my question is, is there any way to limit the “add taxonomy category” function for these admin? In other words, only the “super admin” can add/modify taxonomy categories and sync these to all other sites. Is there a way to do this?

    Any help would be appreciated! Thanks!

  5. Tomasz July 21, 2012 at 5:01 pm #

    I join to the Corey questions, how to do this?

    Thanks!

  6. scottbasgaard July 22, 2012 at 12:32 pm #

    Hey Everyone,

    It’s been some time since I’ve tested/ran this code. I developed it for a client’s site while I was working at WebDevStudios and it worked great for them so I wanted to share what I did to help others out and point people in the right direction since it looks like it could be useful.

    I’m not sure how compatible this is with 3.4.1 (it should be) but it’s possible you will run into issues/bugs.

    As far as limiting this to specific users or roles I would do a conditional to check for the user ID(s).

    http://codex.wordpress.org/Function_Reference/wp_get_current_user

    Thanks!

    Scott

Leave a Reply