PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 8.2.1
Jetpack – WP Security, Backup, Speed, & Growth v8.2.1
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / class.jetpack-network.php
jetpack Last commit date
3rd-party 6 years ago _inc 6 years ago bin 6 years ago css 6 years ago extensions 6 years ago images 6 years ago json-endpoints 6 years ago languages 6 years ago modules 6 years ago sal 6 years ago src 6 years ago vendor 6 years ago views 7 years ago .svnignore 12 years ago CODE-OF-CONDUCT.md 9 years ago changelog.txt 6 years ago class.frame-nonce-preview.php 6 years ago class.jetpack-admin.php 6 years ago class.jetpack-affiliate.php 6 years ago class.jetpack-autoupdate.php 6 years ago class.jetpack-bbpress-json-api-compat.php 6 years ago class.jetpack-cli.php 6 years ago class.jetpack-client-server.php 6 years ago class.jetpack-connection-banner.php 6 years ago class.jetpack-data.php 6 years ago class.jetpack-debugger.php 7 years ago class.jetpack-error.php 10 years ago class.jetpack-gutenberg.php 6 years ago class.jetpack-heartbeat.php 6 years ago class.jetpack-idc.php 6 years ago class.jetpack-ixr-client.php 6 years ago class.jetpack-modules-list-table.php 6 years ago class.jetpack-network-sites-list-table.php 6 years ago class.jetpack-network.php 6 years ago class.jetpack-plan.php 6 years ago class.jetpack-post-images.php 6 years ago class.jetpack-twitter-cards.php 6 years ago class.jetpack-user-agent.php 6 years ago class.jetpack-xmlrpc-server.php 6 years ago class.jetpack.php 6 years ago class.json-api-endpoints.php 6 years ago class.json-api.php 6 years ago class.photon.php 6 years ago composer.json 6 years ago functions.compat.php 6 years ago functions.cookies.php 6 years ago functions.gallery.php 6 years ago functions.global.php 6 years ago functions.opengraph.php 6 years ago functions.photon.php 6 years ago jest.config.js 6 years ago jetpack.php 6 years ago json-api-config.php 10 years ago json-endpoints.php 7 years ago load-jetpack.php 6 years ago locales.php 7 years ago readme.txt 6 years ago require-lib.php 6 years ago uninstall.php 6 years ago wpml-config.xml 10 years ago
class.jetpack-network.php
736 lines
1 <?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFilename
2 /**
3 * Jetpack Network Manager class file.
4 *
5 * @package jetpack
6 */
7
8 use Automattic\Jetpack\Connection\Client;
9 use Automattic\Jetpack\Connection\Manager;
10 use Automattic\Jetpack\Connection\Utils as Connection_Utils;
11 use Automattic\Jetpack\Constants;
12 use Automattic\Jetpack\Status;
13
14 /**
15 * Used to manage Jetpack installation on Multisite Network installs
16 *
17 * SINGLETON: To use call Jetpack_Network::init()
18 *
19 * DO NOT USE ANY STATIC METHODS IN THIS CLASS!!!!!!
20 *
21 * @since 2.9
22 */
23 class Jetpack_Network {
24
25 /**
26 * Holds a static copy of Jetpack_Network for the singleton
27 *
28 * @since 2.9
29 * @var Jetpack_Network
30 */
31 private static $instance = null;
32
33 /**
34 * An instance of the connection manager object.
35 *
36 * @since 7.7
37 * @var Automattic\Jetpack\Connection\Manager
38 */
39 private $connection;
40
41 /**
42 * Name of the network wide settings
43 *
44 * @since 2.9
45 * @var string
46 */
47 private $settings_name = 'jetpack-network-settings';
48
49 /**
50 * Defaults for settings found on the Jetpack > Settings page
51 *
52 * @since 2.9
53 * @var array
54 */
55 private $setting_defaults = array(
56 'auto-connect' => 0,
57 'sub-site-connection-override' => 1,
58 );
59
60 /**
61 * Constructor
62 *
63 * @since 2.9
64 */
65 private function __construct() {
66 require_once ABSPATH . '/wp-admin/includes/plugin.php'; // For the is_plugin... check.
67 require_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php'; // For managing the global whitelist.
68
69 /**
70 * Sanity check to ensure the install is Multisite and we
71 * are in Network Admin
72 */
73 if ( is_multisite() && is_network_admin() ) {
74 add_action( 'network_admin_menu', array( $this, 'add_network_admin_menu' ) );
75 add_action( 'network_admin_edit_jetpack-network-settings', array( $this, 'save_network_settings_page' ), 10, 0 );
76 add_filter( 'admin_body_class', array( $this, 'body_class' ) );
77
78 if ( isset( $_GET['page'] ) && 'jetpack' == $_GET['page'] ) {
79 add_action( 'admin_init', array( $this, 'jetpack_sites_list' ) );
80 }
81 }
82
83 /*
84 * Things that should only run on multisite
85 */
86 if ( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
87 add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
88
89 /*
90 * If admin wants to automagically register new sites set the hook here
91 *
92 * This is a hacky way because xmlrpc is not available on wp_initialize_site
93 */
94 if ( 1 === $this->get_option( 'auto-connect' ) ) {
95 add_action( 'wp_initialize_site', array( $this, 'do_automatically_add_new_site' ) );
96 }
97 }
98 }
99
100 /**
101 * Sets a connection object.
102 *
103 * @param Automattic\Jetpack\Connection\Manager $connection the connection manager object.
104 */
105 public function set_connection( Manager $connection ) {
106 $this->connection = $connection;
107 }
108
109 /**
110 * Sets which modules get activated by default on subsite connection.
111 * Modules can be set in Network Admin > Jetpack > Settings
112 *
113 * @since 2.9
114 * @deprecated since 7.7.0
115 *
116 * @param array $modules List of modules.
117 */
118 public function set_auto_activated_modules( $modules ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
119 _deprecated_function( __METHOD__, 'jetpack-7.7' );
120 }
121
122 /**
123 * Registers new sites upon creation
124 *
125 * @since 2.9
126 * @since 7.4.0 Uses a WP_Site object.
127 * @uses wp_initialize_site
128 *
129 * @param WP_Site $site the WordPress site object.
130 **/
131 public function do_automatically_add_new_site( $site ) {
132 if ( is_a( $site, 'WP_Site' ) ) {
133 $this->do_subsiteregister( $site->id );
134 }
135 }
136
137 /**
138 * Adds .network-admin class to the body tag
139 * Helps distinguish network admin JP styles from regular site JP styles
140 *
141 * @since 2.9
142 *
143 * @param String $classes current assigned body classes.
144 * @return String amended class string.
145 */
146 public function body_class( $classes ) {
147 return trim( $classes ) . ' network-admin ';
148 }
149
150 /**
151 * Provides access to an instance of Jetpack_Network
152 *
153 * This is how the Jetpack_Network object should *always* be accessed
154 *
155 * @since 2.9
156 * @return Jetpack_Network
157 */
158 public static function init() {
159 if ( ! self::$instance || ! is_a( self::$instance, 'Jetpack_Network' ) ) {
160 self::$instance = new Jetpack_Network();
161 }
162
163 return self::$instance;
164 }
165
166 /**
167 * Registers the Multisite admin bar menu item shortcut.
168 * This shortcut helps users quickly and easily navigate to the Jetpack Network Admin
169 * menu from anywhere in their network.
170 *
171 * @since 2.9
172 */
173 public function register_menubar() {
174 add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
175 }
176
177 /**
178 * Runs when Jetpack is deactivated from the network admin plugins menu.
179 * Each individual site will need to have Jetpack::disconnect called on it.
180 * Site that had Jetpack individually enabled will not be disconnected as
181 * on Multisite individually activated plugins are still activated when
182 * a plugin is deactivated network wide.
183 *
184 * @since 2.9
185 **/
186 public function deactivate() {
187 // Only fire if in network admin.
188 if ( ! is_network_admin() ) {
189 return;
190 }
191
192 $sites = get_sites();
193
194 foreach ( $sites as $s ) {
195 switch_to_blog( $s->blog_id );
196 $active_plugins = get_option( 'active_plugins' );
197
198 /*
199 * If this plugin was activated in the subsite individually
200 * we do not want to call disconnect. Plugins activated
201 * individually (before network activation) stay activated
202 * when the network deactivation occurs
203 */
204 if ( ! in_array( 'jetpack/jetpack.php', $active_plugins, true ) ) {
205 Jetpack::disconnect();
206 }
207 }
208 restore_current_blog();
209 }
210
211 /**
212 * Adds a link to the Jetpack Network Admin page in the network admin menu bar.
213 *
214 * @since 2.9
215 **/
216 public function add_to_menubar() {
217 global $wp_admin_bar;
218 // Don't show for logged out users or single site mode.
219 if ( ! is_user_logged_in() || ! is_multisite() ) {
220 return;
221 }
222
223 $wp_admin_bar->add_node(
224 array(
225 'parent' => 'network-admin',
226 'id' => 'network-admin-jetpack',
227 'title' => 'Jetpack',
228 'href' => $this->get_url( 'network_admin_page' ),
229 )
230 );
231 }
232
233 /**
234 * Returns various URL strings. Factory like
235 *
236 * $args can be a string or an array.
237 * If $args is an array there must be an element called name for the switch statement
238 *
239 * Currently supports:
240 * - subsiteregister: Pass array( 'name' => 'subsiteregister', 'site_id' => SITE_ID )
241 * - network_admin_page: Provides link to /wp-admin/network/JETPACK
242 * - subsitedisconnect: Pass array( 'name' => 'subsitedisconnect', 'site_id' => SITE_ID )
243 *
244 * @since 2.9
245 *
246 * @param Mixed $args URL parameters.
247 *
248 * @return String
249 **/
250 public function get_url( $args ) {
251 $url = null; // Default url value.
252
253 if ( is_string( $args ) ) {
254 $name = $args;
255 } else if ( is_array( $args ) ) {
256 $name = $args['name'];
257 } else {
258 return $url;
259 }
260
261 switch ( $name ) {
262 case 'subsiteregister':
263 if ( ! isset( $args['site_id'] ) ) {
264 break; // If there is not a site id present we cannot go further.
265 }
266 $url = network_admin_url(
267 'admin.php?page=jetpack&action=subsiteregister&site_id='
268 . $args['site_id']
269 );
270 break;
271
272 case 'network_admin_page':
273 $url = network_admin_url( 'admin.php?page=jetpack' );
274 break;
275
276 case 'subsitedisconnect':
277 if ( ! isset( $args['site_id'] ) ) {
278 break; // If there is not a site id present we cannot go further.
279 }
280 $url = network_admin_url(
281 'admin.php?page=jetpack&action=subsitedisconnect&site_id='
282 . $args['site_id']
283 );
284 break;
285 }
286
287 return $url;
288 }
289
290 /**
291 * Adds the Jetpack menu item to the Network Admin area
292 *
293 * @since 2.9
294 */
295 public function add_network_admin_menu() {
296 add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_network_admin_page', 'jetpack', array( $this, 'wrap_network_admin_page' ), 'div', 3 );
297 $jetpack_sites_page_hook = add_submenu_page( 'jetpack', __( 'Jetpack Sites', 'jetpack' ), __( 'Sites', 'jetpack' ), 'jetpack_network_sites_page', 'jetpack', array( $this, 'wrap_network_admin_page' ) );
298 $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' ) );
299 add_action( "admin_print_styles-$jetpack_sites_page_hook", array( 'Jetpack_Admin_Page', 'load_wrapper_styles' ) );
300 add_action( "admin_print_styles-$jetpack_settings_page_hook", array( 'Jetpack_Admin_Page', 'load_wrapper_styles' ) );
301 /**
302 * As jetpack_register_genericons is by default fired off a hook,
303 * the hook may have already fired by this point.
304 * So, let's just trigger it manually.
305 */
306 require_once JETPACK__PLUGIN_DIR . '_inc/genericons.php';
307 jetpack_register_genericons();
308
309 if ( ! wp_style_is( 'jetpack-icons', 'registered' ) ) {
310 wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
311 }
312
313 add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
314 }
315
316 /**
317 * Adds JP menu icon
318 *
319 * @since 2.9
320 **/
321 public function admin_menu_css() {
322 wp_enqueue_style( 'jetpack-icons' );
323 }
324
325 /**
326 * Provides functionality for the Jetpack > Sites page.
327 * Does not do the display!
328 *
329 * @since 2.9
330 */
331 public function jetpack_sites_list() {
332 Jetpack::init();
333
334 if ( isset( $_GET['action'] ) ) {
335 switch ( $_GET['action'] ) {
336 case 'subsiteregister':
337 /**
338 * Add actual referrer checking.
339 *
340 * @todo check_admin_referer( 'jetpack-subsite-register' );
341 */
342 Jetpack::log( 'subsiteregister' );
343
344 // If !$_GET['site_id'] stop registration and error.
345 if ( ! isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
346 /**
347 * Log error to state cookie for display later.
348 *
349 * @todo Make state messages show on Jetpack NA pages
350 */
351 Jetpack::state( 'missing_site_id', esc_html__( 'Site ID must be provided to register a sub-site.', 'jetpack' ) );
352 break;
353 }
354
355 // Send data to register endpoint and retrieve shadow blog details.
356 $result = $this->do_subsiteregister();
357 $url = $this->get_url( 'network_admin_page' );
358
359 if ( is_wp_error( $result ) ) {
360 $url = add_query_arg( 'action', 'connection_failed', $url );
361 } else {
362 $url = add_query_arg( 'action', 'connected', $url );
363 }
364
365 wp_safe_redirect( $url );
366 exit;
367
368 case 'subsitedisconnect':
369 Jetpack::log( 'subsitedisconnect' );
370
371 if ( ! isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
372 Jetpack::state( 'missing_site_id', esc_html__( 'Site ID must be provided to disconnect a sub-site.', 'jetpack' ) );
373 break;
374 }
375
376 $this->do_subsitedisconnect();
377 break;
378
379 case 'connected':
380 case 'connection_failed':
381 add_action( 'jetpack_notices', array( $this, 'show_jetpack_notice' ) );
382 break;
383 }
384 }
385 }
386
387 /**
388 * Shows the Jetpack plugin notices.
389 */
390 public function show_jetpack_notice() {
391 if ( isset( $_GET['action'] ) && 'connected' == $_GET['action'] ) {
392 $notice = __( 'Site successfully connected.', 'jetpack' );
393 $classname = 'updated';
394 } elseif ( isset( $_GET['action'] ) && 'connection_failed' == $_GET['action'] ) {
395 $notice = __( 'Site connection failed!', 'jetpack' );
396 $classname = 'error';
397 }
398 ?>
399 <div id="message" class="<?php echo esc_attr( $classname ); ?> jetpack-message jp-connect" style="display:block !important;">
400 <p><?php echo esc_html( $notice ); ?></p>
401 </div>
402 <?php
403 }
404
405 /**
406 * Disconnect functionality for an individual site
407 *
408 * @since 2.9
409 * @see Jetpack_Network::jetpack_sites_list()
410 *
411 * @param int $site_id the site identifier.
412 */
413 public function do_subsitedisconnect( $site_id = null ) {
414 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
415 return;
416 }
417 $site_id = ( is_null( $site_id ) ) ? $_GET['site_id'] : $site_id;
418 switch_to_blog( $site_id );
419 Jetpack::disconnect();
420 restore_current_blog();
421 }
422
423 /**
424 * Registers a subsite with the Jetpack servers
425 *
426 * @since 2.9
427 * @todo Break apart into easier to manage chunks that can be unit tested
428 * @see Jetpack_Network::jetpack_sites_list();
429 *
430 * @param int $site_id the site identifier.
431 */
432 public function do_subsiteregister( $site_id = null ) {
433 if ( ! current_user_can( 'jetpack_disconnect' ) ) {
434 return;
435 }
436
437 if ( ( new Status() )->is_development_mode() ) {
438 return;
439 }
440
441 // Figure out what site we are working on.
442 $site_id = ( is_null( $site_id ) ) ? $_GET['site_id'] : $site_id;
443
444 /*
445 * Here we need to switch to the subsite
446 * For the registration process we really only hijack how it
447 * works for an individual site and pass in some extra data here
448 */
449 switch_to_blog( $site_id );
450
451 add_filter( 'jetpack_register_request_body', array( $this, 'filter_register_request_body' ) );
452 add_action( 'jetpack_site_registered_user_token', array( $this, 'filter_register_user_token' ) );
453
454 // Save the secrets in the subsite so when the wpcom server does a pingback it
455 // will be able to validate the connection.
456 $result = $this->connection->register( 'subsiteregister' );
457
458 if ( is_wp_error( $result ) || ! $result ) {
459 restore_current_blog();
460 return $result;
461 }
462
463 Jetpack::activate_default_modules( false, false, array(), false );
464
465 restore_current_blog();
466 }
467
468 /**
469 * Receives the registration response token.
470 *
471 * @param Object $token the received token.
472 */
473 public function filter_register_user_token( $token ) {
474 $is_master_user = ! Jetpack::is_active();
475 Connection_Utils::update_user_token(
476 get_current_user_id(),
477 sprintf( '%s.%d', $token->secret, get_current_user_id() ),
478 $is_master_user
479 );
480 }
481
482 /**
483 * Filters the registration request body to include additional properties.
484 *
485 * @param Array $properties standard register request body properties.
486 * @return Array amended properties.
487 */
488 public function filter_register_request_body( $properties ) {
489 $blog_details = get_blog_details();
490
491 $network = get_network();
492
493 switch_to_blog( $network->blog_id );
494 // The blog id on WordPress.com of the primary network site.
495 $network_wpcom_blog_id = Jetpack_Options::get_option( 'id' );
496 restore_current_blog();
497
498 /**
499 * Both `state` and `user_id` need to be sent in the request, even though they are the same value.
500 * Connecting via the network admin combines `register()` and `authorize()` methods into one step,
501 * because we assume the main site is already authorized. `state` is used to verify the `register()`
502 * request, while `user_id()` is used to create the token in the `authorize()` request.
503 */
504 return array_merge(
505 $properties,
506 array(
507 'network_url' => $this->get_url( 'network_admin_page' ),
508 'network_wpcom_blog_id' => $network_wpcom_blog_id,
509 'user_id' => get_current_user_id(),
510
511 /*
512 * Use the subsite's registration date as the site creation date.
513 *
514 * This is in contrast to regular standalone sites, where we use the helper
515 * `Jetpack::get_assumed_site_creation_date()` to assume the site's creation date.
516 */
517 'site_created' => $blog_details->registered,
518 )
519 );
520 }
521
522 /**
523 * A hook handler for adding admin pages and subpages.
524 */
525 public function wrap_network_admin_page() {
526 Jetpack_Admin_Page::wrap_ui( array( $this, 'network_admin_page' ) );
527 }
528
529 /**
530 * Handles the displaying of all sites on the network that are
531 * dis/connected to Jetpack
532 *
533 * @since 2.9
534 * @see Jetpack_Network::jetpack_sites_list()
535 */
536 public function network_admin_page() {
537 global $current_site;
538 $this->network_admin_page_header();
539
540 $jp = Jetpack::init();
541
542 // We should be, but ensure we are on the main blog.
543 switch_to_blog( $current_site->blog_id );
544 $main_active = $jp->is_active();
545 restore_current_blog();
546
547 // If we are in dev mode, just show the notice and bail.
548 if ( ( new Status() )->is_development_mode() ) {
549 Jetpack::show_development_mode_notice();
550 return;
551 }
552
553 /*
554 * Ensure the main blog is connected as all other subsite blog
555 * connections will feed off this one
556 */
557 if ( ! $main_active ) {
558 $url = $this->get_url(
559 array(
560 'name' => 'subsiteregister',
561 'site_id' => 1,
562 )
563 );
564 $data = array( 'url' => $jp->build_connect_url() );
565 Jetpack::init()->load_view( 'admin/must-connect-main-blog.php', $data );
566
567 return;
568 }
569
570 require_once 'class.jetpack-network-sites-list-table.php';
571
572 $network_sites_table = new Jetpack_Network_Sites_List_Table();
573 echo '<div class="wrap"><h2>' . esc_html__( 'Sites', 'jetpack' ) . '</h2>';
574 echo '<form method="post">';
575 $network_sites_table->prepare_items();
576 $network_sites_table->display();
577 echo '</form></div>';
578
579 }
580
581 /**
582 * Stylized JP header formatting
583 *
584 * @since 2.9
585 */
586 public function network_admin_page_header() {
587 $is_connected = Jetpack::is_active();
588
589 $data = array(
590 'is_connected' => $is_connected,
591 );
592 Jetpack::init()->load_view( 'admin/network-admin-header.php', $data );
593 }
594
595 /**
596 * Fires when the Jetpack > Settings page is saved.
597 *
598 * @since 2.9
599 */
600 public function save_network_settings_page() {
601
602 if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'jetpack-network-settings' ) ) {
603 // No nonce, push back to settings page.
604 wp_safe_redirect(
605 add_query_arg(
606 array( 'page' => 'jetpack-settings' ),
607 network_admin_url( 'admin.php' )
608 )
609 );
610 exit();
611 }
612
613 // Try to save the Protect whitelist before anything else, since that action can result in errors.
614 $whitelist = str_replace( ' ', '', $_POST['global-whitelist'] );
615 $whitelist = explode( PHP_EOL, $whitelist );
616 $result = jetpack_protect_save_whitelist( $whitelist, true );
617 if ( is_wp_error( $result ) ) {
618 wp_safe_redirect(
619 add_query_arg(
620 array(
621 'page' => 'jetpack-settings',
622 'error' => 'jetpack_protect_whitelist',
623 ),
624 network_admin_url( 'admin.php' )
625 )
626 );
627 exit();
628 }
629
630 /*
631 * Fields
632 *
633 * auto-connect - Checkbox for global Jetpack connection
634 * sub-site-connection-override - Allow sub-site admins to (dis)reconnect with their own Jetpack account
635 */
636 $auto_connect = 0;
637 if ( isset( $_POST['auto-connect'] ) ) {
638 $auto_connect = 1;
639 }
640
641 $sub_site_connection_override = 0;
642 if ( isset( $_POST['sub-site-connection-override'] ) ) {
643 $sub_site_connection_override = 1;
644 }
645
646 $data = array(
647 'auto-connect' => $auto_connect,
648 'sub-site-connection-override' => $sub_site_connection_override,
649 );
650
651 update_site_option( $this->settings_name, $data );
652 wp_safe_redirect(
653 add_query_arg(
654 array(
655 'page' => 'jetpack-settings',
656 'updated' => 'true',
657 ),
658 network_admin_url( 'admin.php' )
659 )
660 );
661 exit();
662 }
663
664 /**
665 * A hook handler for adding admin pages and subpages.
666 */
667 public function wrap_render_network_admin_settings_page() {
668 Jetpack_Admin_Page::wrap_ui( array( $this, 'render_network_admin_settings_page' ) );
669 }
670
671 /**
672 * A hook rendering the admin settings page.
673 */
674 public function render_network_admin_settings_page() {
675 $this->network_admin_page_header();
676 $options = wp_parse_args( get_site_option( $this->settings_name ), $this->setting_defaults );
677
678 $modules = array();
679 $module_slugs = Jetpack::get_available_modules();
680 foreach ( $module_slugs as $slug ) {
681 $module = Jetpack::get_module( $slug );
682 $module['module'] = $slug;
683 $modules[] = $module;
684 }
685
686 usort( $modules, array( 'Jetpack', 'sort_modules' ) );
687
688 if ( ! isset( $options['modules'] ) ) {
689 $options['modules'] = $modules;
690 }
691
692 $data = array(
693 'modules' => $modules,
694 'options' => $options,
695 'jetpack_protect_whitelist' => jetpack_protect_format_whitelist(),
696 );
697
698 Jetpack::init()->load_view( 'admin/network-settings.php', $data );
699 }
700
701 /**
702 * Updates a site wide option
703 *
704 * @since 2.9
705 *
706 * @param string $key option name.
707 * @param mixed $value option value.
708 *
709 * @return boolean
710 **/
711 public function update_option( $key, $value ) {
712 $options = get_site_option( $this->settings_name, $this->setting_defaults );
713 $options[ $key ] = $value;
714
715 return update_site_option( $this->settings_name, $options );
716 }
717
718 /**
719 * Retrieves a site wide option
720 *
721 * @since 2.9
722 *
723 * @param string $name - Name of the option in the database.
724 **/
725 public function get_option( $name ) {
726 $options = get_site_option( $this->settings_name, $this->setting_defaults );
727 $options = wp_parse_args( $options, $this->setting_defaults );
728
729 if ( ! isset( $options[ $name ] ) ) {
730 $options[ $name ] = null;
731 }
732
733 return $options[ $name ];
734 }
735 }
736