<?php

/**
 * Plugin Name: BP Multi Network
 * Plugin URI:  https://wordpress.org/plugins/bp-multi-network/
 * Description: Unique BuddyPress networks in your WordPress multi-network installation
 * Version:     0.2.0
 * Author:      The BuddyPress Community
 * Author URI:  http://buddypress.org
*/

/**
 * The main BuddyPress multi-network class
 *
 * This class adds filters to three places in BuddyPress to intercept and modify
 * database table prefixes based on the current network.
 */
class BP_Multi_Network {

	/**
	 * Array of BuddyPress user-meta keys
	 *
	 * @var array
	 */
	private $user_meta_keys = array(
		'last_activity'                             => false,
		'bp_new_mention_count'                      => false,
		'bp_favorite_activities'                    => false,
		'bp_latest_update'                          => false,
		'total_friend_count'                        => false,
		'total_group_count'                         => false,
		'notification_activity_new_mention'         => false,
		'notification_activity_new_reply'           => false,
		'notification_groups_group_updated'         => false,
		'notification_groups_membership_request'    => false,
		'notification_membership_request_completed' => false,
		'notification_groups_admin_promotion'       => false,
		'notification_groups_invite'                => false,
		'notification_messages_new_message'         => false,
		'notification_messages_new_notice'          => false,
		'closed_notices'                            => false,
		'profile_last_updated'                      => false
	);

	/** Filters ***************************************************************/

	/**
	 * Attach filters to what are usually considered "global" values, and modify
	 * them based on the currently loaded WordPress network of sites.
	 *
	 * - Global cache key
	 * - Table prefix
	 * - User meta key
	 */
	public function __construct() {
		add_filter( 'bp_core_get_global_cache_key', array( $this, 'filter_global_cache_key' ) );
		add_filter( 'bp_core_get_table_prefix',     array( $this, 'filter_table_prefix'     ) );
		add_filter( 'bp_get_user_meta_key',         array( $this, 'filter_user_meta_key'    ) );
	}

	/**
	 * Filter the "global" cache key and maybe add a network ID
	 *
	 * @param string $key
	 * @return string
	 */
	public function filter_global_cache_key( $key = 'bp' ) {

		// Override prefix if not main network and there is a prefix match
		if ( ! $this->is_main_network() && ( true === $this->is_base_cache_key( $key ) ) ) {
			$key = $this->get_network_cache_key() . $key;
		}

		// Use this network's prefix
		return $key;
	}

	/**
	 * Filter the DB table prefix and maybe add a network ID
	 *
	 * @param string $prefix
	 * @return string
	 */
	public function filter_table_prefix( $prefix = '' ) {

		// Override prefix if not main network and there is a prefix match
		if ( ! $this->is_main_network() && ( true === $this->is_base_db_prefix( $prefix ) ) ) {
			$prefix = $this->get_network_db_prefix();
		}

		// Use this network's prefix
		return $prefix;
	}

	/**
	 * Filter the appropriate BuddyPress user-meta keys and prefix them with the
	 * ID of the network they belong to.
	 *
	 * @param string $key
	 * @return string
	 */
	public function filter_user_meta_key( $key = '' ) {

		// Bail if key is not BuddyPress user meta
		if ( ! isset( $this->user_meta_keys[ $key ] ) ) {
			return $key;
		}

		// Bail if on the main network
		if ( $this->is_main_network() ) {
			return $key;
		}

		// Set the user meta key to the new prefix
		if ( false === $this->user_meta_keys[ $key ] ) {
			$this->user_meta_keys[ $key ] = $this->get_network_db_prefix() . $key;
		}

		// Return the modified user meta key
		return $this->user_meta_keys[ $key ];
	}

	/** Helpers ***************************************************************/

	/**
	 * Whether or not the current DB query is from the main network.
	 *
	 * The main network is typically ID 1 (and does not have a modified prefix)
	 * but we also need to check for the PRIMARY_NETWORK_ID constant introduced
	 * in WordPress 3.7 for more sophisticated installations.
	 *
	 * @global object $wpdb
	 * @return boolean
	 */
	private function is_main_network() {
		global $wpdb;

		// Use primary network ID if defined
		$primary_network_id = defined( 'PRIMARY_NETWORK_ID' )
			? (int) PRIMARY_NETWORK_ID
			: 1;

		return (bool) ( $wpdb->siteid === $primary_network_id );
	}

	/** Cache Keys ************************************************************/

	/**
	 * Compare a given cache-key with BuddyPress's "global" cache-key.
	 *
	 * @param string $key
	 * @return string
	 */
	private function is_base_cache_key( $key = '' ) {
		return (bool) ( 'bp' === $key );
	}

	/**
	 * Return the cache-key for the current network
	 *
	 * Note that we're using the prefix for the root-blog, and not the network
	 * ID itself. This is because BuddyPress stores much of its data in the
	 * root-blog options table VS the sitemeta table.
	 *
	 * @global object $wpdb
	 * @return string
	 */
	private function get_network_cache_key() {
		global $wpdb;
		return $wpdb->get_blog_prefix( get_current_site()->blog_id );
	}

	/** DB ********************************************************************/

	/**
	 * Compare a given DB table prefix with the base DB prefix.
	 *
	 * @global object $wpdb
	 * @param string $prefix
	 * @return string
	 */
	private function is_base_db_prefix( $prefix = '' ) {
		global $wpdb;
		return (bool) ( $prefix === $wpdb->base_prefix );
	}

	/**
	 * Return the DB table prefix for the current network
	 *
	 * Note that we're using the prefix for the root-blog, and not the network
	 * ID itself. This is because BuddyPress stores much of its data in the
	 * root-blog options table VS the sitemeta table.
	 *
	 * @global object $wpdb
	 * @return string
	 */
	private function get_network_db_prefix() {
		global $wpdb;
		return $wpdb->get_blog_prefix( get_current_site()->blog_id );
	}
}
new BP_Multi_Network();
