diff --git a/wp-admin/includes/ajax-actions.php b/wp-admin/includes/ajax-actions.php index 257195018f..ca9ddda814 100644 --- a/wp-admin/includes/ajax-actions.php +++ b/wp-admin/includes/ajax-actions.php @@ -2054,9 +2054,17 @@ function wp_ajax_send_link_to_editor() { } function wp_ajax_heartbeat() { - check_ajax_referer( 'heartbeat-nonce', '_nonce' ); + if ( empty( $_POST['_nonce'] ) ) + wp_send_json_error(); + $response = array(); + if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) { + // User is logged in but nonces have expired. + $response['nonces_expired'] = true; + wp_send_json($response); + } + // screen_id is the same as $current_screen->id and the JS global 'pagenow' if ( ! empty($_POST['screen_id']) ) $screen_id = sanitize_key($_POST['screen_id']); @@ -2076,7 +2084,7 @@ function wp_ajax_heartbeat() { // Allow the transport to be replaced with long-polling easily do_action( 'heartbeat_tick', $response, $screen_id ); - // send the current time acording to the server + // Send the current time acording to the server $response['server_time'] = time(); wp_send_json($response); diff --git a/wp-admin/includes/misc.php b/wp-admin/includes/misc.php index ecaf717721..7cb68dd6e2 100644 --- a/wp-admin/includes/misc.php +++ b/wp-admin/includes/misc.php @@ -639,21 +639,25 @@ add_filter( 'heartbeat_received', 'wp_refresh_post_lock', 10, 3 ); function wp_refresh_post_nonces( $response, $data, $screen_id ) { if ( array_key_exists( 'wp-refresh-post-nonces', $data ) ) { $received = $data['wp-refresh-post-nonces']; + $response['wp-refresh-post-nonces'] = array( 'check' => 1 ); if ( ! $post_id = absint( $received['post_id'] ) ) return $response; - if ( ! current_user_can('edit_post', $post_id) ) + if ( ! current_user_can( 'edit_post', $post_id ) || empty( $received['post_nonce'] ) ) return $response; - if ( ! empty( $received['post_nonce'] ) && 2 === wp_verify_nonce( $received['post_nonce'], 'update-post_' . $post_id ) ) { + if ( 2 === wp_verify_nonce( $received['post_nonce'], 'update-post_' . $post_id ) ) { $response['wp-refresh-post-nonces'] = array( - 'replace-autosavenonce' => wp_create_nonce('autosave'), - 'replace-getpermalinknonce' => wp_create_nonce('getpermalink'), - 'replace-samplepermalinknonce' => wp_create_nonce('samplepermalink'), - 'replace-closedpostboxesnonce' => wp_create_nonce('closedpostboxes'), - 'replace-_ajax_linking_nonce' => wp_create_nonce( 'internal-linking' ), - 'replace-_wpnonce' => wp_create_nonce( 'update-post_' . $post_id ), + 'replace' => array( + 'autosavenonce' => wp_create_nonce('autosave'), + 'getpermalinknonce' => wp_create_nonce('getpermalink'), + 'samplepermalinknonce' => wp_create_nonce('samplepermalink'), + 'closedpostboxesnonce' => wp_create_nonce('closedpostboxes'), + '_ajax_linking_nonce' => wp_create_nonce( 'internal-linking' ), + '_wpnonce' => wp_create_nonce( 'update-post_' . $post_id ), + ), + 'heartbeatNonce' => wp_create_nonce( 'heartbeat-nonce' ), ); } } diff --git a/wp-admin/js/post.js b/wp-admin/js/post.js index dd3a427345..7b7d4c3944 100644 --- a/wp-admin/js/post.js +++ b/wp-admin/js/post.js @@ -316,7 +316,7 @@ $(document).on( 'heartbeat-tick.refresh-lock', function( e, data ) { function schedule() { check = false; window.clearTimeout( timeout ); - timeout = window.setTimeout( function(){ check = 1; }, 3600000 ); + timeout = window.setTimeout( function(){ check = true; }, 300000 ); } $(document).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { @@ -329,17 +329,21 @@ $(document).on( 'heartbeat-tick.refresh-lock', function( e, data ) { post_nonce: nonce }; } - check = 2; } }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { - if ( check === 2 ) + var nonces = data['wp-refresh-post-nonces']; + + if ( nonces ) { schedule(); - if ( data['wp-refresh-post-nonces'] ) { - $.each( data['wp-refresh-post-nonces'], function( selector, value ) { - if ( selector.match(/^replace-/) ) - $( '#' + selector.replace('replace-', '') ).val( value ); - }); + if ( nonces.replace ) { + $.each( nonces.replace, function( selector, value ) { + $( '#' + selector ).val( value ); + }); + } + + if ( nonces.heartbeatNonce ) + window.heartbeatSettings.nonce = nonces.heartbeatNonce; } }).ready( function() { schedule(); diff --git a/wp-includes/js/heartbeat.js b/wp-includes/js/heartbeat.js index c76c5525ea..09b38c77f0 100644 --- a/wp-includes/js/heartbeat.js +++ b/wp-includes/js/heartbeat.js @@ -10,7 +10,6 @@ window.wp = window.wp || {}; var self = this, running, beat, - nonce, screenId = typeof pagenow != 'undefined' ? pagenow : '', url = typeof ajaxurl != 'undefined' ? ajaxurl : '', settings, @@ -30,15 +29,13 @@ window.wp = window.wp || {}; this.autostart = true; this.connectionLost = false; - if ( typeof( window.heartbeatSettings ) != 'undefined' ) { - settings = window.heartbeatSettings; + if ( typeof( window.heartbeatSettings ) == 'object' ) { + settings = $.extend( {}, window.heartbeatSettings ); // Add private vars - nonce = settings.nonce || ''; - delete settings.nonce; - url = settings.ajaxurl || url; delete settings.ajaxurl; + delete settings.nonce; interval = settings.interval || 15; // default interval delete settings.interval; @@ -120,7 +117,8 @@ window.wp = window.wp || {}; } function connect() { - var send = {}, data, i, empty = true; + var send = {}, data, i, empty = true, + nonce = typeof window.heartbeatSettings == 'object' ? window.heartbeatSettings.nonce : ''; tick = time(); data = $.extend( {}, queue ); @@ -168,6 +166,11 @@ window.wp = window.wp || {}; if ( self.connectionLost ) errorstate(); + if ( response.nonces_expired ) { + $(document).trigger( 'heartbeat-nonces-expired' ); + return; + } + // Change the interval from PHP if ( response.heartbeat_interval ) { new_interval = response.heartbeat_interval; @@ -334,16 +337,19 @@ window.wp = window.wp || {}; * If the window doesn't have focus, the interval slows down to 2 min. * * @param string speed Interval speed: 'fast' (5sec), 'standard' (15sec) default, 'slow' (60sec) + * @param string ticks Used with speed = 'fast', how many ticks before the speed reverts back * @return int Current interval in seconds */ - this.interval = function( speed ) { + this.interval = function( speed, ticks ) { var reset, seconds; + ticks = parseInt( ticks, 10 ) || 30; + ticks = ticks < 1 || ticks > 30 ? 30 : ticks; if ( speed ) { switch ( speed ) { case 'fast': seconds = 5; - countdown = 30; + countdown = ticks; break; case 'slow': seconds = 60; diff --git a/wp-includes/js/wp-auth-check.js b/wp-includes/js/wp-auth-check.js index a8b4d85da8..2a1731879b 100644 --- a/wp-includes/js/wp-auth-check.js +++ b/wp-includes/js/wp-auth-check.js @@ -1,6 +1,6 @@ // Interim login dialog (function($){ - var wrap, check, timeout; + var wrap, check, scheduleTimeout, hideTimeout; function show() { var parent = $('#wp-auth-check'), form = $('#wp-auth-check-form'), noframe = wrap.find('.wp-auth-fallback-expired'), frame, loaded = false; @@ -32,7 +32,7 @@ height += 35; parent.find('.wp-auth-check-close').show(); wrap.data('logged-in', 1); - setTimeout( function() { hide(); }, 3000 ); + hideTimeout = setTimeout( function() { hide(); }, 3000 ); } parent.css( 'max-height', height + 60 + 'px' ); @@ -62,6 +62,14 @@ function hide() { $(window).off( 'beforeunload.wp-auth-check' ); + window.clearTimeout( hideTimeout ); + + // When on the Edit Post screen, speed up heartbeat after the user logs in to quickly refresh nonces + if ( typeof adminpage != 'undefined' && ( adminpage == 'post-php' || adminpage == 'post-new-php' ) + && typeof wp != 'undefined' && wp.heartbeat ) { + + wp.heartbeat.interval( 'fast', 1 ); + } wrap.fadeOut( 200, function() { wrap.addClass('hidden').css('display', '').find('.wp-auth-check-close').css('display', ''); @@ -71,8 +79,8 @@ function schedule() { check = false; - window.clearTimeout( timeout ); - timeout = window.setTimeout( function(){ check = 1; }, 180000 ); // 3 min. + window.clearTimeout( scheduleTimeout ); + scheduleTimeout = window.setTimeout( function(){ check = 1; }, 300000 ); // 5 min. } $( document ).on( 'heartbeat-tick.wp-auth-check', function( e, data ) {