Completed
Push — fix/block-vr ( dd4d13...b6c320 )
by Bernhard
196:37 queued 183:38
created

class.jetpack-network.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Used to manage Jetpack installation on Multisite Network installs
5
 *
6
 * SINGLETON: To use call Jetpack_Network::init()
7
 *
8
 * DO NOT USE ANY STATIC METHODS IN THIS CLASS!!!!!!
9
 *
10
 * @since 2.9
11
 */
12
class Jetpack_Network {
13
14
	/**
15
	 * Holds a static copy of Jetpack_Network for the singleton
16
	 *
17
	 * @since 2.9
18
	 * @var Jetpack_Network
19
	 */
20
	private static $instance = null;
21
22
	/**
23
	 * Name of the network wide settings
24
	 *
25
	 * @since 2.9
26
	 * @var string
27
	 */
28
	private $settings_name = 'jetpack-network-settings';
29
30
	/**
31
	 * Defaults for settings found on the Jetpack > Settings page
32
	 *
33
	 * @since 2.9
34
	 * @var array
35
	 */
36
	private $setting_defaults = array(
37
		'auto-connect'                  => 0,
38
		'sub-site-connection-override'  => 1,
39
		//'manage_auto_activated_modules' => 0,
40
	);
41
42
	/**
43
	 * Constructor
44
	 *
45
	 * @since 2.9
46
	 */
47
	private function __construct() {
48
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); // For the is_plugin... check
49
		require_once( JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php' ); // For managing the global whitelist
50
		/*
51
		 * Sanity check to ensure the install is Multisite and we
52
		 * are in Network Admin
53
		 */
54
		if ( is_multisite() && is_network_admin() ) {
55
			add_action( 'network_admin_menu', array( $this, 'add_network_admin_menu' ) );
56
			add_action( 'network_admin_edit_jetpack-network-settings', array( $this, 'save_network_settings_page' ), 10, 0 );
57
			add_filter( 'admin_body_class', array( $this, 'body_class' ) );
58
59
			if ( isset( $_GET['page'] ) && 'jetpack' == $_GET['page'] ) {
60
				add_action( 'admin_init', array( $this, 'jetpack_sites_list' ) );
61
			}
62
		}
63
64
		/*
65
		 * Things that should only run on multisite
66
		 */
67
		if ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
68
			add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
69
70
			/*
71
			 * If admin wants to automagically register new sites set the hook here
72
			 *
73
			 * This is a hacky way because xmlrpc is not available on wpmu_new_blog
74
			 */
75
			if ( $this->get_option( 'auto-connect' ) == 1 ) {
76
				add_action( 'wpmu_new_blog', array( $this, 'do_automatically_add_new_site' ) );
77
			}
78
		}
79
80
		// Remove the toggles for 2.9, re-evaluate how they're done and added for a 3.0 release. They don't feel quite right yet.
81
		// add_filter( 'jetpack_get_default_modules', array( $this, 'set_auto_activated_modules' ) );
82
	}
83
84
	/**
85
	 * Sets which modules get activated by default on subsite connection.
86
	 * Modules can be set in Network Admin > Jetpack > Settings
87
	 *
88
	 * @since 2.9
89
	 *
90
	 * @param array $modules
91
	 *
92
	 * @return array
93
	 **/
94
	public function set_auto_activated_modules( $modules ) {
95
		return $modules;
96
97
		/* Remove the toggles for 2.9, re-evaluate how they're done and added for a 3.0 release. They don't feel quite right yet.
98
		if( 1 == $this->get_option( 'manage_auto_activated_modules' ) ) {
99
			return (array) $this->get_option( 'modules' );
100
		} else {
101
			return $modules;
102
		}
103
		*/
104
	}
105
106
	/**
107
	 * Registers new sites upon creation
108
	 *
109
	 * @since 2.9
110
	 * @uses  wpmu_new_blog
111
	 *
112
	 * @param int $blog_id
113
	 **/
