Make WordPress Core

Ticket #25272: 25272.3.patch

File 25272.3.patch, 49.1 KB (added by azaozz, 11 years ago)
  • src/wp-admin/admin-ajax.php

     
    4949        'oembed-cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link',
    5050        'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment',
    5151        'add-link-category', 'add-tag', 'get-tagcloud', 'get-comments', 'replyto-comment',
    52         'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'autosave', 'closed-postboxes',
     52        'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'closed-postboxes',
    5353        'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax',
    5454        'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink',
    5555        'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order',
  • src/wp-admin/edit-form-advanced.php

     
    347347
    348348echo $form_extra;
    349349
    350 wp_nonce_field( 'autosave', 'autosavenonce', false );
    351350wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
    352351wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
    353352?>
  • src/wp-admin/includes/ajax-actions.php

     
    10571057        $x->send();
    10581058}
    10591059
    1060 function wp_ajax_autosave() {
    1061         define( 'DOING_AUTOSAVE', true );
    1062 
    1063         check_ajax_referer( 'autosave', 'autosavenonce' );
    1064 
    1065         if ( ! empty( $_POST['catslist'] ) )
    1066                 $_POST['post_category'] = explode( ',', $_POST['catslist'] );
    1067         if ( $_POST['post_type'] == 'page' || empty( $_POST['post_category'] ) )
    1068                 unset( $_POST['post_category'] );
    1069 
    1070         $data = '';
    1071         $supplemental = array();
    1072         $id = $revision_id = 0;
    1073 
    1074         $post_id = (int) $_POST['post_id'];
    1075         $_POST['ID'] = $_POST['post_ID'] = $post_id;
    1076         $post = get_post( $post_id );
    1077         if ( empty( $post->ID ) || ! current_user_can( 'edit_post', $post->ID ) )
    1078                 wp_die( __( 'You are not allowed to edit this post.' ) );
    1079 
    1080         if ( 'page' == $post->post_type && ! current_user_can( 'edit_page', $post->ID ) )
    1081                 wp_die( __( 'You are not allowed to edit this page.' ) );
    1082 
    1083         if ( 'auto-draft' == $post->post_status )
    1084                 $_POST['post_status'] = 'draft';
    1085 
    1086         if ( ! empty( $_POST['autosave'] ) ) {
    1087                 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) {
    1088                         // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked
    1089                         $id = edit_post();
    1090                 } else {
    1091                         // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user.
    1092                         $revision_id = wp_create_post_autosave( $post->ID );
    1093                         if ( is_wp_error($revision_id) )
    1094                                 $id = $revision_id;
    1095                         else
    1096                                 $id = $post->ID;
    1097                 }
    1098 
    1099                 if ( ! is_wp_error($id) ) {
    1100                         /* translators: draft saved date format, see http://php.net/date */
    1101                         $draft_saved_date_format = __('g:i:s a');
    1102                         /* translators: %s: date and time */
    1103                         $data = sprintf( __('Draft saved at %s.'), date_i18n( $draft_saved_date_format ) );
    1104                 }
    1105         } else {
    1106                 if ( ! empty( $_POST['auto_draft'] ) )
    1107                         $id = 0; // This tells us it didn't actually save
    1108                 else
    1109                         $id = $post->ID;
    1110         }
    1111 
    1112         // @todo Consider exposing any errors, rather than having 'Saving draft...'
    1113         $x = new WP_Ajax_Response( array(
    1114                 'what' => 'autosave',
    1115                 'id' => $id,
    1116                 'data' => $data,
    1117                 'supplemental' => $supplemental
    1118         ) );
    1119         $x->send();
    1120 }
    1121 
    11221060function wp_ajax_closed_postboxes() {
    11231061        check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
    11241062        $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
  • src/wp-admin/includes/misc.php

     
    683683                if ( 2 === wp_verify_nonce( $received['post_nonce'], 'update-post_' . $post_id ) ) {
    684684                        $response['wp-refresh-post-nonces'] = array(
    685685                                'replace' => array(
    686                                         'autosavenonce' => wp_create_nonce('autosave'),
    687686                                        'getpermalinknonce' => wp_create_nonce('getpermalink'),
    688687                                        'samplepermalinknonce' => wp_create_nonce('samplepermalink'),
    689688                                        'closedpostboxesnonce' => wp_create_nonce('closedpostboxes'),
     
    698697        return $response;
    699698}
    700699add_filter( 'heartbeat_received', 'wp_refresh_post_nonces', 10, 3 );
     700
     701/**
     702 * Autosave with heartbeat
     703 *
     704 * @since 3.7
     705 */
     706function wp_autosave( $response, $data ) {
     707        if ( ! empty( $data['autosave'] ) ) {
     708                $saved = autosave( $data['autosave'] );
     709
     710                if ( is_wp_error( $saved ) ) {
     711                        $response['autosave'] = array( 'success' => false, 'message' => $saved->get_error_message() );
     712                } elseif ( empty( $saved ) ) {
     713                        $response['autosave'] = array( 'success' => false, 'message' => __( 'Error while saving the draft.' ) );
     714                } else {
     715                        /* translators: draft saved date format, see http://php.net/date */
     716                        $draft_saved_date_format = __( 'g:i:s a' );
     717                        /* translators: %s: date and time */
     718                        $response['autosave'] = array( 'success' => true, 'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ) );
     719                }
     720        }
     721
     722        return $response;
     723}
     724// Run later as we have to set DOING_AUTOSAVE for back-compat
     725add_filter( 'heartbeat_received', 'wp_autosave', 50, 2 );
  • src/wp-admin/includes/post.php

     
    7979                }
    8080        }
    8181
    82         if ( ! empty( $post_data['post_status'] ) )
     82        if ( ! empty( $post_data['post_status'] ) ) {
    8383                $post_data['post_status'] = sanitize_key( $post_data['post_status'] );
    8484
     85                // No longer an auto-draft
     86                if ( 'auto-draft' == $post_data['post_status'] )
     87                        $post_data['post_status'] = 'draft';
     88        }
     89
    8590        // What to do based on which button they pressed
    8691        if ( isset($post_data['saveasdraft']) && '' != $post_data['saveasdraft'] )
    8792                $post_data['post_status'] = 'draft';
     
    181186        $post_data = _wp_translate_postdata( true, $post_data );
    182187        if ( is_wp_error($post_data) )
    183188                wp_die( $post_data->get_error_message() );
    184         if ( ( empty( $post_data['action'] ) || 'autosave' != $post_data['action'] ) && 'auto-draft' == $post_data['post_status'] ) {
    185                 $post_data['post_status'] = 'draft';
    186         }
    187189
    188190        if ( isset($post_data['visibility']) ) {
    189191                switch ( $post_data['visibility'] ) {
     
    13211323 * @uses _wp_translate_postdata()
    13221324 * @uses _wp_post_revision_fields()
    13231325 *
    1324  * @return unknown
     1326 * @param mixed $post_data Associative array containing the post data or int post ID.
     1327 * @return mixed The autosave revision ID. WP_Error or 0 on error.
    13251328 */
    1326 function wp_create_post_autosave( $post_id ) {
    1327         $translated = _wp_translate_postdata( true );
    1328         if ( is_wp_error( $translated ) )
    1329                 return $translated;
     1329function wp_create_post_autosave( $post_data ) {
     1330        if ( is_numeric( $post_data ) ) {
     1331                $post_id = $post_data;
     1332                $post_data = &$_POST;
     1333        } else {
     1334                $post_id = (int) $post_data['post_ID'];
     1335        }
    13301336
     1337        $post_data = _wp_translate_postdata( true, $post_data );
     1338        if ( is_wp_error( $post_data ) )
     1339                return $post_data;
     1340
    13311341        $post_author = get_current_user_id();
    13321342
    13331343        // Store one autosave per author. If there is already an autosave, overwrite it.
    13341344        if ( $old_autosave = wp_get_post_autosave( $post_id, $post_author ) ) {
    1335                 $new_autosave = _wp_post_revision_fields( $_POST, true );
     1345                $new_autosave = _wp_post_revision_fields( $post_data, true );
    13361346                $new_autosave['ID'] = $old_autosave->ID;
    13371347                $new_autosave['post_author'] = $post_author;
    13381348
    1339                 // If the new autosave is the same content as the post, delete the old autosave.
     1349                // If the new autosave has the same content as the post, delete the autosave.
    13401350                $post = get_post( $post_id );
    13411351                $autosave_is_different = false;
    13421352                foreach ( array_keys( _wp_post_revision_fields() ) as $field ) {
     
    13481358
    13491359                if ( ! $autosave_is_different ) {
    13501360                        wp_delete_post_revision( $old_autosave->ID );
    1351                         return;
     1361                        return 0;
    13521362                }
    13531363
    13541364                return wp_update_post( $new_autosave );
     
    13551365        }
    13561366
    13571367        // _wp_put_post_revision() expects unescaped.
    1358         $post_data = wp_unslash( $_POST );
     1368        $post_data = wp_unslash( $post_data );
    13591369
    13601370        // Otherwise create the new autosave as a special post revision
    13611371        return _wp_put_post_revision( $post_data, true );
     
    13811391function post_preview() {
    13821392
    13831393        $post_ID = (int) $_POST['post_ID'];
    1384         $status = get_post_status( $post_ID );
    1385         if ( 'auto-draft' == $status )
    1386                 wp_die( __('Preview not available. Please save as a draft first.') );
     1394        $_POST['ID'] = $post_ID;
    13871395
    1388         if ( isset($_POST['catslist']) )
    1389                 $_POST['post_category'] = explode(",", $_POST['catslist']);
     1396        if ( ! $post = get_post( $post_ID ) )
     1397                wp_die( __('You attempted to preview a non existing item.') );
    13901398
    1391         if ( isset($_POST['tags_input']) )
    1392                 $_POST['tags_input'] = explode(",", $_POST['tags_input']);
     1399        if ( ! current_user_can( 'edit_post', $post->ID ) )
     1400                wp_die( __('You are not allowed to preview this item.') );
    13931401
    1394         if ( $_POST['post_type'] == 'page' || empty($_POST['post_category']) )
    1395                 unset($_POST['post_category']);
     1402        $is_autosave = false;
    13961403
    1397         $_POST['ID'] = $post_ID;
    1398         $post = get_post($post_ID);
    1399 
    1400         if ( 'page' == $post->post_type ) {
    1401                 if ( ! current_user_can('edit_page', $post_ID) )
    1402                         wp_die( __('You are not allowed to edit this page.') );
     1404        if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'draft' == $post->post_status || 'auto-draft' == $post->post_status ) ) {
     1405                $saved_post_id = edit_post();
    14031406        } else {
    1404                 if ( ! current_user_can('edit_post', $post_ID) )
    1405                         wp_die( __('You are not allowed to edit this post.') );
    1406         }
     1407                $is_autosave = true;
    14071408
    1408         $user_id = get_current_user_id();
    1409         $locked = wp_check_post_lock( $post->ID );
    1410         if ( ! $locked && 'draft' == $post->post_status && $user_id == $post->post_author ) {
    1411                 $id = edit_post();
    1412         } else { // Non drafts are not overwritten. The autosave is stored in a special post revision.
    1413                 $id = wp_create_post_autosave( $post->ID );
    1414                 if ( ! is_wp_error($id) )
    1415                         $id = $post->ID;
     1409                if ( 'auto-draft' == $_POST['post_status'] )
     1410                        $_POST['post_status'] = 'draft';
     1411
     1412                $saved_post_id = wp_create_post_autosave( $post->ID );
    14161413        }
    14171414
    1418         if ( is_wp_error($id) )
    1419                 wp_die( $id->get_error_message() );
     1415        if ( is_wp_error( $saved_post_id ) )
     1416                wp_die( $saved_post_id->get_error_message() );
    14201417
    1421         if ( ! $locked && $_POST['post_status'] == 'draft' && $user_id == $post->post_author ) {
    1422                 $url = add_query_arg( 'preview', 'true', get_permalink($id) );
    1423         } else {
    1424                 $nonce = wp_create_nonce('post_preview_' . $id);
    1425                 $args = array(
    1426                         'preview' => 'true',
    1427                         'preview_id' => $id,
    1428                         'preview_nonce' => $nonce,
    1429                 );
     1418        $query_args = array( 'preview' => 'true' );
    14301419
     1420        if ( $is_autosave && $saved_post_id ) {
     1421                $query_args['preview_id'] = $post->ID;
     1422                $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID );
     1423
    14311424                if ( isset( $_POST['post_format'] ) )
    1432                         $args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] );
    1433 
    1434                 $url = add_query_arg( $args, get_permalink($id) );
     1425                        $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] );
    14351426        }
    14361427
     1428        $url = add_query_arg( $query_args, get_permalink( $post->ID ) );
    14371429        return apply_filters( 'preview_post_link', $url );
    14381430}
     1431
     1432/**
     1433 * Save a post submitted with XHR
     1434 *
     1435 * Intended for use with heartbeat and autosave.js
     1436 *
     1437 * @since 3.7
     1438 *
     1439 * @param $post_data Associative array of the submitted post data.
     1440 * @return mixed The value 0 or WP_Error on failure. The saved post ID on success.
     1441 *               Te ID can be either the draft post_id or the autosave revision post_id.
     1442 */
     1443function autosave( $post_data ) {
     1444        // Back-compat
     1445        if ( ! defined( 'DOING_AUTOSAVE' ) )
     1446                define( 'DOING_AUTOSAVE', true );
     1447
     1448        $post_id = (int) $post_data['post_id'];
     1449        $post_data['ID'] = $post_data['post_ID'] = $post_id;
     1450
     1451        if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) )
     1452                return new WP_Error( 'invalid_nonce', __('ERROR: invalid post data.') );
     1453
     1454        $post = get_post( $post_id );
     1455
     1456        if ( ! current_user_can( 'edit_post', $post->ID ) )
     1457                return new WP_Error( 'edit_post', __('You are not allowed to edit this item.') );
     1458
     1459        if ( 'auto-draft' == $post->post_status )
     1460                $post_data['post_status'] = 'draft';
     1461
     1462        if ( $post_data['post_type'] != 'page' && ! empty( $post_data['catslist'] ) )
     1463                $post_data['post_category'] = explode( ',', $post_data['catslist'] );
     1464
     1465        if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) {
     1466                // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked
     1467                return edit_post( $post_data );
     1468        } else {
     1469                // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user.
     1470                return wp_create_post_autosave( $post_data );
     1471        }
     1472}
  • src/wp-admin/js/post.js

     
    1 var tagBox, commentsBox, editPermalink, makeSlugeditClickable, WPSetThumbnailHTML, WPSetThumbnailID, WPRemoveThumbnail, wptitlehint;
     1var tagBox, commentsBox, WPSetThumbnailHTML, WPSetThumbnailID, WPRemoveThumbnail, wptitlehint;
     2// Prevent fatal errors (back-compat)
     3makeSlugeditClickable = editPermalink = function(){};
    24
    35// return an array with any duplicate, whitespace or values removed
    46function array_unique_noempty(a) {
     
    265267                send['lock'] = lock;
    266268
    267269        data['wp-refresh-post-lock'] = send;
    268 });
    269270
    270 // Post locks: update the lock string or show the dialog if somebody has taken over editing
    271 $(document).on( 'heartbeat-tick.refresh-lock', function( e, data ) {
     271}).on( 'heartbeat-tick.refresh-lock', function( e, data ) {
     272        // Post locks: update the lock string or show the dialog if somebody has taken over editing
    272273        var received, wrap, avatar;
    273274
    274275        if ( data['wp-refresh-post-lock'] ) {
     
    279280                        wrap = $('#post-lock-dialog');
    280281
    281282                        if ( wrap.length && ! wrap.is(':visible') ) {
    282                                 if ( typeof autosave == 'function' ) {
    283                                         $(document).on('autosave-disable-buttons.post-lock', function() {
    284                                                 wrap.addClass('saving');
    285                                         }).on('autosave-enable-buttons.post-lock', function() {
     283                                if ( typeof wp != 'undefined' && wp.autosave ) {
     284                                        // Save the latest changes and disable
     285                                        $(document).one( 'heartbeat-tick', function() {
     286                                                wp.autosave.server.disable();
    286287                                                wrap.removeClass('saving').addClass('saved');
    287                                                 window.onbeforeunload = null;
     288                                                $(window).off( 'beforeunload.autosave' );
    288289                                        });
    289290
    290                                         // Save the latest changes and disable
    291                                         if ( ! autosave() )
    292                                                 window.onbeforeunload = null;
    293 
    294                                         autosave = function(){};
     291                                        wrap.addClass('saving');
     292                                        wp.autosave.server.triggerSave();
    295293                                }
    296294
    297295                                if ( received.lock_error.avatar_src ) {
     
    306304                        $('#active_post_lock').val( received.new_lock );
    307305                }
    308306        }
     307}).on( 'after-autosave.update-post-slug', function() {
     308        // create slug area only if not already there
     309        if ( ! $('#edit-slug-box > *').length ) {
     310                $.post( ajaxurl, {
     311                                action: 'sample-permalink',
     312                                post_id: $('#post_ID').val(),
     313                                new_title: typeof fullscreen != 'undefined' && fullscreen.settings.visible ? $('#wp-fullscreen-title').val() : $('#title').val(),
     314                                samplepermalinknonce: $('#samplepermalinknonce').val()
     315                        },
     316                        function( data ) {
     317                                if ( data != '-1' ) {
     318                                        $('#edit-slug-box').html(data);
     319                                }
     320                        }
     321                );
     322        }
    309323});
    310324
    311325}(jQuery));
     
    351365}(jQuery));
    352366
    353367jQuery(document).ready( function($) {
    354         var stamp, visibility, sticky = '', last = 0, co = $('#content');
     368        var stamp, visibility, sticky = '', last = 0, co = $('#content'), $editSlugWrap = $('#edit-slug-box'),
     369                postId = $('#post_ID').val() || 0, $submitpost = $('#submitpost'), releaseLock = true;
    355370
    356371        postboxes.add_postbox_toggles(pagenow);
    357372
     
    371386                }
    372387        }).filter(':visible').find('.wp-tab-first').focus();
    373388
     389        // The form is being submitted by the user
     390        $submitpost.find( 'input[type="submit"], a.submitdelete' ).on( 'click.autosave', function( event ) {
     391                var $button = $(this);
     392
     393                if ( $button.prop('disabled') ) {
     394                        event.preventDefault();
     395                        return;
     396                }
     397
     398                if ( $button.hasClass('submitdelete') ) {
     399                        return;
     400                }
     401
     402                // The form submission can be blocked from JS or by using HTML 5.0 validation on some fields.
     403                // Run this only on an actual 'submit'.
     404                $('form#post').off( 'submit.edit-post' ).on( 'submit.edit-post', function( event ) {
     405                        if ( event.isDefaultPrevented() )
     406                                return;
     407
     408                        wp.autosave.server.disable();
     409                        releaseLock = false;
     410                        $(window).off( 'beforeunload.edit-post' );
     411
     412                        $submitpost.find('a.submitdelete, #post-preview').addClass('button-disabled');
     413
     414                        if ( $button.attr('id') == 'publish' )
     415                                $submitpost.find('#major-publishing-actions .spinner').show();
     416                        else
     417                                $submitpost.find('#minor-publishing .spinner').show();
     418                });
     419        });
     420
     421        // Submit the form saving a draft or an autosave, and show a preview in a new tab
     422        $('#post-preview').on( 'click.post-preview', function( event ) {
     423                event.preventDefault();
     424
     425                if ( $(this).prop('disabled') ) {
     426                        return;
     427                }
     428
     429                wp.autosave.server.blockSave();
     430                $('input#wp-preview').val('dopreview');
     431                $('form#post').attr('target', 'wp-preview').submit().attr('target', '');
     432
     433                // Workaround for WebKit bug preventing a form submitting twice to the same action.
     434                // https://bugs.webkit.org/show_bug.cgi?id=28633
     435                var ua = navigator.userAgent.toLowerCase();
     436                if ( ua.indexOf('safari') != -1 && ua.indexOf('chrome') == -1 ) {
     437                        $('form#post').attr( 'action', function( index, value ) {
     438                                return value + '?t=' + new Date().getTime();
     439                        });
     440                }
     441
     442                $('input#wp-preview').val('');
     443        });
     444
     445        // This code is meant to allow tabbing from Title to Post content.
     446        $('#title').on( 'keydown.editor-focus', function( event ) {
     447                var editor;
     448
     449                if ( event.which == 9 && ! event.ctrlKey && ! event.altKey && ! event.shiftKey ) {
     450                        editor = typeof tinymce != 'undefined' && tinymce.get('content');
     451
     452                        if ( editor && ! editor.isHidden() ) {
     453                                $(this).one( 'keyup', function() {
     454                                        $('#content_tbl td.mceToolbar > a').focus();
     455                                });
     456                        } else {
     457                                $('#content').focus();
     458                        }
     459
     460                        event.preventDefault();
     461                }
     462        });
     463       
     464        $(window).on( 'beforeunload.edit-post', function() {
     465                var editor = typeof tinymce != 'undefined' && tinymce.activeEditor, compareString;
     466
     467                if ( editor && ! editor.isHidden() ) {
     468                        if ( editor.isDirty() )
     469                                return autosaveL10n.saveAlert;
     470                } else {
     471                        if ( fullscreen && fullscreen.settings.visible ) {
     472                                compareString = wp.autosave.getCompareString({
     473                                        post_title: $('#wp-fullscreen-title').val() || '',
     474                                        content: $('#wp_mce_fullscreen').val() || '',
     475                                        excerpt: $('#excerpt').val() || ''
     476                                });
     477                        } else {
     478                                compareString = wp.autosave.getCompareString();
     479                        }
     480
     481                        if ( compareString != wp.autosave.server.autosaveLast )
     482                                return autosaveL10n.saveAlert;
     483                }
     484        }).on( 'unload.edit-post', function( event ) {
     485                if ( ! releaseLock )
     486                        return;
     487
     488                // Unload is triggered (by hand) on removing the Thickbox iframe.
     489                // Make sure we process only the main document unload.
     490                if ( event.target && event.target.nodeName != '#document' )
     491                        return;
     492
     493                $.ajax({
     494                        type: 'POST',
     495                        url: ajaxurl,
     496                        async: false,
     497                        data: {
     498                                action: 'wp-remove-post-lock',
     499                                _wpnonce: $('#_wpnonce').val(),
     500                                post_ID: $('#post_ID').val(),
     501                                active_post_lock: $('#active_post_lock').val()
     502                        }
     503                });
     504        });
     505
    374506        // multi-taxonomies
    375507        if ( $('#tagsdiv-post_tag').length ) {
    376508                tagBox.init();
     
    680812        } // end submitdiv
    681813
    682814        // permalink
    683         if ( $('#edit-slug-box').length ) {
    684                 editPermalink = function(post_id) {
    685                         var i, c = 0, e = $('#editable-post-name'), revert_e = e.html(), real_slug = $('#post_name'), revert_slug = real_slug.val(), b = $('#edit-slug-buttons'), revert_b = b.html(), full = $('#editable-post-name-full').html();
     815        if ( $editSlugWrap.length ) {
     816                function editPermalink() {
     817                        var i, c = 0, e = $('#editable-post-name'), revert_e = e.html(), real_slug = $('#post_name'),
     818                                revert_slug = real_slug.val(), b = $('#edit-slug-buttons'), revert_b = b.html(),
     819                                full = $('#editable-post-name-full').html();
    686820
    687821                        $('#view-post-btn').hide();
    688822                        b.html('<a href="#" class="save button button-small">'+postL10n.ok+'</a> <a class="cancel" href="#">'+postL10n.cancel+'</a>');
     
    689823                        b.children('.save').click(function() {
    690824                                var new_slug = e.children('input').val();
    691825                                if ( new_slug == $('#editable-post-name-full').text() ) {
    692                                         return $('.cancel', '#edit-slug-buttons').click();
     826                                        return $('#edit-slug-buttons .cancel').click();
    693827                                }
    694828                                $.post(ajaxurl, {
    695829                                        action: 'sample-permalink',
    696                                         post_id: post_id,
     830                                        post_id: postId,
    697831                                        new_slug: new_slug,
    698832                                        new_title: $('#title').val(),
    699833                                        samplepermalinknonce: $('#samplepermalinknonce').val()
     
    707841                                        }
    708842                                        b.html(revert_b);
    709843                                        real_slug.val(new_slug);
    710                                         makeSlugeditClickable();
    711844                                        $('#view-post-btn').show();
    712845                                });
    713846                                return false;
    714847                        });
    715848
    716                         $('.cancel', '#edit-slug-buttons').click(function() {
     849                        $('#edit-slug-buttons .cancel').click(function() {
    717850                                $('#view-post-btn').show();
    718851                                e.html(revert_e);
    719852                                b.html(revert_b);
     
    743876                        }).focus();
    744877                }
    745878
    746                 makeSlugeditClickable = function() {
    747                         $('#editable-post-name').click(function() {
    748                                 $('#edit-slug-buttons').children('.edit-slug').click();
    749                         });
    750                 }
    751                 makeSlugeditClickable();
     879                $editSlugWrap.on( 'click', function( event ) {
     880                        var $target = $( event.target );
     881
     882                        if ( $target.is('#editable-post-name') || $target.hasClass('edit-slug') ) {
     883                                editPermalink();
     884                        }
     885                });
    752886        }
    753887
    754888        // word count
  • src/wp-admin/post.php

     
    296296        break;
    297297
    298298case 'preview':
    299         check_admin_referer( 'autosave', 'autosavenonce' );
     299        check_admin_referer( 'update-post_' . $post_id );
    300300
    301301        $url = post_preview();
    302302
  • src/wp-includes/js/autosave.js

     
    1 var autosave, autosaveLast = '', autosavePeriodical, autosaveDelayPreview = false, notSaved = true, blockSave = false, fullscreen, autosaveLockRelease = true;
     1window.wp = window.wp || {};
    22
    3 jQuery(document).ready( function($) {
     3( function( $, window ) {
     4        var $document = $(document), nextRun = 0, _disabled, _blockSave, _blockSaveTimeout;
    45
    5         if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) {
    6                 autosaveLast = wp.autosave.getCompareString({
    7                         post_title : $('#title').val() || '',
    8                         content : switchEditors.pre_wpautop( $('#content').val() ) || '',
    9                         excerpt : $('#excerpt').val() || ''
    10                 });
    11         } else {
    12                 autosaveLast = wp.autosave.getCompareString();
    13         }
     6/**
     7 * Returns the data saved in both local and remote autosave
     8 *
     9 * @return mixed Object containing the post data or false
     10 */
     11wp.autosave = {
     12        getPostData: function( type ) {
     13                var post_name, parent_id, cats = [], data,
     14                        editor = typeof tinymce != 'undefined' && tinymce.get('content');
    1415
    15         autosavePeriodical = $.schedule({time: autosaveL10n.autosaveInterval * 1000, func: function() { autosave(); }, repeat: true, protect: true});
    16 
    17         //Disable autosave after the form has been submitted
    18         $("#post").submit(function() {
    19                 $.cancel(autosavePeriodical);
    20                 autosaveLockRelease = false;
    21         });
    22 
    23         $('input[type="submit"], a.submitdelete', '#submitpost').click(function(){
    24                 blockSave = true;
    25                 window.onbeforeunload = null;
    26                 $(':button, :submit', '#submitpost').each(function(){
    27                         var t = $(this);
    28                         if ( t.hasClass('button-primary') )
    29                                 t.addClass('button-primary-disabled');
    30                         else
    31                                 t.addClass('button-disabled');
    32                 });
    33                 if ( $(this).attr('id') == 'publish' )
    34                         $('#major-publishing-actions .spinner').show();
    35                 else
    36                         $('#minor-publishing .spinner').show();
    37         });
    38 
    39         window.onbeforeunload = function(){
    40                 var editor = typeof(tinymce) != 'undefined' ? tinymce.activeEditor : false, compareString;
    41 
    4216                if ( editor && ! editor.isHidden() ) {
    43                         if ( editor.isDirty() )
    44                                 return autosaveL10n.saveAlert;
    45                 } else {
    46                         if ( fullscreen && fullscreen.settings.visible ) {
    47                                 compareString = wp.autosave.getCompareString({
    48                                         post_title: $('#wp-fullscreen-title').val() || '',
    49                                         content: $('#wp_mce_fullscreen').val() || '',
    50                                         excerpt: $('#excerpt').val() || ''
    51                                 });
     17                        // Don't run while the tinymce spellcheck is on. It resets all found words.
     18                        if ( editor.plugins.spellchecker && editor.plugins.spellchecker.active ) {
     19                                return false;
    5220                        } else {
    53                                 compareString = wp.autosave.getCompareString();
     21                                tinymce.triggerSave();
    5422                        }
    55 
    56                         if ( compareString != autosaveLast )
    57                                 return autosaveL10n.saveAlert;
    5823                }
    59         };
    6024
    61         $(window).unload( function(e) {
    62                 if ( ! autosaveLockRelease )
    63                         return;
    64 
    65                 // unload fires (twice) on removing the Thickbox iframe. Make sure we process only the main document unload.
    66                 if ( e.target && e.target.nodeName != '#document' )
    67                         return;
    68 
    69                 $.ajax({
    70                         type: 'POST',
    71                         url: ajaxurl,
    72                         async: false,
    73                         data: {
    74                                 action: 'wp-remove-post-lock',
    75                                 _wpnonce: $('#_wpnonce').val(),
    76                                 post_ID: $('#post_ID').val(),
    77                                 active_post_lock: $('#active_post_lock').val()
    78                         }
    79                 });
    80         } );
    81 
    82         // preview
    83         $('#post-preview').click(function(){
    84                 if ( $('#auto_draft').val() == '1' && notSaved ) {
    85                         autosaveDelayPreview = true;
    86                         autosave();
    87                         return false;
    88                 }
    89                 doPreview();
    90                 return false;
    91         });
    92 
    93         doPreview = function() {
    94                 $('input#wp-preview').val('dopreview');
    95                 $('form#post').attr('target', 'wp-preview').submit().attr('target', '');
    96 
    97                 /*
    98                  * Workaround for WebKit bug preventing a form submitting twice to the same action.
    99                  * https://bugs.webkit.org/show_bug.cgi?id=28633
    100                  */
    101                 var ua = navigator.userAgent.toLowerCase();
    102                 if ( ua.indexOf('safari') != -1 && ua.indexOf('chrome') == -1 ) {
    103                         $('form#post').attr('action', function(index, value) {
    104                                 return value + '?t=' + new Date().getTime();
    105                         });
    106                 }
    107 
    108                 $('input#wp-preview').val('');
    109         }
    110 
    111         // This code is meant to allow tabbing from Title to Post content.
    112         $('#title').on('keydown.editor-focus', function(e) {
    113                 var ed;
    114 
    115                 if ( e.which != 9 )
    116                         return;
    117 
    118                 if ( !e.ctrlKey && !e.altKey && !e.shiftKey ) {
    119                         if ( typeof(tinymce) != 'undefined' )
    120                                 ed = tinymce.get('content');
    121 
    122                         if ( ed && !ed.isHidden() ) {
    123                                 $(this).one('keyup', function(e){
    124                                         $('#content_tbl td.mceToolbar > a').focus();
    125                                 });
    126                         } else {
    127                                 $('#content').focus();
    128                         }
    129 
    130                         e.preventDefault();
    131                 }
    132         });
    133 
    134         // autosave new posts after a title is typed but not if Publish or Save Draft is clicked
    135         if ( '1' == $('#auto_draft').val() ) {
    136                 $('#title').blur( function() {
    137                         if ( !this.value || $('#auto_draft').val() != '1' )
    138                                 return;
    139                         delayed_autosave();
    140                 });
    141         }
    142 
    143         // When connection is lost, keep user from submitting changes.
    144         $(document).on('heartbeat-connection-lost.autosave', function( e, error ) {
    145                 if ( 'timeout' === error ) {
    146                         var notice = $('#lost-connection-notice');
    147                         if ( ! wp.autosave.local.hasStorage ) {
    148                                 notice.find('.hide-if-no-sessionstorage').hide();
    149                         }
    150                         notice.show();
    151                         autosave_disable_buttons();
    152                 }
    153         }).on('heartbeat-connection-restored.autosave', function() {
    154                 $('#lost-connection-notice').hide();
    155                 autosave_enable_buttons();
    156         });
    157 });
    158 
    159 function autosave_parse_response( response ) {
    160         var res = wpAjax.parseAjaxResponse(response, 'autosave'), post_id, sup;
    161 
    162         if ( res && res.responses && res.responses.length ) {
    163                 if ( res.responses[0].supplemental ) {
    164                         sup = res.responses[0].supplemental;
    165 
    166                         jQuery.each( sup, function( selector, value ) {
    167                                 if ( selector.match(/^replace-/) )
    168                                         jQuery( '#' + selector.replace('replace-', '') ).val( value );
    169                         });
    170                 }
    171 
    172                 // if no errors: add slug UI and update autosave-message
    173                 if ( !res.errors ) {
    174                         if ( post_id = parseInt( res.responses[0].id, 10 ) )
    175                                 autosave_update_slug( post_id );
    176 
    177                         if ( res.responses[0].data ) // update autosave message
    178                                 jQuery('.autosave-message').text( res.responses[0].data );
    179                 }
    180         }
    181 
    182         return res;
    183 }
    184 
    185 // called when autosaving pre-existing post
    186 function autosave_saved(response) {
    187         blockSave = false;
    188         autosave_parse_response(response); // parse the ajax response
    189         autosave_enable_buttons(); // re-enable disabled form buttons
    190 }
    191 
    192 // called when autosaving new post
    193 function autosave_saved_new(response) {
    194         blockSave = false;
    195         var res = autosave_parse_response(response), post_id;
    196 
    197         if ( res && res.responses.length && !res.errors ) {
    198                 // An ID is sent only for real auto-saves, not for autosave=0 "keepalive" saves
    199                 post_id = parseInt( res.responses[0].id, 10 );
    200 
    201                 if ( post_id ) {
    202                         notSaved = false;
    203                         jQuery('#auto_draft').val('0'); // No longer an auto-draft
    204                 }
    205 
    206                 autosave_enable_buttons();
    207 
    208                 if ( autosaveDelayPreview ) {
    209                         autosaveDelayPreview = false;
    210                         doPreview();
    211                 }
    212         } else {
    213                 autosave_enable_buttons(); // re-enable disabled form buttons
    214         }
    215 }
    216 
    217 function autosave_update_slug(post_id) {
    218         // create slug area only if not already there
    219         if ( 'undefined' != makeSlugeditClickable && jQuery.isFunction(makeSlugeditClickable) && !jQuery('#edit-slug-box > *').size() ) {
    220                 jQuery.post( ajaxurl, {
    221                                 action: 'sample-permalink',
    222                                 post_id: post_id,
    223                                 new_title: fullscreen && fullscreen.settings.visible ? jQuery('#wp-fullscreen-title').val() : jQuery('#title').val(),
    224                                 samplepermalinknonce: jQuery('#samplepermalinknonce').val()
    225                         },
    226                         function(data) {
    227                                 if ( data !== '-1' ) {
    228                                         var box = jQuery('#edit-slug-box');
    229                                         box.html(data);
    230                                         if (box.hasClass('hidden')) {
    231                                                 box.fadeIn('fast', function () {
    232                                                         box.removeClass('hidden');
    233                                                 });
    234                                         }
    235                                         makeSlugeditClickable();
    236                                 }
    237                         }
    238                 );
    239         }
    240 }
    241 
    242 function autosave_loading() {
    243         jQuery('.autosave-message').html(autosaveL10n.savingText);
    244 }
    245 
    246 function autosave_enable_buttons() {
    247         jQuery(document).trigger('autosave-enable-buttons');
    248         if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) {
    249                 // delay that a bit to avoid some rare collisions while the DOM is being updated.
    250                 setTimeout(function(){
    251                         var parent = jQuery('#submitpost');
    252                         parent.find(':button, :submit').removeAttr('disabled');
    253                         parent.find('.spinner').hide();
    254                 }, 500);
    255         }
    256 }
    257 
    258 function autosave_disable_buttons() {
    259         jQuery(document).trigger('autosave-disable-buttons');
    260         jQuery('#submitpost').find(':button, :submit').prop('disabled', true);
    261         // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions.
    262         setTimeout( autosave_enable_buttons, 5000 );
    263 }
    264 
    265 function delayed_autosave() {
    266         setTimeout(function(){
    267                 if ( blockSave )
    268                         return;
    269                 autosave();
    270         }, 200);
    271 }
    272 
    273 autosave = function() {
    274         var post_data = wp.autosave.getPostData(),
    275                 compareString,
    276                 successCallback;
    277 
    278         blockSave = true;
    279 
    280         // post_data.content cannot be retrieved at the moment
    281         if ( ! post_data.autosave )
    282                 return false;
    283 
    284         // No autosave while thickbox is open (media buttons)
    285         if ( jQuery("#TB_window").css('display') == 'block' )
    286                 return false;
    287 
    288         compareString = wp.autosave.getCompareString( post_data );
    289 
    290         // Nothing to save or no change.
    291         if ( compareString == autosaveLast )
    292                 return false;
    293 
    294         autosaveLast = compareString;
    295         jQuery(document).triggerHandler('wpcountwords', [ post_data["content"] ]);
    296 
    297         // Disable buttons until we know the save completed.
    298         autosave_disable_buttons();
    299 
    300         if ( post_data["auto_draft"] == '1' ) {
    301                 successCallback = autosave_saved_new; // new post
    302         } else {
    303                 successCallback = autosave_saved; // pre-existing post
    304         }
    305 
    306         jQuery.ajax({
    307                 data: post_data,
    308                 beforeSend: autosave_loading,
    309                 type: "POST",
    310                 url: ajaxurl,
    311                 success: successCallback
    312         });
    313 
    314         return true;
    315 }
    316 
    317 // Autosave in localStorage
    318 // set as simple object/mixin for now
    319 window.wp = window.wp || {};
    320 wp.autosave = wp.autosave || {};
    321 
    322 (function($){
    323 // Returns the data for saving in both localStorage and autosaves to the server
    324 wp.autosave.getPostData = function() {
    325         var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [],
    32625                data = {
    327                         action: 'autosave',
    328                         autosave: true,
    32926                        post_id: $('#post_ID').val() || 0,
    330                         autosavenonce: $('#autosavenonce').val() || '',
    33127                        post_type: $('#post_type').val() || '',
    33228                        post_author: $('#post_author').val() || '',
    33329                        excerpt: $('#excerpt').val() || ''
    33430                };
    33531
    336         if ( ed && !ed.isHidden() ) {
    337                 // Don't run while the tinymce spellcheck is on. It resets all found words.
    338                 if ( ed.plugins.spellchecker && ed.plugins.spellchecker.active ) {
    339                         data.autosave = false;
    340                         return data;
     32                if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) {
     33                        data['post_title'] = $('#wp-fullscreen-title').val() || '';
     34                        data['content'] = $('#wp_mce_fullscreen').val() || '';
    34135                } else {
    342                         if ( 'mce_fullscreen' == ed.id )
    343                                 tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
    344 
    345                         tinymce.triggerSave();
     36                        data['post_title'] = $('#title').val() || '';
     37                        data['content'] = $('#content').val() || '';
    34638                }
    347         }
    34839
    349         if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) {
    350                 data['post_title'] = $('#wp-fullscreen-title').val() || '';
    351                 data['content'] = $('#wp_mce_fullscreen').val() || '';
    352         } else {
    353                 data['post_title'] = $('#title').val() || '';
    354                 data['content'] = $('#content').val() || '';
    355         }
     40                if ( type == 'local' )
     41                        return data;
    35642
    357         /*
    358         // We haven't been saving tags with autosave since 2.8... Start again?
    359         $('.the-tags').each( function() {
    360                 data[this.name] = this.value;
    361         });
    362         */
     43                $('input[id^="in-category-"]:checked').each( function() {
     44                        cats.push( this.value );
     45                });
     46                data['catslist'] = cats.join(',');
    36347
    364         $('input[id^="in-category-"]:checked').each( function() {
    365                 cats.push(this.value);
    366         });
    367         data['catslist'] = cats.join(',');
     48                if ( post_name = $('#post_name').val() )
     49                        data['post_name'] = post_name;
    36850
    369         if ( post_name = $('#post_name').val() )
    370                 data['post_name'] = post_name;
     51                if ( parent_id = $('#parent_id').val() )
     52                        data['parent_id'] = parent_id;
    37153
    372         if ( parent_id = $('#parent_id').val() )
    373                 data['parent_id'] = parent_id;
     54                if ( $('#comment_status').prop('checked') )
     55                        data['comment_status'] = 'open';
    37456
    375         if ( $('#comment_status').prop('checked') )
    376                 data['comment_status'] = 'open';
     57                if ( $('#ping_status').prop('checked') )
     58                        data['ping_status'] = 'open';
    37759
    378         if ( $('#ping_status').prop('checked') )
    379                 data['ping_status'] = 'open';
     60                if ( $('#auto_draft').val() == '1' )
     61                        data['auto_draft'] = '1';
    38062
    381         if ( $('#auto_draft').val() == '1' )
    382                 data['auto_draft'] = '1';
     63                return data;
     64        },
    38365
    384         return data;
    385 };
     66        // Concatenate title, content and excerpt. Used to track changes when auto-saving.
     67        getCompareString: function( post_data ) {
     68                if ( typeof post_data === 'object' ) {
     69                        return ( post_data.post_title || '' ) + '::' + ( post_data.content || '' ) + '::' + ( post_data.excerpt || '' );
     70                }
    38671
    387 // Concatenate title, content and excerpt. Used to track changes when auto-saving.
    388 wp.autosave.getCompareString = function( post_data ) {
    389         if ( typeof post_data === 'object' ) {
    390                 return ( post_data.post_title || '' ) + '::' + ( post_data.content || '' ) + '::' + ( post_data.excerpt || '' );
     72                return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' );
     73        },
     74
     75        enableButtons: function() {
     76                $document.trigger('autosave-enable-buttons');
     77
     78                if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) {
     79                        $('#submitpost').find(':button, :submit, a.submitdelete, #post-preview').prop( 'disabled', false ).removeClass( 'button-disabled' );
     80                }
     81        },
     82
     83        disableButtons: function() {
     84                $document.trigger('autosave-disable-buttons');
     85                $('#submitpost').find(':button, :submit, a.submitdelete, #post-preview').prop( 'disabled', true ).addClass( 'button-disabled' );
     86                // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions.
     87                setTimeout( wp.autosave.enableButtons, 5000 );
    39188        }
    392 
    393         return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' );
    39489};
    39590
     91// Autosave in localStorage
     92// set as simple object/mixin for now
    39693wp.autosave.local = {
    397 
    39894        lastSavedData: '',
    39995        blog_id: 0,
    40096        hasStorage: false,
     
    502198        save: function( data ) {
    503199                var result = false, post_data, compareString;
    504200
    505                 if ( ! data ) {
    506                         post_data = wp.autosave.getPostData();
    507                 } else {
     201                if ( data ) {
    508202                        post_data = this.getData() || {};
    509203                        $.extend( post_data, data );
    510                         post_data.autosave = true;
     204                } else {
     205                        post_data = wp.autosave.getPostData('local');
     206                        // Cannot get the post data at the moment
     207                        if ( ! post_data )
     208                                return false;
    511209                }
    512210
    513                 // Cannot get the post data at the moment
    514                 if ( ! post_data.autosave )
    515                         return false;
    516 
    517211                compareString = wp.autosave.getCompareString( post_data );
    518212
    519213                // If the content, title and excerpt did not change since the last save, don't save again
     
    697391        }
    698392};
    699393
     394// Autosave on the server
     395wp.autosave.server = {
     396        autosaveLast: '',
     397
     398        init: function() {
     399                var self = this;
     400
     401                $document.on( 'heartbeat-send.autosave', function( event, data ) {
     402                        var autosaveData = self.getData();
     403
     404                        if ( autosaveData ) {
     405                                data.autosave = autosaveData;
     406                        }
     407                }).on( 'heartbeat-tick.autosave', function( event, data ) {
     408                        if ( data.autosave ) {
     409                                self.response( data.autosave );
     410                        }
     411                }).on('heartbeat-connection-lost.autosave', function( event, error ) {
     412                        // When connection is lost, keep user from submitting changes.
     413                        if ( 'timeout' === error ) {
     414                                var notice = $('#lost-connection-notice');
     415
     416                                if ( ! wp.autosave.local.hasStorage ) {
     417                                        notice.find('.hide-if-no-sessionstorage').hide();
     418                                }
     419
     420                                notice.show();
     421                                wp.autosave.disableButtons();
     422                        }
     423                }).on('heartbeat-connection-restored.autosave', function() {
     424                        $('#lost-connection-notice').hide();
     425                        wp.autosave.enableButtons();
     426                }).ready( function() {
     427                        self._schedule();
     428
     429                        if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) {
     430                                self.autosaveLast = wp.autosave.getCompareString({
     431                                        post_title : $('#title').val() || '',
     432                                        content : switchEditors.pre_wpautop( $('#content').val() ) || '',
     433                                        excerpt : $('#excerpt').val() || ''
     434                                });
     435                        } else {
     436                                self.autosaveLast = wp.autosave.getCompareString();
     437                        }
     438
     439                        // Autosave new posts after a title is typed
     440                        if ( $('#auto_draft').val() ) {
     441                                $('#title').blur( function() {
     442                                        if ( ! this.value || $('#auto_draft').val() != '1' )
     443                                                return;
     444
     445                                        self.triggerSave();
     446                                });
     447                        }
     448                });
     449        },
     450
     451        // Block saving for the next 10 sec.
     452        blockSave: function() {
     453                _blockSave = true;
     454                window.clearTimeout( _blockSaveTimeout );
     455                _blockSaveTimeout = window.setTimeout( function() {
     456                        _blockSave = false;
     457                }, 10000 );
     458        },
     459
     460        // Runs on heartbeat-response
     461        response: function( data ) {
     462                this._schedule();
     463                _blockSave = false;
     464                this.autosaveLast = this.lastCompareString;
     465                this.lastCompareString = '';
     466
     467                $document.trigger( 'after-autosave', [data] );
     468                $('.autosave-message').text( data.message );
     469                wp.autosave.enableButtons();
     470
     471                if ( data.success ) {
     472                        // No longer an auto-draft
     473                        $('#auto_draft').val('');
     474                }
     475        },
     476
     477        disable: function() {
     478                _disabled = true;
     479        },
     480
     481        _schedule: function() {
     482                nextRun = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000;
     483        },
     484
     485        // Reset the timing and tell heartbeat to connect now
     486        triggerSave: function() {
     487                nextRun = 0;
     488                wp.heartbeat.connectNow();
     489        },
     490
     491        // Runs on 'heartbeat-send'
     492        getData: function() {
     493                var compareString, postData;
     494
     495                if ( _disabled || _blockSave ) {
     496                        return false;
     497                }
     498
     499                if ( ( new Date() ).getTime() < nextRun ) {
     500                        return false;
     501                }
     502
     503                postData = wp.autosave.getPostData();
     504
     505                // post_data.content cannot be retrieved at the moment
     506                if ( ! postData ) {
     507                        return false;
     508                }
     509
     510                compareString = wp.autosave.getCompareString( postData );
     511
     512                // Nothing to save or no change.
     513                if ( compareString == this.autosaveLast ) {
     514                        return false;
     515                }
     516
     517                this.blockSave();
     518                wp.autosave.disableButtons();
     519                this.lastCompareString = compareString;
     520
     521                $document.trigger( 'wpcountwords', [ postData['content'] ] )
     522                .trigger( 'before-autosave', [ postData ] );
     523
     524                $('.autosave-message').text( autosaveL10n.savingText );
     525                postData._wpnonce = $('#_wpnonce').val() || '';
     526
     527                return postData;
     528        }
     529};
     530
    700531wp.autosave.local.init();
     532wp.autosave.server.init();
    701533
    702 }(jQuery));
     534}( jQuery, window ));
  • src/wp-includes/revision.php

     
    215215 *
    216216 * @param int|object|array $post Post ID, post object OR post array.
    217217 * @param bool $autosave Optional. Is the revision an autosave?
    218  * @return mixed Null or 0 if error, new revision ID if success.
     218 * @return mixed WP_Error or 0 if error, new revision ID if success.
    219219 */
    220220function _wp_put_post_revision( $post = null, $autosave = false ) {
    221221        if ( is_object($post) )
     
    223223        elseif ( !is_array($post) )
    224224                $post = get_post($post, ARRAY_A);
    225225
    226         if ( !$post || empty($post['ID']) )
    227                 return;
     226        if ( ! $post || empty($post['ID']) )
     227                return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
    228228
    229229        if ( isset($post['post_type']) && 'revision' == $post['post_type'] )
    230230                return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) );
  • tests/phpunit/includes/testcase-ajax.php

     
    4242                'oembed_cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link',
    4343                'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment',
    4444                'add-link-category', 'add-tag', 'get-tagcloud', 'get-comments', 'replyto-comment',
    45                 'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'autosave', 'closed-postboxes',
     45                'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'closed-postboxes',
    4646                'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax',
    4747                'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink',
    4848                'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order',
    4949                'save-widget', 'set-post-thumbnail', 'date_format', 'time_format', 'wp-fullscreen-save-post',
    50                 'wp-remove-post-lock', 'dismiss-wp-pointer', 'nopriv_autosave'
     50                'wp-remove-post-lock', 'dismiss-wp-pointer', 'heartbeat', 'nopriv_heartbeat',
    5151        );
    5252
    5353        /**
  • tests/phpunit/tests/ajax/Autosave.php

     
    2323        protected $_post = null;
    2424
    2525        /**
     26         * user_id
     27         * @var int
     28         */
     29        protected $user_id = 0;
     30
     31        /**
    2632         * Set up the test fixture
    2733         */
    2834        public function setUp() {
    2935                parent::setUp();
     36                // Set a user so the $post has 'post_author'
     37                $this->user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
     38                wp_set_current_user( $this->user_id );
     39               
    3040                $post_id = $this->factory->post->create( array( 'post_status' => 'draft' ) );
    3141                $this->_post = get_post( $post_id );
    3242        }
    3343
    3444        /**
     45         * Tear down the test fixture.
     46         * Reset the current user
     47         */
     48        public function tearDown() {
     49                parent::tearDown();
     50                wp_set_current_user( 0 );
     51        }
     52
     53        /**
    3554         * Test autosaving a post
    3655         * @return void
    3756         */
    3857        public function test_autosave_post() {
     58                // The original post_author
     59                wp_set_current_user( $this->user_id );
    3960
    40                 // Become an admin
    41                 $this->_setRole( 'administrator' );
    42 
    4361                // Set up the $_POST request
    4462                $md5 = md5( uniqid() );
    4563                $_POST = array(
    46                     'post_id'       => $this->_post->ID,
    47                     'autosavenonce' => wp_create_nonce( 'autosave' ),
    48                     'post_content'  => $this->_post->post_content . PHP_EOL . $md5,
    49                         'post_type'     => 'post',
    50                     'autosave'      => 1,
     64                        'action' =>     'heartbeat',
     65                        '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
     66                        'data' => array(
     67                                'autosave' => array(
     68                                    'post_id'       => $this->_post->ID,
     69                                    '_wpnonce'      => wp_create_nonce( 'update-post_' . $this->_post->ID ),
     70                                    'post_content'  => $this->_post->post_content . PHP_EOL . $md5,
     71                                        'post_type'     => 'post',
     72                                ),
     73                        ),
    5174                );
    5275
    5376                // Make the request
    5477                try {
    55                         $this->_handleAjax( 'autosave' );
     78                        $this->_handleAjax( 'heartbeat' );
    5679                } catch ( WPAjaxDieContinueException $e ) {
    5780                        unset( $e );
    5881                }
    5982
    60                 // Get the response
    61                 $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
     83                // Get the response, it is in heartbeat's response
     84                $response = json_decode( $this->_last_response, true );
    6285
    6386                // Ensure everything is correct
    64                 $this->assertEquals( $this->_post->ID, (int) $xml->response[0]->autosave['id'] );
    65                 $this->assertEquals( 'autosave_' . $this->_post->ID, (string) $xml->response['action']);
     87                $this->assertNotEmpty( $response['autosave'] );
     88                $this->assertTrue( $response['autosave']['success'] );
    6689
    6790                // Check that the edit happened
    68                 $post = get_post( $this->_post->ID) ;
     91                $post = get_post( $this->_post->ID );
    6992                $this->assertGreaterThanOrEqual( 0, strpos( $post->post_content, $md5 ) );
    7093        }
    71 
     94       
    7295        /**
    73          * Test with an invalid nonce
     96         * Test autosaving a locked post
    7497         * @return void
    7598         */
    76         public function test_with_invalid_nonce( ) {
     99        public function test_autosave_locked_post() {
     100                // Lock the post to another user
     101                $another_user_id = $this->factory->user->create( array( 'role' => 'editor' ) );
     102                wp_set_current_user( $another_user_id );
     103                wp_set_post_lock( $this->_post->ID );
     104               
     105                wp_set_current_user( $this->user_id );
    77106
    78                 // Become an administrator
    79                 $this->_setRole( 'administrator' );
     107                // Ensure post is locked
     108                $this->assertEquals( $another_user_id, wp_check_post_lock( $this->_post->ID ) );
    80109
    81110                // Set up the $_POST request
     111                $md5 = md5( uniqid() );
    82112                $_POST = array(
    83                     'post_id'       => $this->_post->ID,
    84                     'autosavenonce' => md5( uniqid() ),
    85                     'autosave'      => 1
     113                        'action' =>     'heartbeat',
     114                        '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
     115                        'data' => array(
     116                                'autosave' => array(
     117                                    'post_id'       => $this->_post->ID,
     118                                    '_wpnonce'      => wp_create_nonce( 'update-post_' . $this->_post->ID ),
     119                                    'post_content'  => $this->_post->post_content . PHP_EOL . $md5,
     120                                        'post_type'     => 'post',
     121                                ),
     122                        ),
    86123                );
    87124
    88125                // Make the request
    89                 $this->setExpectedException( 'WPAjaxDieStopException', '-1' );
    90                 $this->_handleAjax( 'autosave' );
     126                try {
     127                        $this->_handleAjax( 'heartbeat' );
     128                } catch ( WPAjaxDieContinueException $e ) {
     129                        unset( $e );
     130                }
     131
     132                $response = json_decode( $this->_last_response, true );
     133
     134                // Ensure everything is correct
     135                $this->assertNotEmpty( $response['autosave'] );
     136                $this->assertTrue( $response['autosave']['success'] );
     137
     138                // Check that the original post was NOT edited
     139                $post = get_post( $this->_post->ID );
     140                $this->assertFalse( strpos( $post->post_content, $md5 ) );
     141
     142                // Check if the autosave post was created
     143                $autosave = wp_get_post_autosave( $this->_post->ID, get_current_user_id() );
     144                $this->assertNotEmpty( $autosave );
     145                $this->assertGreaterThanOrEqual( 0, strpos( $autosave->post_content, $md5 ) );
    91146        }
    92147
    93148        /**
    94          * Test with a bad post id
     149         * Test with an invalid nonce
    95150         * @return void
    96151         */
    97         public function test_with_invalid_post_id( ) {
     152        public function test_with_invalid_nonce( ) {
    98153
    99                 // Become an administrator
    100                 $this->_setRole( 'administrator' );
     154                wp_set_current_user( $this->user_id );
    101155
    102156                // Set up the $_POST request
    103157                $_POST = array(
    104                     'post_id'       => 0,
    105                     'autosavenonce' => wp_create_nonce( 'autosave' ),
    106                     'autosave'      => 1,
    107                         'post_type'     => 'post'
     158                        'action' =>     'heartbeat',
     159                        '_nonce' => wp_create_nonce( 'heartbeat-nonce' ),
     160                        'data' => array(
     161                                'autosave' => array(
     162                                    'post_id'       => $this->_post->ID,
     163                                    '_wpnonce'      => substr( md5( uniqid() ), 0, 10 ),
     164                                ),
     165                        ),
    108166                );
    109167
    110168                // Make the request
    111                 $this->setExpectedException( 'WPAjaxDieStopException', 'You are not allowed to edit this post.' );
    112                 $this->_handleAjax( 'autosave' );
     169                try {
     170                        $this->_handleAjax( 'heartbeat' );
     171                } catch ( WPAjaxDieContinueException $e ) {
     172                        unset( $e );
     173                }
     174
     175                $response = json_decode( $this->_last_response, true );
     176
     177                $this->assertNotEmpty( $response['autosave'] );
     178                $this->assertFalse( $response['autosave']['success'] );
    113179        }
    114180}