Ticket #25272: 25272.4.patch
File 25272.4.patch, 57.9 KB (added by , 11 years ago) |
---|
-
src/wp-admin/admin-ajax.php
50 50 'oembed-cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link', 51 51 'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment', 52 52 'add-link-category', 'add-tag', 'get-tagcloud', 'get-comments', 'replyto-comment', 53 'edit-comment', 'add-menu-item', 'add-meta', 'add-user', ' autosave', 'closed-postboxes',53 'edit-comment', 'add-menu-item', 'add-meta', 'add-user', 'closed-postboxes', 54 54 'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax', 55 55 'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink', 56 56 'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order', -
src/wp-admin/edit-form-advanced.php
403 403 404 404 echo $form_extra; 405 405 406 wp_nonce_field( 'autosave', 'autosavenonce', false );407 406 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); 408 407 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); 409 408 ?> -
src/wp-admin/includes/ajax-actions.php
1084 1084 $x->send(); 1085 1085 } 1086 1086 1087 function wp_ajax_autosave() {1088 define( 'DOING_AUTOSAVE', true );1089 1090 check_ajax_referer( 'autosave', 'autosavenonce' );1091 1092 if ( ! empty( $_POST['catslist'] ) )1093 $_POST['post_category'] = explode( ',', $_POST['catslist'] );1094 if ( $_POST['post_type'] == 'page' || empty( $_POST['post_category'] ) )1095 unset( $_POST['post_category'] );1096 1097 $data = '';1098 $supplemental = array();1099 $id = $revision_id = 0;1100 1101 $post_id = (int) $_POST['post_id'];1102 $_POST['ID'] = $_POST['post_ID'] = $post_id;1103 $post = get_post( $post_id );1104 if ( empty( $post->ID ) || ! current_user_can( 'edit_post', $post->ID ) )1105 wp_die( __( 'You are not allowed to edit this post.' ) );1106 1107 if ( 'page' == $post->post_type && ! current_user_can( 'edit_page', $post->ID ) )1108 wp_die( __( 'You are not allowed to edit this page.' ) );1109 1110 if ( 'auto-draft' == $post->post_status )1111 $_POST['post_status'] = 'draft';1112 1113 if ( ! empty( $_POST['autosave'] ) ) {1114 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) {1115 // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked1116 $id = edit_post();1117 } else {1118 // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user.1119 $revision_id = wp_create_post_autosave( $post->ID );1120 if ( is_wp_error($revision_id) )1121 $id = $revision_id;1122 else1123 $id = $post->ID;1124 }1125 1126 if ( ! is_wp_error($id) ) {1127 /* translators: draft saved date format, see http://php.net/date */1128 $draft_saved_date_format = __('g:i:s a');1129 /* translators: %s: date and time */1130 $data = sprintf( __('Draft saved at %s.'), date_i18n( $draft_saved_date_format ) );1131 }1132 } else {1133 if ( ! empty( $_POST['auto_draft'] ) )1134 $id = 0; // This tells us it didn't actually save1135 else1136 $id = $post->ID;1137 }1138 1139 // @todo Consider exposing any errors, rather than having 'Saving draft...'1140 $x = new WP_Ajax_Response( array(1141 'what' => 'autosave',1142 'id' => $id,1143 'data' => $data,1144 'supplemental' => $supplemental1145 ) );1146 $x->send();1147 }1148 1149 1087 function wp_ajax_closed_postboxes() { 1150 1088 check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' ); 1151 1089 $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array(); -
src/wp-admin/includes/misc.php
724 724 if ( 2 === wp_verify_nonce( $received['post_nonce'], 'update-post_' . $post_id ) ) { 725 725 $response['wp-refresh-post-nonces'] = array( 726 726 'replace' => array( 727 'autosavenonce' => wp_create_nonce('autosave'),728 727 'getpermalinknonce' => wp_create_nonce('getpermalink'), 729 728 'samplepermalinknonce' => wp_create_nonce('samplepermalink'), 730 729 'closedpostboxesnonce' => wp_create_nonce('closedpostboxes'), … … 739 738 return $response; 740 739 } 741 740 add_filter( 'heartbeat_received', 'wp_refresh_post_nonces', 10, 3 ); 741 742 /** 743 * Autosave with heartbeat 744 * 745 * @since 3.8 746 */ 747 function heartbeat_autosave( $response, $data ) { 748 if ( ! empty( $data['wp_autosave'] ) ) { 749 $saved = wp_autosave( $data['wp_autosave'] ); 750 751 if ( is_wp_error( $saved ) ) { 752 $response['wp_autosave'] = array( 'success' => false, 'message' => $saved->get_error_message() ); 753 } elseif ( empty( $saved ) ) { 754 $response['wp_autosave'] = array( 'success' => false, 'message' => __( 'Error while saving.' ) ); 755 } else { 756 /* translators: draft saved date format, see http://php.net/date */ 757 $draft_saved_date_format = __( 'g:i:s a' ); 758 /* translators: %s: date and time */ 759 $response['wp_autosave'] = array( 'success' => true, 'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ) ); 760 } 761 } 762 763 return $response; 764 } 765 // Run later as we have to set DOING_AUTOSAVE for back-compat 766 add_filter( 'heartbeat_received', 'heartbeat_autosave', 50, 2 ); -
src/wp-admin/includes/post.php
79 79 } 80 80 } 81 81 82 if ( ! empty( $post_data['post_status'] ) ) 82 if ( ! empty( $post_data['post_status'] ) ) { 83 83 $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); 84 84 85 // No longer an auto-draft 86 if ( 'auto-draft' == $post_data['post_status'] ) 87 $post_data['post_status'] = 'draft'; 88 } 89 85 90 // What to do based on which button they pressed 86 91 if ( isset($post_data['saveasdraft']) && '' != $post_data['saveasdraft'] ) 87 92 $post_data['post_status'] = 'draft'; … … 190 195 $post_data = _wp_translate_postdata( true, $post_data ); 191 196 if ( is_wp_error($post_data) ) 192 197 wp_die( $post_data->get_error_message() ); 193 if ( ( empty( $post_data['action'] ) || 'autosave' != $post_data['action'] ) && 'auto-draft' == $post_data['post_status'] ) {194 $post_data['post_status'] = 'draft';195 }196 198 197 199 if ( isset($post_data['visibility']) ) { 198 200 switch ( $post_data['visibility'] ) { … … 1335 1337 * @uses _wp_translate_postdata() 1336 1338 * @uses _wp_post_revision_fields() 1337 1339 * 1338 * @return unknown 1340 * @param mixed $post_data Associative array containing the post data or int post ID. 1341 * @return mixed The autosave revision ID. WP_Error or 0 on error. 1339 1342 */ 1340 function wp_create_post_autosave( $post_id ) { 1341 $translated = _wp_translate_postdata( true ); 1342 if ( is_wp_error( $translated ) ) 1343 return $translated; 1343 function wp_create_post_autosave( $post_data ) { 1344 if ( is_numeric( $post_data ) ) { 1345 $post_id = $post_data; 1346 $post_data = &$_POST; 1347 } else { 1348 $post_id = (int) $post_data['post_ID']; 1349 } 1344 1350 1351 $post_data = _wp_translate_postdata( true, $post_data ); 1352 if ( is_wp_error( $post_data ) ) 1353 return $post_data; 1354 1345 1355 $post_author = get_current_user_id(); 1346 1356 1347 1357 // Store one autosave per author. If there is already an autosave, overwrite it. 1348 1358 if ( $old_autosave = wp_get_post_autosave( $post_id, $post_author ) ) { 1349 $new_autosave = _wp_post_revision_fields( $ _POST, true );1359 $new_autosave = _wp_post_revision_fields( $post_data, true ); 1350 1360 $new_autosave['ID'] = $old_autosave->ID; 1351 1361 $new_autosave['post_author'] = $post_author; 1352 1362 1353 // If the new autosave is the same content as the post, delete the oldautosave.1363 // If the new autosave has the same content as the post, delete the autosave. 1354 1364 $post = get_post( $post_id ); 1355 1365 $autosave_is_different = false; 1356 1366 foreach ( array_keys( _wp_post_revision_fields() ) as $field ) { … … 1362 1372 1363 1373 if ( ! $autosave_is_different ) { 1364 1374 wp_delete_post_revision( $old_autosave->ID ); 1365 return ;1375 return 0; 1366 1376 } 1367 1377 1368 1378 return wp_update_post( $new_autosave ); … … 1369 1379 } 1370 1380 1371 1381 // _wp_put_post_revision() expects unescaped. 1372 $post_data = wp_unslash( $ _POST);1382 $post_data = wp_unslash( $post_data ); 1373 1383 1374 1384 // Otherwise create the new autosave as a special post revision 1375 1385 return _wp_put_post_revision( $post_data, true ); … … 1395 1405 function post_preview() { 1396 1406 1397 1407 $post_ID = (int) $_POST['post_ID']; 1398 $status = get_post_status( $post_ID ); 1399 if ( 'auto-draft' == $status ) 1400 wp_die( __('Preview not available. Please save as a draft first.') ); 1408 $_POST['ID'] = $post_ID; 1401 1409 1402 if ( isset($_POST['catslist']) )1403 $_POST['post_category'] = explode(",", $_POST['catslist']);1410 if ( ! $post = get_post( $post_ID ) ) 1411 wp_die( __('You attempted to preview a non existing item.') ); 1404 1412 1405 if ( isset($_POST['tags_input']) )1406 $_POST['tags_input'] = explode(",", $_POST['tags_input']);1413 if ( ! current_user_can( 'edit_post', $post->ID ) ) 1414 wp_die( __('You are not allowed to preview this item.') ); 1407 1415 1408 if ( $_POST['post_type'] == 'page' || empty($_POST['post_category']) ) 1409 unset($_POST['post_category']); 1416 $is_autosave = false; 1410 1417 1411 $_POST['ID'] = $post_ID; 1412 $post = get_post($post_ID); 1413 1414 if ( 'page' == $post->post_type ) { 1415 if ( ! current_user_can('edit_page', $post_ID) ) 1416 wp_die( __('You are not allowed to edit this page.') ); 1418 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'draft' == $post->post_status || 'auto-draft' == $post->post_status ) ) { 1419 $saved_post_id = edit_post(); 1417 1420 } else { 1418 if ( ! current_user_can('edit_post', $post_ID) ) 1419 wp_die( __('You are not allowed to edit this post.') ); 1420 } 1421 $is_autosave = true; 1421 1422 1422 $user_id = get_current_user_id(); 1423 $locked = wp_check_post_lock( $post->ID ); 1424 if ( ! $locked && 'draft' == $post->post_status && $user_id == $post->post_author ) { 1425 $id = edit_post(); 1426 } else { // Non drafts are not overwritten. The autosave is stored in a special post revision. 1427 $id = wp_create_post_autosave( $post->ID ); 1428 if ( ! is_wp_error($id) ) 1429 $id = $post->ID; 1423 if ( 'auto-draft' == $_POST['post_status'] ) 1424 $_POST['post_status'] = 'draft'; 1425 1426 $saved_post_id = wp_create_post_autosave( $post->ID ); 1430 1427 } 1431 1428 1432 if ( is_wp_error( $id) )1433 wp_die( $ id->get_error_message() );1429 if ( is_wp_error( $saved_post_id ) ) 1430 wp_die( $saved_post_id->get_error_message() ); 1434 1431 1435 if ( ! $locked && $_POST['post_status'] == 'draft' && $user_id == $post->post_author ) { 1436 $url = add_query_arg( 'preview', 'true', get_permalink($id) ); 1437 } else { 1438 $nonce = wp_create_nonce('post_preview_' . $id); 1439 $args = array( 1440 'preview' => 'true', 1441 'preview_id' => $id, 1442 'preview_nonce' => $nonce, 1443 ); 1432 $query_args = array( 'preview' => 'true' ); 1444 1433 1434 if ( $is_autosave && $saved_post_id ) { 1435 $query_args['preview_id'] = $post->ID; 1436 $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID ); 1437 1445 1438 if ( isset( $_POST['post_format'] ) ) 1446 $args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); 1447 1448 $url = add_query_arg( $args, get_permalink($id) ); 1439 $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); 1449 1440 } 1450 1441 1442 $url = add_query_arg( $query_args, get_permalink( $post->ID ) ); 1451 1443 return apply_filters( 'preview_post_link', $url ); 1452 1444 } 1445 1446 /** 1447 * Save a post submitted with XHR 1448 * 1449 * Intended for use with heartbeat and autosave.js 1450 * 1451 * @since 3.8 1452 * 1453 * @param $post_data Associative array of the submitted post data. 1454 * @return mixed The value 0 or WP_Error on failure. The saved post ID on success. 1455 * Te ID can be the draft post_id or the autosave revision post_id. 1456 */ 1457 function wp_autosave( $post_data ) { 1458 // Back-compat 1459 if ( ! defined( 'DOING_AUTOSAVE' ) ) 1460 define( 'DOING_AUTOSAVE', true ); 1461 1462 $post_id = (int) $post_data['post_id']; 1463 $post_data['ID'] = $post_data['post_ID'] = $post_id; 1464 1465 if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) ) 1466 return new WP_Error( 'invalid_nonce', __('ERROR: invalid post data.') ); 1467 1468 $post = get_post( $post_id ); 1469 1470 if ( ! current_user_can( 'edit_post', $post->ID ) ) 1471 return new WP_Error( 'edit_post', __('You are not allowed to edit this item.') ); 1472 1473 if ( 'auto-draft' == $post->post_status ) 1474 $post_data['post_status'] = 'draft'; 1475 1476 if ( $post_data['post_type'] != 'page' && ! empty( $post_data['catslist'] ) ) 1477 $post_data['post_category'] = explode( ',', $post_data['catslist'] ); 1478 1479 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) { 1480 // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked 1481 return edit_post( $post_data ); 1482 } else { 1483 // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user. 1484 return wp_create_post_autosave( $post_data ); 1485 } 1486 } -
src/wp-admin/js/post.js
1 var tagBox, commentsBox, editPermalink, makeSlugeditClickable, WPSetThumbnailHTML, WPSetThumbnailID, WPRemoveThumbnail, wptitlehint; 1 var tagBox, commentsBox, WPSetThumbnailHTML, WPSetThumbnailID, WPRemoveThumbnail, wptitlehint; 2 // Prevent fatal errors (back-compat) 3 makeSlugeditClickable = editPermalink = function(){}; 2 4 3 5 // return an array with any duplicate, whitespace or values removed 4 6 function array_unique_noempty(a) { … … 265 267 send['lock'] = lock; 266 268 267 269 data['wp-refresh-post-lock'] = send; 268 });269 270 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 272 273 var received, wrap, avatar; 273 274 274 275 if ( data['wp-refresh-post-lock'] ) { … … 279 280 wrap = $('#post-lock-dialog'); 280 281 281 282 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(); 286 287 wrap.removeClass('saving').addClass('saved'); 287 window.onbeforeunload = null;288 $(window).off( 'beforeunload.autosave' ); 288 289 }); 289 290 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(); 295 293 } 296 294 297 295 if ( received.lock_error.avatar_src ) { … … 306 304 $('#active_post_lock').val( received.new_lock ); 307 305 } 308 306 } 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 } 309 323 }); 310 324 311 325 }(jQuery)); … … 351 365 }(jQuery)); 352 366 353 367 jQuery(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; 355 370 356 371 postboxes.add_postbox_toggles(pagenow); 357 372 … … 376 391 wp.heartbeat.setInterval( 15 ); 377 392 } 378 393 394 // The form is being submitted by the user 395 $submitpost.find( 'input[type="submit"], a.submitdelete' ).on( 'click.autosave', function( event ) { 396 var $button = $(this); 397 398 if ( $button.prop('disabled') ) { 399 event.preventDefault(); 400 return; 401 } 402 403 if ( $button.hasClass('submitdelete') ) { 404 return; 405 } 406 407 // The form submission can be blocked from JS or by using HTML 5.0 validation on some fields. 408 // Run this only on an actual 'submit'. 409 $('form#post').off( 'submit.edit-post' ).on( 'submit.edit-post', function( event ) { 410 if ( event.isDefaultPrevented() ) 411 return; 412 413 wp.autosave.server.disable(); 414 releaseLock = false; 415 $(window).off( 'beforeunload.edit-post' ); 416 417 $submitpost.find('a.submitdelete, #post-preview').addClass('button-disabled'); 418 419 if ( $button.attr('id') == 'publish' ) 420 $submitpost.find('#major-publishing-actions .spinner').show(); 421 else 422 $submitpost.find('#minor-publishing .spinner').show(); 423 }); 424 }); 425 426 // Submit the form saving a draft or an autosave, and show a preview in a new tab 427 $('#post-preview').on( 'click.post-preview', function( event ) { 428 var $this = $(this), 429 $form = $('form#post'), 430 $previewField = $('input#wp-preview'), 431 target = $this.attr('target') || 'wp-preview'; 432 433 event.preventDefault(); 434 435 if ( $this.prop('disabled') ) { 436 return; 437 } 438 439 wp.autosave.server.blockSave(); 440 $previewField.val('dopreview'); 441 $form.attr( 'target', target ).submit().attr( 'target', '' ); 442 443 // Workaround for WebKit bug preventing a form submitting twice to the same action. 444 // https://bugs.webkit.org/show_bug.cgi?id=28633 445 var ua = navigator.userAgent.toLowerCase(); 446 if ( ua.indexOf('safari') != -1 && ua.indexOf('chrome') == -1 ) { 447 $form.attr( 'action', function( index, value ) { 448 return value + '?t=' + new Date().getTime(); 449 }); 450 } 451 452 $previewField.val(''); 453 }); 454 455 // This code is meant to allow tabbing from Title to Post content. 456 $('#title').on( 'keydown.editor-focus', function( event ) { 457 var editor; 458 459 if ( event.which == 9 && ! event.ctrlKey && ! event.altKey && ! event.shiftKey ) { 460 editor = typeof tinymce != 'undefined' && tinymce.get('content'); 461 462 if ( editor && ! editor.isHidden() ) { 463 $(this).one( 'keyup', function() { 464 $('#content_tbl td.mceToolbar > a').focus(); 465 }); 466 } else { 467 $('#content').focus(); 468 } 469 470 event.preventDefault(); 471 } 472 }); 473 474 $(window).on( 'beforeunload.edit-post', function() { 475 var editor = typeof tinymce != 'undefined' && tinymce.activeEditor, compareString; 476 477 if ( editor && ! editor.isHidden() ) { 478 if ( editor.isDirty() ) 479 return autosaveL10n.saveAlert; 480 } else { 481 if ( fullscreen && fullscreen.settings.visible ) { 482 compareString = wp.autosave.getCompareString({ 483 post_title: $('#wp-fullscreen-title').val() || '', 484 content: $('#wp_mce_fullscreen').val() || '', 485 excerpt: $('#excerpt').val() || '' 486 }); 487 } else { 488 compareString = wp.autosave.getCompareString(); 489 } 490 491 if ( compareString !== wp.autosave.server.autosaveLast ) 492 return autosaveL10n.saveAlert; 493 } 494 }).on( 'unload.edit-post', function( event ) { 495 if ( ! releaseLock ) 496 return; 497 498 // Unload is triggered (by hand) on removing the Thickbox iframe. 499 // Make sure we process only the main document unload. 500 if ( event.target && event.target.nodeName != '#document' ) 501 return; 502 503 $.ajax({ 504 type: 'POST', 505 url: ajaxurl, 506 async: false, 507 data: { 508 action: 'wp-remove-post-lock', 509 _wpnonce: $('#_wpnonce').val(), 510 post_ID: $('#post_ID').val(), 511 active_post_lock: $('#active_post_lock').val() 512 } 513 }); 514 }); 515 379 516 // multi-taxonomies 380 517 if ( $('#tagsdiv-post_tag').length ) { 381 518 tagBox.init(); … … 685 822 } // end submitdiv 686 823 687 824 // permalink 688 if ( $('#edit-slug-box').length ) { 689 editPermalink = function(post_id) { 690 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(); 825 if ( $editSlugWrap.length ) { 826 function editPermalink() { 827 var i, c = 0, e = $('#editable-post-name'), revert_e = e.html(), real_slug = $('#post_name'), 828 revert_slug = real_slug.val(), b = $('#edit-slug-buttons'), revert_b = b.html(), 829 full = $('#editable-post-name-full').html(); 691 830 692 831 $('#view-post-btn').hide(); 693 832 b.html('<a href="#" class="save button button-small">'+postL10n.ok+'</a> <a class="cancel" href="#">'+postL10n.cancel+'</a>'); … … 694 833 b.children('.save').click(function() { 695 834 var new_slug = e.children('input').val(); 696 835 if ( new_slug == $('#editable-post-name-full').text() ) { 697 return $(' .cancel', '#edit-slug-buttons').click();836 return $('#edit-slug-buttons .cancel').click(); 698 837 } 699 838 $.post(ajaxurl, { 700 839 action: 'sample-permalink', 701 post_id: post _id,840 post_id: postId, 702 841 new_slug: new_slug, 703 842 new_title: $('#title').val(), 704 843 samplepermalinknonce: $('#samplepermalinknonce').val() … … 712 851 } 713 852 b.html(revert_b); 714 853 real_slug.val(new_slug); 715 makeSlugeditClickable();716 854 $('#view-post-btn').show(); 717 855 }); 718 856 return false; 719 857 }); 720 858 721 $(' .cancel', '#edit-slug-buttons').click(function() {859 $('#edit-slug-buttons .cancel').click(function() { 722 860 $('#view-post-btn').show(); 723 861 e.html(revert_e); 724 862 b.html(revert_b); … … 748 886 }).focus(); 749 887 } 750 888 751 makeSlugeditClickable = function() { 752 $('#editable-post-name').click(function() { 753 $('#edit-slug-buttons').children('.edit-slug').click(); 754 }); 755 } 756 makeSlugeditClickable(); 889 $editSlugWrap.on( 'click', function( event ) { 890 var $target = $( event.target ); 891 892 if ( $target.is('#editable-post-name') || $target.hasClass('edit-slug') ) { 893 editPermalink(); 894 } 895 }); 757 896 } 758 897 759 898 // word count -
src/wp-admin/post.php
304 304 break; 305 305 306 306 case 'preview': 307 check_admin_referer( ' autosave', 'autosavenonce');307 check_admin_referer( 'update-post_' . $post_id ); 308 308 309 309 $url = post_preview(); 310 310 -
src/wp-includes/js/autosave.js
1 var autosave, autosaveLast = '', autosavePeriodical, autosaveDelayPreview = false, notSaved = true, blockSave = false, fullscreen, autosaveLockRelease = true; 1 /* global tinymce, wpCookies, autosaveL10n, fullscreen, switchEditors */ 2 window.wp = window.wp || {}; 2 3 3 jQuery(document).ready( function($) { 4 ( function( $, window ) { 5 var _disabled, _blockSave, _blockSaveTimer, 6 $document = $(document), 7 nextRun = 0; 4 8 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 } 9 /** 10 * Returns the data saved in both local and remote autosave 11 * 12 * @return mixed Object containing the post data or false 13 */ 14 wp.autosave = { 15 getPostData: function( type ) { 16 var post_name, parent_id, data, 17 cats = [], 18 editor = typeof tinymce !== 'undefined' && tinymce.get('content'); 14 19 15 autosavePeriodical = $.schedule({time: autosaveL10n.autosaveInterval * 1000, func: function() { autosave(); }, repeat: true, protect: true});16 17 //Disable autosave after the form has been submitted18 $("#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 else31 t.addClass('button-disabled');32 });33 if ( $(this).attr('id') == 'publish' )34 $('#major-publishing-actions .spinner').show();35 else36 $('#minor-publishing .spinner').show();37 });38 39 window.onbeforeunload = function(){40 var editor = typeof(tinymce) != 'undefined' ? tinymce.activeEditor : false, compareString;41 42 20 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 }); 21 // Don't run while the tinymce spellcheck is on. It resets all found words. 22 if ( editor.plugins.spellchecker && editor.plugins.spellchecker.active ) { 23 return false; 52 24 } else { 53 compareString = wp.autosave.getCompareString();25 tinymce.triggerSave(); 54 26 } 55 56 if ( compareString != autosaveLast )57 return autosaveL10n.saveAlert;58 27 } 59 };60 28 61 $(window).unload( function(e) { 62 if ( ! autosaveLockRelease ) 63 return; 29 data = { 30 post_id: $( '#post_ID' ).val() || 0, 31 post_type: $( '#post_type' ).val() || '', 32 post_author: $( '#post_author' ).val() || '', 33 excerpt: $( '#excerpt' ).val() || '' 34 }; 64 35 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; 36 if ( typeof fullscreen !== 'undefined' && fullscreen.settings.visible ) { 37 data.post_title = $( '#wp-fullscreen-title' ).val() || ''; 38 data.content = $( '#wp_mce_fullscreen' ).val() || ''; 39 } else { 40 data.post_title = $( '#title' ).val() || ''; 41 data.content = $( '#content' ).val() || ''; 88 42 } 89 doPreview();90 return false;91 });92 43 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 }); 44 if ( type === 'local' ) { 45 return data; 106 46 } 107 47 108 $('input#wp-preview').val(''); 109 } 48 $( 'input[id^="in-category-"]:checked' ).each( function() { 49 cats.push( this.value ); 50 }); 51 data.catslist = cats.join(','); 110 52 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(); 53 if ( post_name = $( '#post_name' ).val() ) { 54 data.post_name = post_name; 131 55 } 132 });133 56 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, status ) { 145 if ( 'timeout' === error || 503 == status ) { 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(); 57 if ( parent_id = $( '#parent_id' ).val() ) { 58 data.parent_id = parent_id; 152 59 } 153 }).on('heartbeat-connection-restored.autosave', function() {154 $('#lost-connection-notice').hide();155 autosave_enable_buttons();156 });157 });158 60 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 }); 61 if ( $( '#comment_status' ).prop( 'checked' ) ) { 62 data.comment_status = 'open'; 170 63 } 171 64 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 ); 65 if ( $( '#ping_status' ).prop( 'checked' ) ) { 66 data.ping_status = 'open'; 179 67 } 180 }181 68 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 69 if ( $( '#auto_draft' ).val() === '1' ) { 70 data.auto_draft = '1'; 204 71 } 205 72 206 autosave_enable_buttons(); 73 return data; 74 }, 207 75 208 if ( autosaveDelayPreview ) { 209 autosaveDelayPreview = false; 210 doPreview(); 76 // Concatenate title, content and excerpt. Used to track changes when auto-saving. 77 getCompareString: function( post_data ) { 78 if ( typeof post_data === 'object' ) { 79 return ( post_data.post_title || '' ) + '::' + ( post_data.content || '' ) + '::' + ( post_data.excerpt || '' ); 211 80 } 212 } else {213 autosave_enable_buttons(); // re-enable disabled form buttons214 }215 }216 81 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 } 82 return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' ); 83 }, 241 84 242 function autosave_loading() { 243 jQuery('.autosave-message').html(autosaveL10n.savingText); 244 } 85 enableButtons: function() { 86 $document.trigger( 'autosave-enable-buttons' ); 245 87 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 } 88 if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) { 89 $( '#submitpost' ).find( ':button, :submit, a.submitdelete, #post-preview' ).prop( 'disabled', false ).removeClass( 'button-disabled' ); 90 } 91 }, 257 92 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 93 disableButtons: function() { 94 $document.trigger('autosave-disable-buttons'); 95 $( '#submitpost' ).find( ':button, :submit, a.submitdelete, #post-preview' ).prop( 'disabled', true ).addClass( 'button-disabled' ); 96 // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions. 97 setTimeout( wp.autosave.enableButtons, 5000 ); 304 98 } 99 }; 305 100 306 jQuery.ajax({307 data: post_data,308 beforeSend: autosave_loading,309 type: "POST",310 url: ajaxurl,311 success: successCallback312 });313 314 return true;315 }316 317 101 // Autosave in localStorage 318 102 // 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 server324 wp.autosave.getPostData = function() {325 var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [],326 data = {327 action: 'autosave',328 autosave: true,329 post_id: $('#post_ID').val() || 0,330 autosavenonce: $('#autosavenonce').val() || '',331 post_type: $('#post_type').val() || '',332 post_author: $('#post_author').val() || '',333 excerpt: $('#excerpt').val() || ''334 };335 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;341 } else {342 if ( 'mce_fullscreen' == ed.id )343 tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});344 345 tinymce.triggerSave();346 }347 }348 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 }356 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 */363 364 $('input[id^="in-category-"]:checked').each( function() {365 cats.push(this.value);366 });367 data['catslist'] = cats.join(',');368 369 if ( post_name = $('#post_name').val() )370 data['post_name'] = post_name;371 372 if ( parent_id = $('#parent_id').val() )373 data['parent_id'] = parent_id;374 375 if ( $('#comment_status').prop('checked') )376 data['comment_status'] = 'open';377 378 if ( $('#ping_status').prop('checked') )379 data['ping_status'] = 'open';380 381 if ( $('#auto_draft').val() == '1' )382 data['auto_draft'] = '1';383 384 return data;385 };386 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 || '' );391 }392 393 return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' );394 };395 396 103 wp.autosave.local = { 397 398 104 lastSavedData: '', 399 105 blog_id: 0, 400 106 hasStorage: false, … … 404 110 var test = Math.random(), result = false; 405 111 406 112 try { 407 sessionStorage.setItem( 'wp-test', test);408 result = sessionStorage.getItem( 'wp-test')== test;409 sessionStorage.removeItem( 'wp-test');113 sessionStorage.setItem( 'wp-test', test ); 114 result = sessionStorage.getItem( 'wp-test' ) === test; 115 sessionStorage.removeItem( 'wp-test' ); 410 116 } catch(e) {} 411 117 412 118 this.hasStorage = result; … … 424 130 if ( this.hasStorage && this.blog_id ) { 425 131 stored_obj = sessionStorage.getItem( 'wp-autosave-' + this.blog_id ); 426 132 427 if ( stored_obj ) 133 if ( stored_obj ) { 428 134 stored_obj = JSON.parse( stored_obj ); 429 else135 } else { 430 136 stored_obj = {}; 137 } 431 138 } 432 139 433 140 return stored_obj; … … 460 167 getData: function() { 461 168 var stored = this.getStorage(), post_id = $('#post_ID').val(); 462 169 463 if ( !stored || !post_id ) 170 if ( !stored || !post_id ) { 464 171 return false; 172 } 465 173 466 174 return stored[ 'post_' + post_id ] || false; 467 175 }, … … 477 185 setData: function( stored_data ) { 478 186 var stored = this.getStorage(), post_id = $('#post_ID').val(); 479 187 480 if ( !stored || !post_id ) 188 if ( !stored || !post_id ) { 481 189 return false; 190 } 482 191 483 if ( stored_data ) 192 if ( stored_data ) { 484 193 stored[ 'post_' + post_id ] = stored_data; 485 else if ( stored.hasOwnProperty( 'post_' + post_id ) )194 } else if ( stored.hasOwnProperty( 'post_' + post_id ) ) { 486 195 delete stored[ 'post_' + post_id ]; 487 else196 } else { 488 197 return false; 198 } 489 199 490 200 return this.setStorage(stored); 491 201 }, … … 502 212 save: function( data ) { 503 213 var result = false, post_data, compareString; 504 214 505 if ( ! data ) { 506 post_data = wp.autosave.getPostData(); 507 } else { 215 if ( data ) { 508 216 post_data = this.getData() || {}; 509 217 $.extend( post_data, data ); 510 post_data.autosave = true; 218 } else { 219 post_data = wp.autosave.getPostData('local'); 220 // Cannot get the post data at the moment 221 if ( ! post_data ) { 222 return false; 223 } 511 224 } 512 225 513 // Cannot get the post data at the moment514 if ( ! post_data.autosave )515 return false;516 517 226 compareString = wp.autosave.getCompareString( post_data ); 518 227 519 228 // If the content, title and excerpt did not change since the last save, don't save again 520 if ( compareString == this.lastSavedData )229 if ( compareString === this.lastSavedData ) { 521 230 return false; 231 } 522 232 523 post_data ['save_time'] = (new Date()).getTime();524 post_data ['status'] = $('#post_status').val() || '';233 post_data.save_time = ( new Date() ).getTime(); 234 post_data.status = $( '#post_status' ).val() || ''; 525 235 result = this.setData( post_data ); 526 236 527 if ( result ) 237 if ( result ) { 528 238 this.lastSavedData = compareString; 239 } 529 240 530 241 return result; 531 242 }, … … 535 246 var self = this; 536 247 537 248 // Check if the browser supports sessionStorage and it's not disabled 538 if ( ! this.checkStorage() ) 249 if ( ! this.checkStorage() ) { 539 250 return; 251 } 540 252 541 253 // Don't run if the post type supports neither 'editor' (textarea#content) nor 'excerpt'. 542 if ( ! $('#content').length && ! $('#excerpt').length ) 254 if ( ! $('#content').length && ! $('#excerpt').length ) { 543 255 return; 256 } 544 257 545 if ( settings ) 258 if ( settings ) { 546 259 $.extend( this, settings ); 260 } 547 261 548 if ( !this.blog_id ) 549 this.blog_id = typeof window.autosaveL10n != 'undefined' ? window.autosaveL10n.blog_id : 0; 262 if ( !this.blog_id ) { 263 this.blog_id = typeof window.autosaveL10n !== 'undefined' ? window.autosaveL10n.blog_id : 0; 264 } 550 265 551 266 $(document).ready( function(){ self.run(); } ); 552 267 }, … … 566 281 protect: true 567 282 }); 568 283 569 $('form#post').on('submit.autosave-local', function() { 570 var editor = typeof tinymce != 'undefined' && tinymce.get('content'), post_id = $('#post_ID').val() || 0; 284 $( 'form#post' ).on( 'submit.autosave-local', function() { 285 var editor = typeof tinymce !== 'undefined' && tinymce.get('content'), 286 post_id = $('#post_ID').val() || 0; 571 287 572 288 if ( editor && ! editor.isHidden() ) { 573 289 // Last onSubmit event in the editor, needs to run after the content has been moved to the textarea. 574 290 editor.onSubmit.add( function() { 575 291 wp.autosave.local.save({ 576 post_title: $( '#title').val() || '',577 content: $( '#content').val() || '',578 excerpt: $( '#excerpt').val() || ''292 post_title: $( '#title' ).val() || '', 293 content: $( '#content' ).val() || '', 294 excerpt: $( '#excerpt' ).val() || '' 579 295 }); 580 296 }); 581 297 } else { 582 298 self.save({ 583 post_title: $( '#title').val() || '',584 content: $( '#content').val() || '',585 excerpt: $( '#excerpt').val() || ''299 post_title: $( '#title' ).val() || '', 300 content: $( '#content' ).val() || '', 301 excerpt: $( '#excerpt' ).val() || '' 586 302 }); 587 303 } 588 304 … … 592 308 593 309 // Strip whitespace and compare two strings 594 310 compare: function( str1, str2 ) { 595 function remove ( string ) {311 function removeSpaces( string ) { 596 312 return string.toString().replace(/[\x20\t\r\n\f]+/g, ''); 597 313 } 598 314 599 return ( remove ( str1 || '' ) == remove( str2 || '' ) );315 return ( removeSpaces( str1 || '' ) === removeSpaces( str2 || '' ) ); 600 316 }, 601 317 602 318 /** … … 607 323 * @return void 608 324 */ 609 325 checkPost: function() { 610 var self = this, post_data = this.getData(), content, post_title, excerpt, notice, 611 post_id = $('#post_ID').val() || 0, cookie = wpCookies.get( 'wp-saving-post-' + post_id ); 326 var content, post_title, excerpt, notice, 327 self = this, 328 post_data = this.getData(), 329 post_id = $( '#post_ID' ).val() || 0, 330 cookie = wpCookies.get( 'wp-saving-post-' + post_id ); 612 331 613 if ( ! post_data ) 332 if ( ! post_data ) { 614 333 return; 334 } 615 335 616 336 if ( cookie ) { 617 337 wpCookies.remove( 'wp-saving-post-' + post_id ); 618 338 619 if ( cookie == 'saved' ) {339 if ( cookie === 'saved' ) { 620 340 // The post was saved properly, remove old data and bail 621 341 this.setData( false ); 622 342 return; … … 624 344 } 625 345 626 346 // There is a newer autosave. Don't show two "restore" notices at the same time. 627 if ( $( '#has-newer-autosave').length )347 if ( $( '#has-newer-autosave' ).length ) { 628 348 return; 349 } 629 350 630 content = $( '#content').val() || '';631 post_title = $( '#title').val() || '';632 excerpt = $( '#excerpt').val() || '';351 content = $( '#content' ).val() || ''; 352 post_title = $( '#title' ).val() || ''; 353 excerpt = $( '#excerpt' ).val() || ''; 633 354 634 if ( $( '#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' )355 if ( $( '#wp-content-wrap' ).hasClass( 'tmce-active' ) && typeof switchEditors !== 'undefined' ) { 635 356 content = switchEditors.pre_wpautop( content ); 357 } 636 358 637 359 // cookie == 'check' means the post was not saved properly, always show #local-storage-notice 638 if ( cookie != 'check' && this.compare( content, post_data.content ) && this.compare( post_title, post_data.post_title ) && this.compare( excerpt, post_data.excerpt ) ) { 360 if ( cookie !== 'check' && this.compare( content, post_data.content ) && 361 this.compare( post_title, post_data.post_title ) && this.compare( excerpt, post_data.excerpt ) ) { 639 362 return; 640 363 } 641 364 … … 646 369 excerpt: excerpt 647 370 }; 648 371 649 notice = $( '#local-storage-notice');650 $('.wrap h2').first().after( notice.addClass( 'updated').show() );372 notice = $( '#local-storage-notice' ); 373 $('.wrap h2').first().after( notice.addClass( 'updated' ).show() ); 651 374 652 notice.on( 'click ', function(e) {375 notice.on( 'click.autosae-local', function(e) { 653 376 var target = $( e.target ); 654 377 655 if ( target.hasClass( 'restore-backup') ) {378 if ( target.hasClass( 'restore-backup' ) ) { 656 379 self.restorePost( self.restore_post_data ); 657 380 target.parent().hide(); 658 $(this).find( 'p.undo-restore').show();659 } else if ( target.hasClass( 'undo-restore-backup') ) {381 $(this).find( 'p.undo-restore' ).show(); 382 } else if ( target.hasClass( 'undo-restore-backup' ) ) { 660 383 self.restorePost( self.undo_post_data ); 661 384 target.parent().hide(); 662 $(this).find( 'p.local-restore').show();385 $(this).find( 'p.local-restore' ).show(); 663 386 } 664 387 665 388 e.preventDefault(); … … 674 397 // Set the last saved data 675 398 this.lastSavedData = wp.autosave.getCompareString( post_data ); 676 399 677 if ( $('#title').val() != post_data.post_title ) 678 $('#title').focus().val( post_data.post_title || '' ); 400 if ( $( '#title' ).val() !== post_data.post_title ) { 401 $( '#title' ).focus().val( post_data.post_title || '' ); 402 } 679 403 680 $( '#excerpt').val( post_data.excerpt || '' );681 editor = typeof tinymce != 'undefined' && tinymce.get('content');404 $( '#excerpt' ).val( post_data.excerpt || '' ); 405 editor = typeof tinymce !== 'undefined' && tinymce.get('content'); 682 406 683 if ( editor && ! editor.isHidden() && typeof switchEditors != 'undefined' ) {407 if ( editor && ! editor.isHidden() && typeof switchEditors !== 'undefined' ) { 684 408 // Make sure there's an undo level in the editor 685 409 editor.undoManager.add(); 686 410 editor.setContent( post_data.content ? switchEditors.wpautop( post_data.content ) : '' ); 687 411 } else { 688 412 // Make sure the Text editor is selected 689 $( '#content-html').click();690 $( '#content').val( post_data.content );413 $( '#content-html' ).click(); 414 $( '#content' ).val( post_data.content ); 691 415 } 692 416 693 417 return true; … … 697 421 } 698 422 }; 699 423 424 // Autosave on the server 425 wp.autosave.server = { 426 autosaveLast: '', 427 428 init: function() { 429 var self = this; 430 431 $document.on( 'heartbeat-send.autosave', function( event, data ) { 432 var autosaveData = self.getData(); 433 434 if ( autosaveData ) { 435 data.wp_autosave = autosaveData; 436 } 437 }).on( 'heartbeat-tick.autosave', function( event, data ) { 438 if ( data.wp_autosave ) { 439 self.response( data.wp_autosave ); 440 } 441 }).on( 'heartbeat-connection-lost.autosave', function( event, error, status ) { 442 // When connection is lost, keep user from submitting changes. 443 if ( 'timeout' === error || 603 === status ) { 444 var notice = $('#lost-connection-notice'); 445 446 if ( ! wp.autosave.local.hasStorage ) { 447 notice.find('.hide-if-no-sessionstorage').hide(); 448 } 449 450 notice.show(); 451 wp.autosave.disableButtons(); 452 } 453 }).on( 'heartbeat-connection-restored.autosave', function() { 454 $('#lost-connection-notice').hide(); 455 wp.autosave.enableButtons(); 456 }).ready( function() { 457 self._schedule(); 458 459 if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors !== 'undefined' ) { 460 self.autosaveLast = wp.autosave.getCompareString({ 461 post_title : $( '#title' ).val() || '', 462 content : switchEditors.pre_wpautop( $( '#content' ).val() ) || '', 463 excerpt : $( '#excerpt' ).val() || '' 464 }); 465 } else { 466 self.autosaveLast = wp.autosave.getCompareString(); 467 } 468 469 // Autosave new posts after a title is typed 470 if ( $( '#auto_draft' ).val() ) { 471 $( '#title' ).blur( function() { 472 if ( ! this.value || $( '#auto_draft' ).val() !== '1' ) { 473 return; 474 } 475 476 self.triggerSave(); 477 }); 478 } 479 }); 480 }, 481 482 // Block saving for the next 10 sec. 483 blockSave: function() { 484 _blockSave = true; 485 window.clearTimeout( _blockSaveTimer ); 486 _blockSaveTimer = window.setTimeout( function() { 487 _blockSave = false; 488 }, 10000 ); 489 }, 490 491 // Runs on heartbeat-response 492 response: function( data ) { 493 this._schedule(); 494 _blockSave = false; 495 this.autosaveLast = this.lastCompareString; 496 this.lastCompareString = ''; 497 498 $document.trigger( 'after-autosave', [data] ); 499 $( '.autosave-message' ).text( data.message ); 500 wp.autosave.enableButtons(); 501 502 if ( data.success ) { 503 // No longer an auto-draft 504 $( '#auto_draft' ).val(''); 505 } 506 }, 507 508 disable: function() { 509 _disabled = true; 510 }, 511 512 _schedule: function() { 513 nextRun = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000; 514 }, 515 516 // Reset the timing and tell heartbeat to connect now 517 triggerSave: function() { 518 nextRun = 0; 519 wp.heartbeat.connectNow(); 520 }, 521 522 // Runs on 'heartbeat-send' 523 getData: function() { 524 var compareString, postData; 525 526 if ( _disabled || _blockSave ) { 527 return false; 528 } 529 530 if ( ( new Date() ).getTime() < nextRun ) { 531 return false; 532 } 533 534 postData = wp.autosave.getPostData(); 535 536 // post_data.content cannot be retrieved at the moment? 537 if ( ! postData ) { 538 return false; 539 } 540 541 compareString = wp.autosave.getCompareString( postData ); 542 543 // Nothing to save or no change. 544 if ( compareString === this.autosaveLast ) { 545 return false; 546 } 547 548 this.blockSave(); 549 wp.autosave.disableButtons(); 550 this.lastCompareString = compareString; 551 552 $document.trigger( 'wpcountwords', [ postData.content ] ) 553 .trigger( 'before-autosave', [ postData ] ); 554 555 $( '.autosave-message' ).text( autosaveL10n.savingText ); 556 postData._wpnonce = $( '#_wpnonce' ).val() || ''; 557 558 return postData; 559 } 560 }; 561 700 562 wp.autosave.local.init(); 563 wp.autosave.server.init(); 701 564 702 }( jQuery));565 }( jQuery, window )); -
src/wp-includes/revision.php
215 215 * 216 216 * @param int|object|array $post Post ID, post object OR post array. 217 217 * @param bool $autosave Optional. Is the revision an autosave? 218 * @return mixed Nullor 0 if error, new revision ID if success.218 * @return mixed WP_Error or 0 if error, new revision ID if success. 219 219 */ 220 220 function _wp_put_post_revision( $post = null, $autosave = false ) { 221 221 if ( is_object($post) ) … … 223 223 elseif ( !is_array($post) ) 224 224 $post = get_post($post, ARRAY_A); 225 225 226 if ( ! $post || empty($post['ID']) )227 return ;226 if ( ! $post || empty($post['ID']) ) 227 return new WP_Error( 'invalid_post', __( 'Invalid post ID' ) ); 228 228 229 229 if ( isset($post['post_type']) && 'revision' == $post['post_type'] ) 230 230 return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) ); -
tests/phpunit/includes/testcase-ajax.php
42 42 'oembed_cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link', 43 43 'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment', 44 44 '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', 46 46 'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax', 47 47 'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink', 48 48 'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order', 49 49 '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', 51 51 ); 52 52 53 53 /** -
tests/phpunit/tests/ajax/Autosave.php
23 23 protected $_post = null; 24 24 25 25 /** 26 * user_id 27 * @var int 28 */ 29 protected $user_id = 0; 30 31 /** 26 32 * Set up the test fixture 27 33 */ 28 34 public function setUp() { 29 35 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 30 40 $post_id = $this->factory->post->create( array( 'post_status' => 'draft' ) ); 31 41 $this->_post = get_post( $post_id ); 32 42 } 33 43 34 44 /** 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 /** 35 54 * Test autosaving a post 36 55 * @return void 37 56 */ 38 57 public function test_autosave_post() { 58 // The original post_author 59 wp_set_current_user( $this->user_id ); 39 60 40 // Become an admin41 $this->_setRole( 'administrator' );42 43 61 // Set up the $_POST request 44 62 $md5 = md5( uniqid() ); 45 63 $_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 'wp_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 ), 51 74 ); 52 75 53 76 // Make the request 54 77 try { 55 $this->_handleAjax( ' autosave' );78 $this->_handleAjax( 'heartbeat' ); 56 79 } catch ( WPAjaxDieContinueException $e ) { 57 80 unset( $e ); 58 81 } 59 82 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 ); 62 85 63 86 // Ensure everything is correct 64 $this->assert Equals( $this->_post->ID, (int) $xml->response[0]->autosave['id'] );65 $this->assert Equals( 'autosave_' . $this->_post->ID, (string) $xml->response['action']);87 $this->assertNotEmpty( $response['wp_autosave'] ); 88 $this->assertTrue( $response['wp_autosave']['success'] ); 66 89 67 90 // Check that the edit happened 68 $post = get_post( $this->_post->ID );91 $post = get_post( $this->_post->ID ); 69 92 $this->assertGreaterThanOrEqual( 0, strpos( $post->post_content, $md5 ) ); 70 93 } 71 94 72 95 /** 73 * Test with an invalid nonce96 * Test autosaving a locked post 74 97 * @return void 75 98 */ 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 ); 77 106 78 // Become an administrator79 $this-> _setRole( 'administrator');107 // Ensure post is locked 108 $this->assertEquals( $another_user_id, wp_check_post_lock( $this->_post->ID ) ); 80 109 81 110 // Set up the $_POST request 111 $md5 = md5( uniqid() ); 82 112 $_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 'wp_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 ), 86 123 ); 87 124 88 125 // 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['wp_autosave'] ); 136 $this->assertTrue( $response['wp_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 ) ); 91 146 } 92 147 93 148 /** 94 * Test with a bad post id149 * Test with an invalid nonce 95 150 * @return void 96 151 */ 97 public function test_with_invalid_ post_id( ) {152 public function test_with_invalid_nonce( ) { 98 153 99 // Become an administrator 100 $this->_setRole( 'administrator' ); 154 wp_set_current_user( $this->user_id ); 101 155 102 156 // Set up the $_POST request 103 157 $_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 'wp_autosave' => array( 162 'post_id' => $this->_post->ID, 163 '_wpnonce' => substr( md5( uniqid() ), 0, 10 ), 164 ), 165 ), 108 166 ); 109 167 110 168 // 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['wp_autosave'] ); 178 $this->assertFalse( $response['wp_autosave']['success'] ); 113 179 } 114 180 }