114
	public function do_automatically_add_new_site( $blog_id ) {
115
		$this->do_subsiteregister( $blog_id );
116
	}
117
118
	/**
119
	 * Adds .network-admin class to the body tag
120
	 * Helps distinguish network admin JP styles from regular site JP styles
121
	 *
122
	 * @since 2.9
123
	 */
124
	public function body_class( $classes ) {
125
		return trim( $classes ) . ' network-admin ';
126
	}
127
128
	/**
129
	 * Provides access to an instance of Jetpack_Network
130
	 *
131
	 * This is how the Jetpack_Network object should *always* be accessed
132
	 *
133
	 * @since 2.9
134
	 * @return Jetpack_Network
135
	 */
136
	public static function init() {
137
		if ( ! self::$instance || ! is_a( self::$instance, 'Jetpack_Network' ) ) {
138
			self::$instance = new Jetpack_Network;
139
		}
140
141
		return self::$instance;
142
	}
143
144
	/**
145
	 * Registers the Multisite admin bar menu item shortcut.
146
	 * This shortcut helps users quickly and easily navigate to the Jetpack Network Admin
147
	 * menu from anywhere in their network.
148
	 *
149
	 * @since 2.9
150
	 */
151
	public function register_menubar() {
152
		add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
153
	}
154
155
	/**
156
	 * Runs when Jetpack is deactivated from the network admin plugins menu.
157
	 * Each individual site will need to have Jetpack::disconnect called on it.
158
	 * Site that had Jetpack individually enabled will not be disconnected as
159
	 * on Multisite individually activated plugins are still activated when
160
	 * a plugin is deactivated network wide.
161
	 *
162
	 * @since 2.9
163
	 **/
164
	public function deactivate() {
165
		// Only fire if in network admin
166
		if ( ! is_network_admin() ) {
167
			return;
168
		}
169
170
		$sites = get_sites();
171
172
		foreach ( $sites as $s ) {
173
			switch_to_blog( $s->blog_id );
174
			$active_plugins = get_option( 'active_plugins' );
175
176
			/*
177
			 * If this plugin was activated in the subsite individually
178
			 * we do not want to call disconnect. Plugins activated
179
		 	 * individually (before network activation) stay activated
180
		 	 * when the network deactivation occurs
181
		 	 */
182
			if ( ! in_array( 'jetpack/jetpack.php', $active_plugins ) ) {
183
				Jetpack::disconnect();
184
			}
185
		}
186
		restore_current_blog();
187
	}
188
189
	/**
190
	 * Adds a link to the Jetpack Network Admin page in the network admin menu bar.
191
	 *
192
	 * @since 2.9
193
	 **/
194
	public function add_to_menubar() {
195
		global $wp_admin_bar;
196
		// Don't show for logged out users or single site mode.
197
		if ( ! is_user_logged_in() || ! is_multisite() ) {
198
			return;
199
		}
200
201
		$wp_admin_bar->add_node( array(
202
			'parent' => 'network-admin',
203
			'id'     => 'network-admin-jetpack',
204
			'title'  => 'Jetpack',
205
			'href'   => $this->get_url( 'network_admin_page' ),
206
		) );
207
	}
208
209
	/**
210
	 * Returns various URL strings. Factory like
211
	 *
212
	 * $args can be a string or an array.
213
	 * If $args is an array there must be an element called name for the switch statement
214
	 *
215
	 * Currently supports:
216
	 * - subsiteregister: Pass array( 'name' => 'subsiteregister', 'site_id' => SITE_ID )
217
	 * - network_admin_page: Provides link to /wp-admin/network/JETPACK
218
	 * - subsitedisconnect: Pass array( 'name' => 'subsitedisconnect', 'site_id' => SITE_ID )
219
	 *
220
	 * @since 2.9
221
	 *
222
	 * @param Mixed $args
223
	 *
224
	 * @return String
225
	 **/
