PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 7.4.3
Jetpack – WP Security, Backup, Speed, & Growth v7.4.3
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / class.jetpack-network.php
class.jetpack-network.php
780 lines 22.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 wp_initialize_site
74 */
75 if ( $this->get_option( 'auto-connect' ) == 1 ) {
76 add_action( 'wp_initialize_site', 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 * @since 7.4.0 Uses a WP_Site object.
111 * @uses wp_initialize_site
112 *
113 * @param WP_Site $site
114 **/
115 public function do_automatically_add_new_site( $site ) {
116 if ( is_a( $site, 'WP_Site') ) {
117 $this->do_subsiteregister( $site->id );
118 }
119 }
120
121 /**
122 * Adds .network-admin class to the body tag
123 * Helps distinguish network admin JP styles from regular site JP styles
124 *
125 * @since 2.9
126 */
127 public function body_class( $classes ) {
128 return trim( $classes ) . ' network-admin ';
129 }
130
131 /**
132 * Provides access to an instance of Jetpack_Network
133 *
134 * This is how the Jetpack_Network object should *always* be accessed
135 *
136 * @since 2.9
137 * @return Jetpack_Network
138 */
139 public static function init() {
140 if ( ! self::$instance || ! is_a( self::$instance, 'Jetpack_Network' ) ) {
141 self::$instance = new Jetpack_Network;
142 }
143
144 return self::$instance;
145 }
146
147 /**
148 * Registers the Multisite admin bar menu item shortcut.
149 * This shortcut helps users quickly and easily navigate to the Jetpack Network Admin
150 * menu from anywhere in their network.
151 *
152 * @since 2.9
153 */
154 public function register_menubar() {
155 add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
156 }
157
158 /**
159 * Runs when Jetpack is deactivated from the network admin plugins menu.
160 * Each individual site will need to have Jetpack::disconnect called on it.
161 * Site that had Jetpack individually enabled will not be disconnected as
162 * on Multisite individually activated plugins are still activated when
163 * a plugin is deactivated network wide.
164 *
165 * @since 2.9
166 **/
167 public function deactivate() {
168 // Only fire if in network admin
169 if ( ! is_network_admin() ) {
170 return;
171 }
172
173 $sites = get_sites();
174
175 foreach ( $sites as $s ) {
176 switch_to_blog( $s->blog_id );
177 $active_plugins = get_option( 'active_plugins' );
178
179 /*
180 * If this plugin was activated in the subsite individually
181 * we do not want to call disconnect. Plugins activated
182 * individually (before network activation) stay activated
183 * when the network deactivation occurs
184 */
185 if ( ! in_array( 'jetpack/jetpack.php', $active_plugins ) ) {
186 Jetpack::disconnect();
187 }
188 }
189 restore_current_blog();
190 }
191
192 /**
193 * Adds a link to the Jetpack Network Admin page in the network admin menu bar.
194 *
195 * @since 2.9
196 **/
197 public function add_to_menubar() {
198 global $wp_admin_bar;
199 // Don't show for logged out users or single site mode.
200 if ( ! is_user_logged_in() || ! is_multisite() ) {
201 return;
202 }
203
204 $wp_admin_bar->add_node( array(
205 'parent' => 'network-admin',
206 'id' => 'network-admin-jetpack',
207 'title' => 'Jetpack',
208 'href' => $this->get_url( 'network_admin_page' ),
209 ) );
210 }
211
212 /**
213 * Returns various URL strings. Factory like
214 *
215 * $args can be a string or an array.
216 * If $args is an array there must be an element called name for the switch statement
217 *
218 * Currently supports:
219 * - subsiteregister: Pass array( 'name' => 'subsiteregister', 'site_id' => SITE_ID )
220 * - network_admin_page: Provides link to /wp-admin/network/JETPACK
221 * - subsitedisconnect: Pass array( 'name' => 'subsitedisconnect', 'site_id' => SITE_ID )
222 *
223 * @since 2.9
224 *
225 * @param Mixed $args
226 *
227 * @return String
228 **/
229 public function get_url( $args ) {
230 $url = null; // Default url value
231
232 if ( is_string( $args ) ) {
233 $name = $args;
234 } else {
235 $name = $args['name'];
236 }
237
238 switch ( $name ) {
239 case 'subsiteregister':
240 if ( ! isset( $args['site_id'] ) ) {
241 break; // If there is not a site id present we cannot go further
242 }
243 $url = network_admin_url(
244 'admin.php?page=jetpack&action=subsiteregister&site_id='
245 . $args['site_id']
246 );
247 break;
248
249 case 'network_admin_page':
250 $url = network_admin_url( 'admin.php?page=jetpack' );
251 break;
252
253 case 'subsitedisconnect':
254 if ( ! isset( $args['site_id'] ) ) {
255 break; // If there is not a site id present we cannot go further
256 }
257 $url = network_admin_url(
258 'admin.php?page=jetpack&action=subsitedisconnect&site_id='
259 . $args['site_id']
260 );
261 break;
262 }
263
264 return $url;
265 }
266
267 /**
268 * Adds the Jetpack menu item to the Network Admin area
269 *
270 * @since 2.9
271 */
272 public function add_network_admin_menu() {
273 add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_network_admin_page', 'jetpack', array( $this, 'wrap_network_admin_page' ), 'div', 3 );
274 $jetpack_sites_page_hook = add_submenu_page( 'jetpack', __( 'Jetpack Sites', 'jetpack' ), __( 'Sites', 'jetpack' ), 'jetpack_network_sites_page', 'jetpack', array( $this, 'wrap_network_admin_page' ) );
275 $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' ) );
276 add_action( "admin_print_styles-$jetpack_sites_page_hook", array( 'Jetpack_Admin_Page', 'load_wrapper_styles' ) );
277 add_action( "admin_print_styles-$jetpack_settings_page_hook", array( 'Jetpack_Admin_Page', 'load_wrapper_styles' ) );
278 /**
279 * As jetpack_register_genericons is by default fired off a hook,
280 * the hook may have already fired by this point.
281 * So, let's just trigger it manually.
282 */
283 require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
284 jetpack_register_genericons();
285
286 if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
287 wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
288 }
289
290 add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
291 }
292
293 /**
294 * Adds JP menu icon
295 *
296 * @since 2.9
297 **/
298 function admin_menu_css() {
299 wp_enqueue_style( 'jetpack-icons' );
300 }
301
302 /**
303 * Provides functionality for the Jetpack > Sites page.
304 * Does not do the display!
305 *
306 * @since 2.9
307 */
308 public function jetpack_sites_list() {
309 Jetpack::init();
310
311 if ( isset( $_GET['action'] ) ) {
312 switch ( $_GET['action'] ) {
313 case 'subsiteregister':
314 /*
315 * @todo check_admin_referer( 'jetpack-subsite-register' );
316 */
317 Jetpack::log( 'subsiteregister' );
318
319 // If !$_GET['site_id'] stop registration and error
320 if ( ! isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
321 // Log error to state cookie for display later
322 /**
323 * @todo Make state messages show on Jetpack NA pages
324 **/
325 Jetpack::state( 'missing_site_id', esc_html__( 'Site ID must be provided to register a sub-site.', 'jetpack' ) );
326 break;
327 }
328
329 // Send data to register endpoint and retrieve shadow blog details
330 $result = $this->do_subsiteregister();
331 $url = $this->get_url( 'network_admin_page' );
332
333 if ( is_wp_error( $result ) ) {
334 $url = add_query_arg( 'action', 'connection_failed', $url );
335 } else {
336 $url = add_query_arg( 'action', 'connected', $url );
337 }
338
339 wp_safe_redirect( $url );
340 exit;
341
342 case 'subsitedisconnect':
343 Jetpack::log( 'subsitedisconnect' );
344
345 if ( ! isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
346 Jetpack::state( 'missing_site_id', esc_html__( 'Site ID must be provided to disconnect a sub-site.', 'jetpack' ) );
347 break;
348 }
349
350 $this->do_subsitedisconnect();
351 break;
352
353 case 'connected':
354 case 'connection_failed':
355 add_action( 'jetpack_notices', array( $this, 'show_jetpack_notice' ) );
356 break;
357 }
358 }
359 }
360
361 public function show_jetpack_notice() {
362 if ( isset( $_GET['action'] ) && 'connected' == $_GET['action'] ) {
363 $notice = __( 'Site successfully connected.', 'jetpack' );
364 $classname = 'updated';
365 } else if ( isset( $_GET['action'] ) && 'connection_failed' == $_GET['action'] ) {
366 $notice = __( 'Site connection failed!', 'jetpack' );
367 $classname = 'error';
368 }
369 ?>
370 <div id="message" class="<?php echo esc_attr( $classname );?> jetpack-message jp-connect" style="display:block !important;">
371 <p><?php echo esc_html( $notice ); ?></p>
372 </div>
373 <?php
374 }
375
376 /**
377 * Disconnect functionality for an individual site
378 *
379 * @since 2.9
380 * @see Jetpack_Network::jetpack_sites_list()
381 */
382 public function do_subsitedisconnect( $site_id = null ) {
383 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
384 return;
385 }
386 $site_id = ( is_null( $site_id ) ) ? $_GET['site_id'] : $site_id;
387 switch_to_blog( $site_id );
388 Jetpack::disconnect();
389 restore_current_blog();
390 }
391
392 /**
393 * Registers a subsite with the Jetpack servers
394 *
395 * @since 2.9
396 * @todo Break apart into easier to manage chunks that can be unit tested
397 * @see Jetpack_Network::jetpack_sites_list();
398 */
399 public function do_subsiteregister( $site_id = null ) {
400 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
401 return;
402 }
403
404 if ( Jetpack::is_development_mode() ) {
405 return;
406 }
407
408 $jp = Jetpack::init();
409
410 // Figure out what site we are working on
411 $site_id = ( is_null( $site_id ) ) ? $_GET['site_id'] : $site_id;
412
413 // better to try (and fail) to set a higher timeout than this system
414 // supports than to have register fail for more users than it should
415 $timeout = Jetpack::set_min_time_limit( 60 ) / 2;
416
417 // The blog id on WordPress.com of the primary network site
418 $network_wpcom_blog_id = Jetpack_Options::get_option( 'id' );
419
420 /*
421 * Here we need to switch to the subsite
422 * For the registration process we really only hijack how it
423 * works for an individual site and pass in some extra data here
424 */
425 switch_to_blog( $site_id );
426
427 // Save the secrets in the subsite so when the wpcom server does a pingback it
428 // will be able to validate the connection
429 $secrets = $jp->generate_secrets( 'register' );
430 if (
431 empty( $secrets['secret_1'] ) ||
432 empty( $secrets['secret_2'] ) ||
433 empty( $secrets['exp'] )
434 ) {
435 return new Jetpack_Error( 'missing_secrets' );
436 }
437
438 // Gra info for gmt offset
439 $gmt_offset = get_option( 'gmt_offset' );
440 if ( ! $gmt_offset ) {
441 $gmt_offset = 0;
442 }
443
444 /*
445 * Get the stats_option option from the db.
446 * It looks like the server strips this out so maybe it is not necessary?
447 * Does it match the Jetpack site with the old stats plugin id?
448 *
449 * @todo Find out if sending the stats_id is necessary
450 */
451 $stat_options = get_option( 'stats_options' );
452 $stat_id = $stat_options = isset( $stats_options['blog_id'] ) ? $stats_options['blog_id'] : null;
453 $user_id = get_current_user_id();
454
455 $tracks_identity = jetpack_tracks_get_identity( $user_id );
456
457 /*
458 * Use the subsite's registration date as the site creation date.
459 *
460 * This is in contrast to regular standalone sites, where we use the helper
461 * `Jetpack::get_assumed_site_creation_date()` to assume the site's creation date.
462 */
463 $blog_details = get_blog_details();
464 $site_creation_date = $blog_details->registered;
465
466 /**
467 * Both `state` and `user_id` need to be sent in the request, even though they are the same value.
468 * Connecting via the network admin combines `register()` and `authorize()` methods into one step,
469 * because we assume the main site is already authorized. `state` is used to verify the `register()`
470 * request, while `user_id()` is used to create the token in the `authorize()` request.
471 */
472 $args = array(
473 'method' => 'POST',
474 'body' => array(
475 'network_url' => $this->get_url( 'network_admin_page' ),
476 'network_wpcom_blog_id' => $network_wpcom_blog_id,
477 'siteurl' => site_url(),
478 'home' => home_url(),
479 'gmt_offset' => $gmt_offset,
480 'timezone_string' => (string) get_option( 'timezone_string' ),
481 'site_name' => (string) get_option( 'blogname' ),
482 'secret_1' => $secrets['secret_1'],
483 'secret_2' => $secrets['secret_2'],
484 'site_lang' => get_locale(),
485 'timeout' => $timeout,
486 'stats_id' => $stat_id, // Is this still required?
487 'user_id' => $user_id,
488 'state' => $user_id,
489 '_ui' => $tracks_identity['_ui'],
490 '_ut' => $tracks_identity['_ut'],
491 'site_created' => $site_creation_date,
492 'jetpack_version' => JETPACK__VERSION
493 ),
494 'headers' => array(
495 'Accept' => 'application/json',
496 ),
497 'timeout' => $timeout,
498 );
499
500 Jetpack::apply_activation_source_to_args( $args['body'] );
501
502 // Attempt to retrieve shadow blog details
503 $response = Jetpack_Client::_wp_remote_request(
504 Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'subsiteregister' ) ), $args, true
505 );
506
507 /*
508 * $response should either be invalid or contain:
509 * - jetpack_id => id
510 * - jetpack_secret => blog_token
511 * - jetpack_public
512 *
513 * Store the wpcom site details
514 */
515 $valid_response = $jp->validate_remote_register_response( $response );
516
517 if ( is_wp_error( $valid_response ) || ! $valid_response ) {
518 restore_current_blog();
519 return $valid_response;
520 }
521
522 // Grab the response values to work with
523 $code = wp_remote_retrieve_response_code( $response );
524 $entity = wp_remote_retrieve_body( $response );
525 if ( $entity ) {
526 $json = json_decode( $entity );
527 } else {
528 $json = false;
529 }
530
531 if ( empty( $json->jetpack_secret ) || ! is_string( $json->jetpack_secret ) ) {
532 restore_current_blog();
533 return new Jetpack_Error( 'jetpack_secret', '', $code );
534 }
535
536 if ( isset( $json->jetpack_public ) ) {
537 $jetpack_public = (int) $json->jetpack_public;
538 } else {
539 $jetpack_public = false;
540 }
541
542 Jetpack_Options::update_options( array(
543 'id' => (int) $json->jetpack_id,
544 'blog_token' => (string) $json->jetpack_secret,
545 'public' => $jetpack_public,
546 ) );
547
548 /*
549 * Update the subsiteregister method on wpcom so that it also sends back the
550 * token in this same request
551 */
552 $is_master_user = ! Jetpack::is_active();
553 Jetpack::update_user_token(
554 get_current_user_id(),
555 sprintf( '%s.%d', $json->token->secret, get_current_user_id() ),
556 $is_master_user
557 );
558
559 Jetpack::activate_default_modules();
560
561 restore_current_blog();
562 }
563
564 function wrap_network_admin_page() {
565 Jetpack_Admin_Page::wrap_ui( array( $this, 'network_admin_page' ) );
566 }
567
568 /**
569 * Handles the displaying of all sites on the network that are
570 * dis/connected to Jetpack
571 *
572 * @since 2.9
573 * @see Jetpack_Network::jetpack_sites_list()
574 */
575 function network_admin_page() {
576 global $current_site;
577 $this->network_admin_page_header();
578
579 $jp = Jetpack::init();
580
581 // We should be, but ensure we are on the main blog
582 switch_to_blog( $current_site->blog_id );
583 $main_active = $jp->is_active();
584 restore_current_blog();
585
586 // If we are in dev mode, just show the notice and bail
587 if ( Jetpack::is_development_mode() ) {
588 Jetpack::show_development_mode_notice();
589 return;
590 }
591
592 /*
593 * Ensure the main blog is connected as all other subsite blog
594 * connections will feed off this one
595 */
596 if ( ! $main_active ) {
597 $url = $this->get_url( array(
598 'name' => 'subsiteregister',
599 'site_id' => 1,
600 ) );
601 $data = array( 'url' => $jp->build_connect_url() );
602 Jetpack::init()->load_view( 'admin/must-connect-main-blog.php', $data );
603
604 return;
605 }
606
607 require_once( 'class.jetpack-network-sites-list-table.php' );
608 $myListTable = new Jetpack_Network_Sites_List_Table();
609 echo '<div class="wrap"><h2>' . __( 'Sites', 'jetpack' ) . '</h2>';
610 echo '<form method="post">';
611 $myListTable->prepare_items();
612 $myListTable->display();
613 echo '</form></div>';
614
615 }
616
617 /**
618 * Stylized JP header formatting
619 *
620 * @since 2.9
621 */
622 function network_admin_page_header() {
623 global $current_user;
624
625 $is_connected = Jetpack::is_active();
626
627 $data = array(
628 'is_connected' => $is_connected
629 );
630 Jetpack::init()->load_view( 'admin/network-admin-header.php', $data );
631 }
632
633
634 /**
635 * Fires when the Jetpack > Settings page is saved.
636 *
637 * @since 2.9
638 */
639 public function save_network_settings_page() {
640
641 if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'jetpack-network-settings' ) ) {
642 // no nonce, push back to settings page
643 wp_safe_redirect(
644 add_query_arg(
645 array( 'page' => 'jetpack-settings' ),
646 network_admin_url( 'admin.php' )
647 )
648 );
649 exit();
650 }
651
652 // try to save the Protect whitelist before anything else, since that action can result in errors
653 $whitelist = str_replace( ' ', '', $_POST['global-whitelist'] );
654 $whitelist = explode( PHP_EOL, $whitelist );
655 $result = jetpack_protect_save_whitelist( $whitelist, $global = true );
656 if ( is_wp_error( $result ) ) {
657 wp_safe_redirect(
658 add_query_arg(
659 array( 'page' => 'jetpack-settings', 'error' => 'jetpack_protect_whitelist' ),
660 network_admin_url( 'admin.php' )
661 )
662 );
663 exit();
664 }
665
666 /*
667 * Fields
668 *
669 * auto-connect - Checkbox for global Jetpack connection
670 * sub-site-connection-override - Allow sub-site admins to (dis)reconnect with their own Jetpack account
671 */
672 $auto_connect = 0;
673 if ( isset( $_POST['auto-connect'] ) ) {
674 $auto_connect = 1;
675 }
676
677 $sub_site_connection_override = 0;
678 if ( isset( $_POST['sub-site-connection-override'] ) ) {
679 $sub_site_connection_override = 1;
680 }
681
682 /* 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.
683 $manage_auto_activated_modules = 0;
684 if ( isset( $_POST['manage_auto_activated_modules'] ) ) {
685 $manage_auto_activated_modules = 1;
686 }
687
688 $modules = array();
689 if ( isset( $_POST['modules'] ) ) {
690 $modules = $_POST['modules'];
691 }
692 */
693
694 $data = array(
695 'auto-connect' => $auto_connect,
696 'sub-site-connection-override' => $sub_site_connection_override,
697 //'manage_auto_activated_modules' => $manage_auto_activated_modules,
698 //'modules' => $modules,
699 );
700
701 update_site_option( $this->settings_name, $data );
702 wp_safe_redirect(
703 add_query_arg(
704 array( 'page' => 'jetpack-settings', 'updated' => 'true' ),
705 network_admin_url( 'admin.php' )
706 )
707 );
708 exit();
709 }
710
711 public function wrap_render_network_admin_settings_page() {
712 Jetpack_Admin_Page::wrap_ui( array( $this, 'render_network_admin_settings_page' ) );
713 }
714
715 public function render_network_admin_settings_page() {
716 $this->network_admin_page_header();
717 $options = wp_parse_args( get_site_option( $this->settings_name ), $this->setting_defaults );
718
719 $modules = array();
720 $module_slugs = Jetpack::get_available_modules();
721 foreach ( $module_slugs as $slug ) {
722 $module = Jetpack::get_module( $slug );
723 $module['module'] = $slug;
724 $modules[] = $module;
725 }
726
727 usort( $modules, array( 'Jetpack', 'sort_modules' ) );
728
729 if ( ! isset( $options['modules'] ) ) {
730 $options['modules'] = $modules;
731 }
732
733 $data = array(
734 'modules' => $modules,
735 'options' => $options,
736 'jetpack_protect_whitelist' => jetpack_protect_format_whitelist(),
737 );
738
739 Jetpack::init()->load_view( 'admin/network-settings.php', $data );
740 }
741
742 /**
743 * Updates a site wide option
744 *
745 * @since 2.9
746 *
747 * @param string $key
748 * @param mixed $value
749 *
750 * @return boolean
751 **/
752 public function update_option( $key, $value ) {
753 $options = get_site_option( $this->settings_name, $this->setting_defaults );
754 $options[ $key ] = $value;
755
756 return update_site_option( $this->settings_name, $options );
757 }
758
759 /**
760 * Retrieves a site wide option
761 *
762 * @since 2.9
763 *
764 * @param string $name - Name of the option in the database
765 **/
766 public function get_option( $name ) {
767 $options = get_site_option( $this->settings_name, $this->setting_defaults );
768 $options = wp_parse_args( $options, $this->setting_defaults );
769
770 if ( ! isset( $options[ $name ] ) ) {
771 $options[ $name ] = null;
772 }
773
774 return $options[ $name ];
775 }
776
777 }
778
779 // end class
780