| @@ -1,0 +1,455 @@ | ||
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * Network | |
| 4 | + * | |
| 5 | + * @package GamiPress\Network | |
| 6 | + * @author GamiPress <[email protected]>, Ruben Garcia <[email protected]> | |
| 7 | + * @since 1.4.0 | |
| 8 | + */ | |
| 9 | +// Exit if accessed directly | |
| 10 | +if( !defined( 'ABSPATH' ) ) exit; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * Initializes GamiPress on multisite installs | |
| 14 | + * | |
| 15 | + * @since 1.4.0 | |
| 16 | + */ | |
| 17 | +function gamipress_init_multisite() { | |
| 18 | + | |
| 19 | + global $wpdb; | |
| 20 | + | |
| 21 | + if( is_multisite() ) { | |
| 22 | + | |
| 23 | + // Update GamiPress database if network wide active | |
| 24 | + if( gamipress_is_network_wide_active() ) { | |
| 25 | + | |
| 26 | + // Setup WordPress database tables | |
| 27 | + GamiPress()->db->posts = $wpdb->base_prefix . 'posts'; | |
| 28 | + GamiPress()->db->postmeta = $wpdb->base_prefix . 'postmeta'; | |
| 29 | + GamiPress()->db->users = $wpdb->base_prefix . 'users'; | |
| 30 | + GamiPress()->db->usermeta = $wpdb->base_prefix . 'usermeta'; | |
| 31 | + | |
| 32 | + // Setup GamiPress database tables | |
| 33 | + GamiPress()->db->logs = $wpdb->base_prefix . 'gamipress_logs'; | |
| 34 | + GamiPress()->db->logs_meta = $wpdb->base_prefix . 'gamipress_logs_meta'; | |
| 35 | + GamiPress()->db->user_earnings = $wpdb->base_prefix . 'gamipress_user_earnings'; | |
| 36 | + GamiPress()->db->user_earnings_meta = $wpdb->base_prefix . 'gamipress_user_earnings_meta'; | |
| 37 | + | |
| 38 | + } | |
| 39 | + | |
| 40 | + } | |
| 41 | + | |
| 42 | +} | |
| 43 | +add_action( 'gamipress_init', 'gamipress_init_multisite', 1 ); | |
| 44 | + | |
| 45 | +/** | |
| 46 | + * Create array of blog ids in network | |
| 47 | + * | |
| 48 | + * @since 1.0.0 | |
| 49 | + * | |
| 50 | + * @return array Array of blog_ids | |
| 51 | + */ | |
| 52 | +function gamipress_get_network_site_ids() { | |
| 53 | + | |
| 54 | + global $wpdb; | |
| 55 | + | |
| 56 | + $cache = gamipress_get_cache( 'gamipress_blog_ids', false, false ); | |
| 57 | + | |
| 58 | + if( is_multisite() ) { | |
| 59 | + | |
| 60 | + // If result already cached, return it | |
| 61 | + if( is_array( $cache ) ) { | |
| 62 | + $blog_ids = $cache; | |
| 63 | + } else { | |
| 64 | + $blog_ids = $wpdb->get_results( "SELECT blog_id FROM " . $wpdb->base_prefix . "blogs" ); | |
| 65 | + | |
| 66 | + gamipress_set_cache( 'gamipress_blog_ids', $blog_ids ); | |
| 67 | + } | |
| 68 | + foreach ($blog_ids as $key => $value ) { | |
| 69 | + $sites[] = $value->blog_id; | |
| 70 | + } | |
| 71 | + | |
| 72 | + } else { | |
| 73 | + $sites[] = get_current_blog_id(); | |
| 74 | + } | |
| 75 | + | |
| 76 | + return $sites; | |
| 77 | + | |
| 78 | +} | |
| 79 | + | |
| 80 | +/** | |
| 81 | + * Replace per site queries to root site when GamiPress is active network wide | |
| 82 | + * | |
| 83 | + * @since 1.4.0 | |
| 84 | + * | |
| 85 | + * @param string $request | |
| 86 | + * @param WP_Query $wp_query | |
| 87 | + * | |
| 88 | + * @return string | |
| 89 | + */ | |
| 90 | +function gamipress_network_wide_post_request( $request, $wp_query ) { | |
| 91 | + | |
| 92 | + global $wpdb; | |
| 93 | + | |
| 94 | + // If GamiPress is active network wide and we are not in main site, then filter all queries to our post types | |
| 95 | + if( | |
| 96 | + gamipress_is_network_wide_active() | |
| 97 | + && ! is_main_site() | |
| 98 | + && isset( $wp_query->query_vars['post_type'] ) | |
| 99 | + ) { | |
| 100 | + | |
| 101 | + $post_type = $wp_query->query_vars['post_type']; | |
| 102 | + | |
| 103 | + if( is_array( $post_type ) ) { | |
| 104 | + $post_type = $post_type[0]; | |
| 105 | + } | |
| 106 | + | |
| 107 | + if( | |
| 108 | + in_array( $post_type, array( 'points-type', 'achievement-type', 'rank-type' ) ) | |
| 109 | + || in_array( $post_type, gamipress_get_requirement_types_slugs() ) | |
| 110 | + || in_array( $post_type, gamipress_get_achievement_types_slugs() ) | |
| 111 | + || in_array( $post_type, gamipress_get_rank_types_slugs() ) | |
| 112 | + ) { | |
| 113 | + | |
| 114 | + // Replace {prefix}{site}posts to {prefix}posts | |
| 115 | + $request = str_replace( $wpdb->posts, "{$wpdb->base_prefix}posts", $request ); | |
| 116 | + | |
| 117 | + // Replace {prefix}{site}postmeta to {prefix}postmeta | |
| 118 | + $request = str_replace( $wpdb->postmeta, "{$wpdb->base_prefix}postmeta", $request ); | |
| 119 | + | |
| 120 | + } | |
| 121 | + | |
| 122 | + } | |
| 123 | + | |
| 124 | + return $request; | |
| 125 | + | |
| 126 | +} | |
| 127 | +add_filter( 'posts_request', 'gamipress_network_wide_post_request', 10, 2 ); | |
| 128 | + | |
| 129 | +/** | |
| 130 | + * Check if GamiPress is network wide active | |
| 131 | + * | |
| 132 | + * @since 1.4.0 | |
| 133 | + * | |
| 134 | + * @return bool | |
| 135 | + */ | |
| 136 | +function gamipress_is_network_wide_active() { | |
| 137 | + | |
| 138 | + // Available filter to disable network data centralization | |
| 139 | + if( apply_filters( 'gamipress_disable_network_data_centralization', false ) ) { | |
| 140 | + GamiPress()->network_wide_active = false; | |
| 141 | + return false; | |
| 142 | + } | |
| 143 | + | |
| 144 | + if( GamiPress()->network_wide_active === null ) { | |
| 145 | + | |
| 146 | + if( ! is_multisite() ) { | |
| 147 | + | |
| 148 | + // Set to false if not is a multisite install | |
| 149 | + GamiPress()->network_wide_active = false; | |
| 150 | + | |
| 151 | + } else { | |
| 152 | + | |
| 153 | + // Normally the is_plugin_active_for_network() function is only available in the admin area | |
| 154 | + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { | |
| 155 | + require_once ABSPATH . '/wp-admin/includes/plugin.php'; | |
| 156 | + } | |
| 157 | + | |
| 158 | + GamiPress()->network_wide_active = is_plugin_active_for_network( 'gamipress/gamipress.php' ); | |
| 159 | + } | |
| 160 | + | |
| 161 | + } | |
| 162 | + | |
| 163 | + return GamiPress()->network_wide_active; | |
| 164 | + | |
| 165 | +} | |
| 166 | + | |
| 167 | +/** | |
| 168 | + * Check if a plugin is active on any site of network | |
| 169 | + * | |
| 170 | + * @since 1.4.8 | |
| 171 | + * | |
| 172 | + * @param string $plugin Path to the main plugin file from plugins directory. | |
| 173 | + * | |
| 174 | + * @return bool | |
| 175 | + */ | |
| 176 | +function gamipress_is_plugin_active_on_network( $plugin ) { | |
| 177 | + | |
| 178 | + $active_sites = gamipress_get_plugin_active_sites( $plugin ); | |
| 179 | + | |
| 180 | + return ! empty( $active_sites ); | |
| 181 | + | |
| 182 | +} | |
| 183 | + | |
| 184 | +/** | |
| 185 | + * Get all sites a plugin is active | |
| 186 | + * | |
| 187 | + * @since 1.4.8 | |
| 188 | + * | |
| 189 | + * @param string $plugin Path to the main plugin file from plugins directory. | |
| 190 | + * | |
| 191 | + * @return array | |
| 192 | + */ | |
| 193 | +function gamipress_get_plugin_active_sites( $plugin ) { | |
| 194 | + | |
| 195 | + // Bail if not is a multisite install | |
| 196 | + if( ! is_multisite() ) { | |
| 197 | + return array( get_current_blog_id() ); | |
| 198 | + } | |
| 199 | + | |
| 200 | + // If plugin is active network wide, return all sites | |
| 201 | + if( is_plugin_active_for_network( $plugin ) ) { | |
| 202 | + return gamipress_get_network_site_ids(); | |
| 203 | + } | |
| 204 | + | |
| 205 | + $sites_active = array(); | |
| 206 | + | |
| 207 | + // Store a copy of the original ID for later | |
| 208 | + $blog_id = get_current_blog_id(); | |
| 209 | + | |
| 210 | + // Loop through all sites | |
| 211 | + foreach( gamipress_get_network_site_ids() as $site_blog_id ) { | |
| 212 | + | |
| 213 | + // Get active plugins in this site and check if plugin is active | |
| 214 | + $active_plugins = gamipress_get_active_plugins_in_site( $site_blog_id ); | |
| 215 | + | |
| 216 | + if( in_array( $plugin, $active_plugins, true ) ) { | |
| 217 | + $sites_active[] = $site_blog_id; | |
| 218 | + } | |
| 219 | + | |
| 220 | + } | |
| 221 | + | |
| 222 | + // Restore the original blog so the sky doesn't fall | |
| 223 | + if ( $blog_id != get_current_blog_id() && is_multisite() ) { | |
| 224 | + restore_current_blog(); | |
| 225 | + } | |
| 226 | + | |
| 227 | + return $sites_active; | |
| 228 | + | |
| 229 | +} | |
| 230 | + | |
| 231 | +/** | |
| 232 | + * Get active plugins in a site (used to cache results) | |
| 233 | + * | |
| 234 | + * @since 1.4.8 | |
| 235 | + * | |
| 236 | + * @param int $site_blog_id Site ID. | |
| 237 | + * | |
| 238 | + * @return array | |
| 239 | + */ | |
| 240 | +function gamipress_get_active_plugins_in_site( $site_blog_id = null ) { | |
| 241 | + | |
| 242 | + if( $site_blog_id === null ) { | |
| 243 | + $site_blog_id = get_current_blog_id(); | |
| 244 | + } | |
| 245 | + | |
| 246 | + $cache = gamipress_get_cache( 'gamipress_active_plugins', array(), false ); | |
| 247 | + | |
| 248 | + if( ! is_array( $cache ) ) { | |
| 249 | + $cache = array(); | |
| 250 | + } | |
| 251 | + | |
| 252 | + // If result already cached, return it | |
| 253 | + if( isset( $cache[$site_blog_id] ) ) { | |
| 254 | + return $cache[$site_blog_id]; | |
| 255 | + } | |
| 256 | + | |
| 257 | + // Store a copy of the original ID for later | |
| 258 | + $blog_id = get_current_blog_id(); | |
| 259 | + | |
| 260 | + // If we're polling a different blog, switch to it | |
| 261 | + if ( $blog_id != $site_blog_id ) { | |
| 262 | + switch_to_blog( $site_blog_id ); | |
| 263 | + } | |
| 264 | + | |
| 265 | + // Get active plugins in this site | |
| 266 | + $cache[$site_blog_id] = (array) get_option( 'active_plugins', array() ); | |
| 267 | + | |
| 268 | + // Restore the original blog | |
| 269 | + if ( $blog_id != $site_blog_id && is_multisite() ) { | |
| 270 | + restore_current_blog(); | |
| 271 | + } | |
| 272 | + | |
| 273 | + gamipress_set_cache( 'gamipress_active_plugins', $cache ); | |
| 274 | + | |
| 275 | + return $cache[$site_blog_id]; | |
| 276 | + | |
| 277 | +} | |
| 278 | + | |
| 279 | +/** | |
| 280 | + * On save settings, also update GamiPress site option to make it network wide accessible | |
| 281 | + * | |
| 282 | + * @since 1.4.0 | |
| 283 | + * | |
| 284 | + * @param string $override | |
| 285 | + * @param mixed $options | |
| 286 | + * @param CMB2 $cmb | |
| 287 | + * | |
| 288 | + * @return mixed | |
| 289 | + */ | |
| 290 | +function gamipress_save_network_settings( $override, $options, $cmb ) { | |
| 291 | + | |
| 292 | + if( gamipress_is_network_wide_active() ) { | |
| 293 | + // Set options as network option | |
| 294 | + update_site_option( 'gamipress_settings', $options ); | |
| 295 | + } | |
| 296 | + | |
| 297 | + return $override; | |
| 298 | + | |
| 299 | +} | |
| 300 | +add_action( 'cmb2_override_option_save_gamipress_settings', 'gamipress_save_network_settings', 10, 3 ); | |
| 301 | + | |
| 302 | +/** | |
| 303 | + * Filter the post edit link to return edit link from main site | |
| 304 | + * | |
| 305 | + * @since 1.4.0 | |
| 306 | + * | |
| 307 | + * @param string $link The edit link. | |
| 308 | + * @param int $post_id Post ID. | |
| 309 | + * @param string $context The link context. If set to 'display' then ampersands are encoded. | |
| 310 | + * | |
| 311 | + * @return string | |
| 312 | + */ | |
| 313 | +function gamipress_main_site_edit_post_link( $link, $post_id, $context ) { | |
| 314 | + | |
| 315 | + if( ! gamipress_is_network_wide_active() || is_main_site() ) { | |
| 316 | + return $link; | |
| 317 | + } | |
| 318 | + | |
| 319 | + // Try to get the post type of current site | |
| 320 | + $post_type = get_post_type( $post_id ); | |
| 321 | + | |
| 322 | + if( ! $post_type ) { | |
| 323 | + // If this post not exists on current site, then get his post type from main site | |
| 324 | + $post_type = gamipress_get_post_type( $post_id ); | |
| 325 | + } | |
| 326 | + | |
| 327 | + if( | |
| 328 | + in_array( $post_type, array( 'points-type', 'achievement-type', 'rank-type' ) ) | |
| 329 | + || in_array( $post_type, gamipress_get_requirement_types_slugs() ) | |
| 330 | + || in_array( $post_type, gamipress_get_achievement_types_slugs() ) | |
| 331 | + || in_array( $post_type, gamipress_get_rank_types_slugs() ) | |
| 332 | + ) { | |
| 333 | + | |
| 334 | + $post_type_object = get_post_type_object( $post_type ); | |
| 335 | + | |
| 336 | + if ( 'display' == $context ) | |
| 337 | + $action = '&action=edit'; | |
| 338 | + else | |
| 339 | + $action = '&action=edit'; | |
| 340 | + | |
| 341 | + $link = get_admin_url( get_main_site_id(), sprintf( $post_type_object->_edit_link . $action, $post_id ) ); | |
| 342 | + } | |
| 343 | + | |
| 344 | + return $link; | |
| 345 | + | |
| 346 | +} | |
| 347 | +add_filter( 'get_edit_post_link', 'gamipress_main_site_edit_post_link', 10, 3 ); | |
| 348 | + | |
| 349 | +/** | |
| 350 | + * Override CMB2 can save function to avoid issues on multisite installs | |
| 351 | + * | |
| 352 | + * @since 1.4.9 | |
| 353 | + * | |
| 354 | + * @param bool $can_save | |
| 355 | + * @param object $cmb | |
| 356 | + * | |
| 357 | + * @return bool | |
| 358 | + */ | |
| 359 | +function gamipress_network_can_save_meta_boxes( $can_save, $cmb ) { | |
| 360 | + | |
| 361 | + global $post; | |
| 362 | + | |
| 363 | + if( is_multisite() && $post ) { | |
| 364 | + | |
| 365 | + // Custom can save check | |
| 366 | + $can_save = ( | |
| 367 | + $cmb->prop( 'save_fields' ) | |
| 368 | + // check nonce | |
| 369 | + && isset( $_POST[ $cmb->nonce() ] ) | |
| 370 | + && wp_verify_nonce( $_POST[ $cmb->nonce() ], $cmb->nonce() ) | |
| 371 | + // check if autosave | |
| 372 | + && ! ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) | |
| 373 | + // get the metabox types & compare it to this type | |
| 374 | + && ( in_array( $post->post_type, $cmb->box_types() ) ) | |
| 375 | + // Don't do updates during a switch-to-blog instance. | |
| 376 | + //&& ! ( is_multisite() && ms_is_switched() ) // Removed, ms_is_switched() always returns true | |
| 377 | + ); | |
| 378 | + | |
| 379 | + } | |
| 380 | + | |
| 381 | + return $can_save; | |
| 382 | + | |
| 383 | +} | |
| 384 | +add_filter( 'cmb2_can_save', 'gamipress_network_can_save_meta_boxes', 10, 2 ); | |
| 385 | + | |
| 386 | +/** | |
| 387 | + * Force to clean switched sites global to avoid issues with other plugins (since wp_restore_blog() doesn't cleans it) | |
| 388 | + * | |
| 389 | + * @since 1.5.7 | |
| 390 | + */ | |
| 391 | +function gamipress_fix_network_wp_insert_post() { | |
| 392 | + | |
| 393 | + if ( is_multisite() && ms_is_switched() ) { | |
| 394 | + // Force to clean switched sites global | |
| 395 | + $GLOBALS['_wp_switched_stack'] = array(); | |
| 396 | + } | |
| 397 | + | |
| 398 | +} | |
| 399 | +add_action( 'wp_insert_post', 'gamipress_fix_network_wp_insert_post', 9 ); | |
| 400 | + | |
| 401 | +/** | |
| 402 | + * Switch to main site | |
| 403 | + * | |
| 404 | + * @since 1.6.3 | |
| 405 | + * @updated 2.1.0 Update the $gamipress_original_blog_id global var | |
| 406 | + * | |
| 407 | + * @return int The blog ID before switch to main site | |
| 408 | + */ | |
| 409 | +function gamipress_switch_to_main_site() { | |
| 410 | + | |
| 411 | + global $gamipress_original_blog_id; | |
| 412 | + | |
| 413 | + $gamipress_original_blog_id = get_current_blog_id(); | |
| 414 | + | |
| 415 | + // Switch to main site if not already on main site | |
| 416 | + if( ! is_main_site() ) { | |
| 417 | + switch_to_blog( get_main_site_id() ); | |
| 418 | + } | |
| 419 | + | |
| 420 | + return $gamipress_original_blog_id; | |
| 421 | + | |
| 422 | +} | |
| 423 | + | |
| 424 | +/** | |
| 425 | + * Switch to main site just if GamiPress is network wide active | |
| 426 | + * | |
| 427 | + * @since 1.6.3 | |
| 428 | + * | |
| 429 | + * @return int The blog ID before switch to main site | |
| 430 | + */ | |
| 431 | +function gamipress_switch_to_main_site_if_network_wide_active() { | |
| 432 | + | |
| 433 | + // Switch to main site if GamiPress is network wide active | |
| 434 | + if( gamipress_is_network_wide_active() ) { | |
| 435 | + return gamipress_switch_to_main_site(); | |
| 436 | + } | |
| 437 | + | |
| 438 | + return get_current_blog_id(); | |
| 439 | + | |
| 440 | +} | |
| 441 | + | |
| 442 | +/** | |
| 443 | + * Get the original site ID even if GamiPress has switched to main site | |
| 444 | + * | |
| 445 | + * @since 2.1.0 | |
| 446 | + * | |
| 447 | + * @return int The blog ID before switch to main site | |
| 448 | + */ | |
| 449 | +function gamipress_get_original_site_id() { | |
| 450 | + | |
| 451 | + global $gamipress_original_blog_id; | |
| 452 | + | |
| 453 | + return ( $gamipress_original_blog_id ? $gamipress_original_blog_id : get_current_blog_id() ); | |
| 454 | + | |
| 455 | +} | |