226
	public function get_url( $args ) {
227
		$url = null; // Default url value
228
229
		if ( is_string( $args ) ) {
230
			$name = $args;
231
		} else {
232
			$name = $args['name'];
233
		}
234
235
		switch ( $name ) {
236
			case 'subsiteregister':
237
				if ( ! isset( $args['site_id'] ) ) {
238
					break; // If there is not a site id present we cannot go further
239
				}
240
				$url = network_admin_url(
241
					'admin.php?page=jetpack&action=subsiteregister&site_id='
242
					. $args['site_id']
243
				);
244
				break;
245
246
			case 'network_admin_page':
247
				$url = network_admin_url( 'admin.php?page=jetpack' );
248
				break;
249
250
			case 'subsitedisconnect':
251
				if ( ! isset( $args['site_id'] ) ) {
252
					break; // If there is not a site id present we cannot go further
253
				}
254
				$url = network_admin_url(
255
					'admin.php?page=jetpack&action=subsitedisconnect&site_id='
256
					. $args['site_id']
257
				);
258
				break;
259
		}
260
261
		return $url;
262
	}
263
264
	/**
265
	 * Adds the Jetpack  menu item to the Network Admin area
266
	 *
267
	 * @since 2.9
268
	 */
269
	public function add_network_admin_menu() {
270
		add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_network_admin_page', 'jetpack', array( $this, 'wrap_network_admin_page' ), 'div', 3 );
271
		$jetpack_sites_page_hook = add_submenu_page( 'jetpack', __( 'Jetpack Sites', 'jetpack' ), __( 'Sites', 'jetpack' ), 'jetpack_network_sites_page', 'jetpack', array( $this, 'wrap_network_admin_page' ) );
272
		$jetpack_settings_page_hook = add_submenu_page( 'jetpack', __( 'Settings', 'jetpack' ), __( 'Settings', 'jetpack' ), 'jetpack_network_settings_page', 'jetpack-settings', array( $this, 'wrap_render_network_admin_settings_page' ) );
273
		add_action( "admin_print_styles-$jetpack_sites_page_hook",  array( 'Jetpack_Admin_Page', 'load_wrapper_styles' ) );
274
		add_action( "admin_print_styles-$jetpack_settings_page_hook",  array( 'Jetpack_Admin_Page', 'load_wrapper_styles' ) );
275
		/**
276
		 * As jetpack_register_genericons is by default fired off a hook,
277
		 * the hook may have already fired by this point.
278
		 * So, let's just trigger it manually.
279
		 */
280
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
281
		jetpack_register_genericons();
282
283 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
284
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
285
		}
286
287
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
288
	}
289
290
	/**
291
	 * Adds JP menu icon
292
	 *
293
	 * @since 2.9
294
	 **/
295
	function admin_menu_css() {
296
		wp_enqueue_style( 'jetpack-icons' );
297
	}
298
299
	/**
300
	 * Provides functionality for the Jetpack > Sites page.
301
	 * Does not do the display!
302
	 *
303
	 * @since 2.9
304
	 */
305
	public function jetpack_sites_list() {
306
		Jetpack::init();
307
308
		if ( isset( $_GET['action'] ) ) {
309
			switch ( $_GET['action'] ) {
310
				case 'subsiteregister':
311
					/*
312
					 * @todo check_admin_referer( 'jetpack-subsite-register' );
313
					 */
314
					Jetpack::log( 'subsiteregister' );
315
316
					// If !$_GET['site_id'] stop registration and error
317 View Code Duplication
					if ( ! isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
318
						// Log error to state cookie for display later
319
						/**
320
						 * @todo Make state messages show on Jetpack NA pages
321
						 **/
322
						Jetpack::state( 'missing_site_id', esc_html__( 'Site ID must be provided to register a sub-site.', 'jetpack' ) );
323
						break;
324
					}
325
326
					// Send data to register endpoint and retrieve shadow blog details
327
					$result = $this->do_subsiteregister();
328
					$url    = $this->get_url( 'network_admin_page' );
329
330
					if ( is_wp_error( $result ) ) {
331
						$url = add_query_arg( 'action', 'connection_failed', $url );
332
					} else {
333
						$url = add_query_arg( 'action', 'connected', $url );
334
					}
335
336
					wp_safe_redirect( $url );
337
					exit;
338
339
				case 'subsitedisconnect':
340
					Jetpack::log( 'subsitedisconnect' );
341
342 View Code Duplication
					if ( ! isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
343
						Jetpack::state( 'missing_site_id', esc_html__( 'Site ID must be provided to disconnect a sub-site.', 'jetpack' ) );
344
						break;
345
					}
346
347
					$this->do_subsitedisconnect();
348
					break;
349
350
				case 'connected':
351
				case 'connection_failed':
352
					add_action( 'jetpack_notices', array( $this, 'show_jetpack_notice' ) );
353
					break;
354
			}
355
		}
356
	}
