| @@ -26,13 +26,13 @@ | ||
| 26 | 26 | */ |
| 27 | 27 | public $network_settings_page_slug = 'wp_stream_network_settings'; |
| 28 | 28 | |
| 29 | 29 | /** |
| 30 | - * The option name for the network settings. | |
| 30 | + * Default setting page slug | |
| 31 | 31 | * |
| 32 | 32 | * @var string |
| 33 | 33 | */ |
| 34 | - public $network_settings_option = 'wp_stream_network'; | |
| 34 | + public $default_settings_page_slug = 'wp_stream_default_settings'; | |
| 35 | 35 | |
| 36 | 36 | /** |
| 37 | 37 | * Class constructor |
| 38 | 38 | * |
| @@ -83,21 +83,14 @@ | ||
| 83 | 83 | * |
| 84 | 84 | * @see https://core.trac.wordpress.org/ticket/22589 |
| 85 | 85 | */ |
| 86 | 86 | public function ajax_network_admin() { |
| 87 | - $http_referer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : ''; | |
| 88 | - | |
| 89 | - // Prefer filterable `wp_doing_ajax()` (WP 4.7+); the plugin supports 4.6. | |
| 90 | - $doing_ajax = function_exists( 'wp_doing_ajax' ) | |
| 91 | - ? wp_doing_ajax() | |
| 92 | - : ( defined( 'DOING_AJAX' ) && DOING_AJAX ); | |
| 93 | - | |
| 94 | 87 | if ( |
| 95 | - $doing_ajax | |
| 88 | + defined( 'DOING_AJAX' ) | |
| 96 | 89 | && |
| 97 | - 0 === stripos( $http_referer, network_admin_url() ) | |
| 90 | + DOING_AJAX | |
| 98 | 91 | && |
| 99 | - $this->can_view_network_records() | |
| 92 | + preg_match( '#^' . network_admin_url() . '#i', $_SERVER['HTTP_REFERER'] ) | |
| 100 | 93 | ) { |
| 101 | 94 | define( 'WP_NETWORK_ADMIN', true ); |
| 102 | 95 | return WP_NETWORK_ADMIN; |
| 103 | 96 | } |
| @@ -105,36 +98,8 @@ | ||
| 105 | 98 | return false; |
| 106 | 99 | } |
| 107 | 100 | |
| 108 | 101 | /** |
| 109 | - * Whether the current user is allowed to read records across the whole | |
| 110 | - * network (and to be treated as being in the Network Admin). | |
| 111 | - * | |
| 112 | - * The Referer prefix checked in ajax_network_admin() is caller-controlled, | |
| 113 | - * so it can only ever be a UI hint about where a request came from -- never | |
| 114 | - * a source of authority. Network-wide record access additionally requires a | |
| 115 | - * real network capability, otherwise a site-level Stream viewer could spoof | |
| 116 | - * the header to lift the per-blog restriction applied in | |
| 117 | - * network_query_args() or to have their actions logged against blog_id 0. | |
| 118 | - * | |
| 119 | - * @return bool | |
| 120 | - */ | |
| 121 | - public function can_view_network_records() { | |
| 122 | - if ( ! is_multisite() ) { | |
| 123 | - return false; | |
| 124 | - } | |
| 125 | - | |
| 126 | - // WP-CLI runs with shell-level access and usually with no logged-in | |
| 127 | - // user, so capability checks would fail for a legitimate operator and | |
| 128 | - // break `wp stream query --blog_id=N`. It sits outside this boundary. | |
| 129 | - if ( defined( 'WP_CLI' ) && WP_CLI ) { | |
| 130 | - return true; | |
| 131 | - } | |
| 132 | - | |
| 133 | - return current_user_can( 'manage_network_options' ); | |
| 134 | - } | |
| 135 | - | |
| 136 | - /** | |
| 137 | 102 | * Builds a stdClass object used when displaying actions done in network administration |
| 138 | 103 | * |
| 139 | 104 | * @return object |
| 140 | 105 | */ |
| @@ -259,10 +224,15 @@ | ||
| 259 | 224 | } |
| 260 | 225 | |
| 261 | 226 | $current_page = wp_stream_filter_input( INPUT_GET, 'page' ); |
| 262 | 227 | |
| 263 | - if ( $this->network_settings_page_slug === $current_page ) { | |
| 264 | - $description = __( 'These settings apply to all sites on the network.', 'stream' ); | |
| 228 | + switch ( $current_page ) { | |
| 229 | + case $this->network_settings_page_slug: | |
| 230 | + $description = __( 'These settings apply to all sites on the network.', 'stream' ); | |
| 231 | + break; | |
| 232 | + case $this->default_settings_page_slug: | |
| 233 | + $description = __( 'These default settings will apply to new sites created on the network. These settings do not alter existing sites.', 'stream' ); | |
| 234 | + break; | |
| 265 | 235 | } |
| 266 | 236 | |
| 267 | 237 | return $description; |
| 268 | 238 | } |
| @@ -380,50 +350,48 @@ | ||
| 380 | 350 | /** |
| 381 | 351 | * Wrapper for the settings API to work on the network settings page |
| 382 | 352 | */ |
| 383 | 353 | public function network_options_action() { |
| 354 | + $allowed_referers = array( | |
| 355 | + $this->network_settings_page_slug, | |
| 356 | + $this->default_settings_page_slug, | |
| 357 | + ); | |
| 384 | 358 | |
| 385 | - // Check the nonce. | |
| 386 | - if ( | |
| 387 | - empty( $_POST['_wpnonce'] ) | |
| 388 | - || | |
| 389 | - ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), sprintf( '%s-options', $this->network_settings_option ) ) | |
| 390 | - ) { | |
| 359 | + // @codingStandardsIgnoreLine | |
| 360 | + if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referers, true ) ) { | |
| 391 | 361 | return; |
| 392 | 362 | } |
| 393 | 363 | |
| 394 | - // Check the user capability. | |
| 395 | - if ( ! current_user_can( $this->plugin->admin->settings_cap ) ) { | |
| 396 | - return; | |
| 397 | - } | |
| 364 | + // @codingStandardsIgnoreLine | |
| 365 | + $options = isset( $_POST['option_page'] ) ? explode( ',', stripslashes( $_POST['option_page'] ) ) : null; | |
| 398 | 366 | |
| 399 | - // Check the action. | |
| 400 | - $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; | |
| 401 | - if ( $this->network_settings_page_slug !== $action ) { | |
| 402 | - return; | |
| 403 | - } | |
| 367 | + if ( $options ) { | |
| 404 | 368 | |
| 405 | - $option = ! empty( $_POST['option_page'] ) ? sanitize_key( wp_unslash( $_POST['option_page'] ) ) : false; | |
| 369 | + foreach ( $options as $option ) { | |
| 370 | + $option = trim( $option ); | |
| 371 | + $value = null; | |
| 372 | + $sections = $this->plugin->settings->get_fields(); | |
| 406 | 373 | |
| 407 | - if ( $option && $this->network_settings_option === $option ) { | |
| 374 | + foreach ( $sections as $section_name => $section ) { | |
| 375 | + foreach ( $section['fields'] as $field_idx => $field ) { | |
| 376 | + $option_key = $section_name . '_' . $field['name']; | |
| 408 | 377 | |
| 409 | - $value = array(); | |
| 410 | - $posted_options = isset( $_POST[ $option ] ) && is_array( $_POST[ $option ] ) ? wp_unslash( $_POST[ $option ] ) : array(); | |
| 411 | - $sections = $this->plugin->settings->get_fields(); | |
| 378 | + // @codingStandardsIgnoreStart | |
| 379 | + if ( isset( $_POST[ $option ][ $option_key ] ) ) { | |
| 380 | + $value[ $option_key ] = $_POST[ $option ][ $option_key ]; | |
| 381 | + } else { | |
| 382 | + $value[ $option_key ] = false; | |
| 383 | + } | |
| 384 | + // @codingStandardsIgnoreEnd | |
| 385 | + } | |
| 386 | + } | |
| 412 | 387 | |
| 413 | - foreach ( $sections as $section_name => $section ) { | |
| 414 | - foreach ( $section['fields'] as $field_idx => $field ) { | |
| 415 | - $option_key = $section_name . '_' . $field['name']; | |
| 388 | + if ( ! is_array( $value ) ) { | |
| 389 | + $value = trim( $value ); | |
| 390 | + } | |
| 416 | 391 | |
| 417 | - if ( isset( $posted_options[ $option_key ] ) ) { | |
| 418 | - $value[ $option_key ] = $this->plugin->settings->sanitize_setting_by_field_type( $posted_options[ $option_key ], $field['type'] ); | |
| 419 | - } else { | |
| 420 | - $value[ $option_key ] = false; | |
| 421 | - } | |
| 422 | - } | |
| 392 | + update_site_option( $option, $value ); | |
| 423 | 393 | } |
| 424 | - | |
| 425 | - update_site_option( $this->network_settings_option, $value ); | |
| 426 | 394 | } |
| 427 | 395 | |
| 428 | 396 | if ( ! count( get_settings_errors() ) ) { |
| 429 | 397 | add_settings_error( 'general', 'settings_updated', __( 'Settings saved.', 'stream' ), 'updated' ); |
| @@ -533,21 +501,8 @@ | ||
| 533 | 501 | * @return array |
| 534 | 502 | */ |
| 535 | 503 | public function network_query_args( $args ) { |
| 536 | 504 | $args['site_id'] = is_numeric( $args['site_id'] ) ? $args['site_id'] : get_current_site()->id; |
| 537 | - | |
| 538 | - // Only users with a network capability may choose which blog to read | |
| 539 | - // from. For everyone else the requested blog_id is ignored entirely and | |
| 540 | - // forced to the current blog: a numeric type check is not an | |
| 541 | - // authorization check, and the Stream tables are shared across the | |
| 542 | - // whole network, so honouring an arbitrary ?blog_id= would let a | |
| 543 | - // site-level viewer read another site's activity. | |
| 544 | - if ( ! $this->can_view_network_records() ) { | |
| 545 | - $args['blog_id'] = get_current_blog_id(); | |
| 546 | - | |
| 547 | - return $args; | |
| 548 | - } | |
| 549 | - | |
| 550 | 505 | $args['blog_id'] = is_numeric( $args['blog_id'] ) ? $args['blog_id'] : ( is_network_admin() ? null : get_current_blog_id() ); |
| 551 | 506 | |
| 552 | 507 | return $args; |
| 553 | 508 | } |