357
358
	public function show_jetpack_notice() {
359
		if ( isset( $_GET['action'] ) && 'connected' == $_GET['action'] ) {
360
			$notice = __( 'Site successfully connected.', 'jetpack' );
361
			$classname = 'updated';
362
		} else if ( isset( $_GET['action'] ) && 'connection_failed' == $_GET['action'] ) {
363
			$notice = __( 'Site connection failed!', 'jetpack' );
364
			$classname = 'error';
365
		}
366
		?>
367
		<div id="message" class="<?php echo esc_attr( $classname );?> jetpack-message jp-connect" style="display:block !important;">
0 ignored issues
show
The variable $classname does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
368
			<p><?php echo esc_html( $notice ); ?></p>
0 ignored issues
show
The variable $notice does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
369
		</div>
370
		<?php
371
	}
372
373
	/**
374
	 * Disconnect functionality for an individual site
375
	 *
376
	 * @since 2.9
377
	 * @see   Jetpack_Network::jetpack_sites_list()
378
	 */
379
	public function do_subsitedisconnect( $site_id = null ) {
380
		if ( ! current_user_can( 'jetpack_disconnect' ) ) {
381
			return;
382
		}
383
		$site_id = ( is_null( $site_id ) ) ? $_GET['site_id'] : $site_id;
384
		switch_to_blog( $site_id );
385
		Jetpack::disconnect();
386
		restore_current_blog();
387
	}
388
389
	/**
390
	 * Registers a subsite with the Jetpack servers
391
	 *
392
	 * @since 2.9
393
	 * @todo  Break apart into easier to manage chunks that can be unit tested
394
	 * @see   Jetpack_Network::jetpack_sites_list();
395
	 */
396
	public function do_subsiteregister( $site_id = null ) {
397
		if ( ! current_user_can( 'jetpack_disconnect' ) ) {
398
			return;
399
		}
400
401
		if ( Jetpack::is_development_mode() ) {
402
			return;
403
		}
404
405
		$jp = Jetpack::init();
406
407
		// Figure out what site we are working on
408
		$site_id = ( is_null( $site_id ) ) ? $_GET['site_id'] : $site_id;
409
410
		// better to try (and fail) to set a higher timeout than this system
411
		// supports than to have register fail for more users than it should
412
		$timeout = Jetpack::set_min_time_limit( 60 ) / 2;
413
414
		// The blog id on WordPress.com of the primary network site
415
		$network_wpcom_blog_id = Jetpack_Options::get_option( 'id' );
416
417
		/*
418
		 * Here we need to switch to the subsite
419
		 * For the registration process we really only hijack how it
420
		 * works for an individual site and pass in some extra data here
421
		 */
422
		switch_to_blog( $site_id );
423
424
		// Save the secrets in the subsite so when the wpcom server does a pingback it
425
		// will be able to validate the connection
426
		$secrets = $jp->generate_secrets( 'register' );
427 View Code Duplication
		if (
428
			empty( $secrets['secret_1'] ) ||
429
			empty( $secrets['secret_2']  ) ||
430
			empty( $secrets['exp'] )
431
		) {
432
			return new Jetpack_Error( 'missing_secrets' );
433
		}
434
435
		// Gra info for gmt offset
436
		$gmt_offset = get_option( 'gmt_offset' );
437
		if ( ! $gmt_offset ) {
438
			$gmt_offset = 0;
439
		}
440
441
		/*
442
		 * Get the stats_option option from the db.
443
		 * It looks like the server strips this out so maybe it is not necessary?
444
		 * Does it match the Jetpack site with the old stats plugin id?
445
		 *
446
		 * @todo Find out if sending the stats_id is necessary
447
		 */
448
		$stat_options = get_option( 'stats_options' );
449
		$stat_id = $stat_options = isset( $stats_options['blog_id'] ) ? $stats_options['blog_id'] : null;
450
		$user_id = get_current_user_id();
451
452
		$tracks_identity = jetpack_tracks_get_identity( $user_id );
453
454
		/**
455
		 * Both `state` and `user_id` need to be sent in the request, even though they are the same value.
456
		 * Connecting via the network admin combines `register()` and `authorize()` methods into one step,
457
		 * because we assume the main site is already authorized. `state` is used to verify the `register()`
458
		 * request, while `user_id()` is used to create the token in the `authorize()` request.
459
		 */
460
		$args = array(
461
			'method'  => 'POST',
462
			'body'    => array(
463
				'network_url'           => $this->get_url( 'network_admin_page' ),
464
				'network_wpcom_blog_id' => $network_wpcom_blog_id,
465
				'siteurl'               => site_url(),
466
				'home'                  => home_url(),
467
				'gmt_offset'            => $gmt_offset,
468
				'timezone_string'       => (string) get_option( 'timezone_string' ),
469
				'site_name'             => (string) get_option( 'blogname' ),
470
				'secret_1'              => $secrets['secret_1'],
471
				'secret_2'              => $secrets['secret_2'],
472
				'site_lang'             => get_locale(),
473
				'timeout'               => $timeout,
474
				'stats_id'              => $stat_id, // Is this still required?
475
				'user_id'               => $user_id,
476
				'state'                 => $user_id,
477
				'_ui'                   => $tracks_identity['_ui'],
478
				'_ut'                   => $tracks_identity['_ut'],
479
				'jetpack_version'       => JETPACK__VERSION
480
			),
481
			'headers' => array(
482
				'Accept' => 'application/json',
483
			),
484
			'timeout' => $timeout,
485
		);
486
487
		Jetpack::apply_activation_source_to_args( $args['body'] );
488
489
		// Attempt to retrieve shadow blog details
490
		$response = Jetpack_Client::_wp_remote_request(
491
			Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'subsiteregister' ) ), $args, true
492
		);
493
494
		/*
495
		 * $response should either be invalid or contain:
496
		 * - jetpack_id	=> id
497
		 * - jetpack_secret => blog_token
498
		 * - jetpack_public
499
		 *
500
		 * Store the wpcom site details
501
		 */
502
		$valid_response = $jp->validate_remote_register_response( $response );
503
504
		if ( is_wp_error( $valid_response ) || ! $valid_response ) {
505
			restore_current_blog();
506
			return $valid_response;
507
		}
508
509
		// Grab the response values to work with
510
		$code   = wp_remote_retrieve_response_code( $response );
511
		$entity = wp_remote_retrieve_body( $response );
512
		if ( $entity ) {
513
			$json = json_decode( $entity );
514
		} else {
515
			$json = false;
516
		}
517
518 View Code Duplication
		if ( empty( $json->jetpack_secret ) || ! is_string( $json->jetpack_secret ) ) {
519
			restore_current_blog();
520
			return new Jetpack_Error( 'jetpack_secret', '', $code );
521
		}
522
523
		if ( isset( $json->jetpack_public ) ) {
524
			$jetpack_public = (int) $json->jetpack_public;
525
		} else {
526
			$jetpack_public = false;
527
		}
528
529
		Jetpack_Options::update_options( array(
530
			'id'         => (int) $json->jetpack_id,
531
			'blog_token' => (string) $json->jetpack_secret,
532
			'public'     => $jetpack_public,
533
		) );
534
535
		/*
536
		 * Update the subsiteregister method on wpcom so that it also sends back the
537
		 * token in this same request
538
		 */
539
		$is_master_user = ! Jetpack::is_active();
540
		Jetpack::update_user_token(
541
			get_current_user_id(),
542
			sprintf( '%s.%d', $json->token->secret, get_current_user_id() ),
543
			$is_master_user
544
		);
545
546
		Jetpack::activate_default_modules();
547
548
		restore_current_blog();
549
	}
550
551
	function wrap_network_admin_page() {
552
		Jetpack_Admin_Page::wrap_ui( array( $this, 'network_admin_page' ) );
553
	}
554
555
	/**
556
	 * Handles the displaying of all sites on the network that are
557
	 * dis/connected to Jetpack
558
	 *
559
	 * @since 2.9
560
	 * @see   Jetpack_Network::jetpack_sites_list()
561
	 */
562
	function network_admin_page() {
563
		global $current_site;
564
		$this->network_admin_page_header();
565
566
		$jp = Jetpack::init();
567
568
		// We should be, but ensure we are on the main blog
569
		switch_to_blog( $current_site->blog_id );
570
		$main_active = $jp->is_active();
571
		restore_current_blog();
572
573
		// If we are in dev mode, just show the notice and bail
574
		if ( Jetpack::is_development_mode() ) {
575
			Jetpack::show_development_mode_notice();
576
			return;
577
		}
578
579
		/*
580
		 * Ensure the main blog is connected as all other subsite blog
581
		 * connections will feed off this one
582
		 */
583
		if ( ! $main_active ) {
584
			$url  = $this->get_url( array(
585
				'name'    => 'subsiteregister',
586
				'site_id' => 1,
587
			) );
588
			$data = array( 'url' => $jp->build_connect_url() );
589
			Jetpack::init()->load_view( 'admin/must-connect-main-blog.php', $data );
590
591
			return;
592
		}
593
594
		require_once( 'class.jetpack-network-sites-list-table.php' );
595
		$myListTable = new Jetpack_Network_Sites_List_Table();
596
		echo '<div class="wrap"><h2>' . __( 'Sites', 'jetpack' ) . '</h2>';
597
		echo '<form method="post">';
598
		$myListTable->prepare_items();
599
		$myListTable->display();
600
		echo '</form></div>';
601
602
	}
603
604
	/**
605
	 * Stylized JP header formatting
606
	 *
607
	 * @since 2.9
608
	 */
609
	function network_admin_page_header() {
610
		global $current_user;
611
612
		$is_connected = Jetpack::is_active();
613
614
		$data = array(
615
			'is_connected' => $is_connected
616
		);
617
		Jetpack::init()->load_view( 'admin/network-admin-header.php', $data );
618
	}
619
620
621
	/**
622
	 * Fires when the Jetpack > Settings page is saved.
623
	 *
624
	 * @since 2.9
625
	 */
626
	public function save_network_settings_page() {
627
628
		if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'jetpack-network-settings' ) ) {
629
			// no nonce, push back to settings page
630
			wp_safe_redirect(
631
				add_query_arg(
632
					array( 'page' => 'jetpack-settings' ),
633
					network_admin_url( 'admin.php' )
634
				)
635
			);
636
			exit();
637
		}
638
639
		// try to save the Protect whitelist before anything else, since that action can result in errors
640
		$whitelist = str_replace( ' ', '', $_POST['global-whitelist'] );
641
		$whitelist = explode( PHP_EOL, $whitelist );
642
		$result    = jetpack_protect_save_whitelist( $whitelist, $global = true );
643
		if ( is_wp_error( $result ) ) {
644
			wp_safe_redirect(
645
				add_query_arg(
646
					array( 'page' => 'jetpack-settings', 'error' => 'jetpack_protect_whitelist' ),
647
					network_admin_url( 'admin.php' )
648
				)
649
			);
650
			exit();
651
		}
652
653
		/*
654
		 * Fields
655
		 *
656
		 * auto-connect - Checkbox for global Jetpack connection
657
		 * sub-site-connection-override - Allow sub-site admins to (dis)reconnect with their own Jetpack account
658
		 */
659
		$auto_connect = 0;
660
		if ( isset( $_POST['auto-connect'] ) ) {
661
			$auto_connect = 1;
662
		}
663
664
		$sub_site_connection_override = 0;
665
		if ( isset( $_POST['sub-site-connection-override'] ) ) {
666
			$sub_site_connection_override = 1;
667
		}
668
669
		/* Remove the toggles for 2.9, re-evaluate how they're done and added for a 3.0 release. They don't feel quite right yet.
670
		$manage_auto_activated_modules = 0;
671
		if ( isset( $_POST['manage_auto_activated_modules'] ) ) {
672
			$manage_auto_activated_modules = 1;
673
		}
674
675
		$modules = array();
676
		if ( isset( $_POST['modules'] ) ) {
677
			$modules = $_POST['modules'];
678
		}
679
		*/
680
681
		$data = array(
682
			'auto-connect'                  => $auto_connect,
683
			'sub-site-connection-override'  => $sub_site_connection_override,
684
			//'manage_auto_activated_modules' => $manage_auto_activated_modules,
685
			//'modules'                       => $modules,
686
		);
687
688
		update_site_option( $this->settings_name, $data );
689
		wp_safe_redirect(
690
			add_query_arg(
691
				array( 'page' => 'jetpack-settings', 'updated' => 'true' ),
692
				network_admin_url( 'admin.php' )
693
			)
694
		);
695
		exit();
696
	}
697
698
	public function wrap_render_network_admin_settings_page() {
699
		Jetpack_Admin_Page::wrap_ui( array( $this, 'render_network_admin_settings_page' ) );
700
	}
701
702
	public function render_network_admin_settings_page() {
703
		$this->network_admin_page_header();
704
		$options = wp_parse_args( get_site_option( $this->settings_name ), $this->setting_defaults );
705
706
		$modules = array();
707
		$module_slugs = Jetpack::get_available_modules();
708
		foreach ( $module_slugs as $slug ) {
709
			$module           = Jetpack::get_module( $slug );
710
			$module['module'] = $slug;
711
			$modules[]        = $module;
712
		}
713
714
		usort( $modules, array( 'Jetpack', 'sort_modules' ) );
715
716
		if ( ! isset( $options['modules'] ) ) {
717
			$options['modules'] = $modules;
718
		}
719
720
		$data = array(
721
			'modules' => $modules,
722
			'options' => $options,
723
			'jetpack_protect_whitelist' => jetpack_protect_format_whitelist(),
724
		);
725
726
		Jetpack::init()->load_view( 'admin/network-settings.php', $data );
727
	}
728
729
	/**
730
	 * Updates a site wide option
731
	 *
732
	 * @since 2.9
733
	 *
734
	 * @param string $key
735
	 * @param mixed  $value
736
	 *
737
	 * @return boolean
738
	 **/
739
	public function update_option( $key, $value ) {
740
		$options  = get_site_option( $this->settings_name, $this->setting_defaults );
741
		$options[ $key ] = $value;
742
743
		return update_site_option( $this->settings_name, $options );
744
	}
745
746
	/**
747
	 * Retrieves a site wide option
748
	 *
749
	 * @since 2.9
750
	 *
751
	 * @param string $name - Name of the option in the database
752
	 **/
753
	public function get_option( $name ) {
754
		$options = get_site_option( $this->settings_name, $this->setting_defaults );
755
		$options = wp_parse_args( $options, $this->setting_defaults );
756
757
		if ( ! isset( $options[ $name ] ) ) {
758
			$options[ $name ] = null;
759
		}
760
761
		return $options[ $name ];
762
	}
763
764
}
765
766
// end class
767