Ticket #25272: 25272.2.patch
File 25272.2.patch, 42.7 KB (added by , 11 years ago) |
---|
-
src/wp-admin/admin-ajax.php
49 49 'oembed-cache', 'image-editor', 'delete-comment', 'delete-tag', 'delete-link', 50 50 'delete-meta', 'delete-post', 'trash-post', 'untrash-post', 'delete-page', 'dim-comment', 51 51 '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', 53 53 'hidden-columns', 'update-welcome-panel', 'menu-get-metabox', 'wp-link-ajax', 54 54 'menu-locations-save', 'menu-quick-search', 'meta-box-order', 'get-permalink', 55 55 'sample-permalink', 'inline-save', 'inline-save-tax', 'find_posts', 'widgets-order', -
src/wp-admin/edit-form-advanced.php
347 347 348 348 echo $form_extra; 349 349 350 wp_nonce_field( 'autosave', 'autosavenonce', false );351 350 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); 352 351 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); 353 352 ?> -
src/wp-admin/includes/ajax-actions.php
1042 1042 $x->send(); 1043 1043 } 1044 1044 1045 function wp_ajax_autosave() {1046 define( 'DOING_AUTOSAVE', true );1047 1048 check_ajax_referer( 'autosave', 'autosavenonce' );1049 1050 $_POST['post_category'] = explode(",", $_POST['catslist']);1051 if ( $_POST['post_type'] == 'page' || empty($_POST['post_category']) )1052 unset($_POST['post_category']);1053 1054 $data = '';1055 $supplemental = array();1056 $id = $revision_id = 0;1057 1058 $post_id = (int) $_POST['post_id'];1059 $_POST['ID'] = $_POST['post_ID'] = $post_id;1060 $post = get_post($post_id);1061 if ( 'auto-draft' == $post->post_status )1062 $_POST['post_status'] = 'draft';1063 1064 if ( 'page' == $post->post_type ) {1065 if ( !current_user_can('edit_page', $post->ID) )1066 wp_die( __( 'You are not allowed to edit this page.' ) );1067 } else {1068 if ( !current_user_can('edit_post', $post->ID) )1069 wp_die( __( 'You are not allowed to edit this post.' ) );1070 }1071 1072 if ( ! empty( $_POST['autosave'] ) ) {1073 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) {1074 // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked1075 $id = edit_post();1076 } else {1077 // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user.1078 $revision_id = wp_create_post_autosave( $post->ID );1079 if ( is_wp_error($revision_id) )1080 $id = $revision_id;1081 else1082 $id = $post->ID;1083 }1084 1085 if ( ! is_wp_error($id) ) {1086 /* translators: draft saved date format, see http://php.net/date */1087 $draft_saved_date_format = __('g:i:s a');1088 /* translators: %s: date and time */1089 $data = sprintf( __('Draft saved at %s.'), date_i18n( $draft_saved_date_format ) );1090 }1091 } else {1092 if ( ! empty( $_POST['auto_draft'] ) )1093 $id = 0; // This tells us it didn't actually save1094 else1095 $id = $post->ID;1096 }1097 1098 // @todo Consider exposing any errors, rather than having 'Saving draft...'1099 $x = new WP_Ajax_Response( array(1100 'what' => 'autosave',1101 'id' => $id,1102 'data' => $data,1103 'supplemental' => $supplemental1104 ) );1105 $x->send();1106 }1107 1108 1045 function wp_ajax_closed_postboxes() { 1109 1046 check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' ); 1110 1047 $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array(); -
src/wp-admin/includes/misc.php
650 650 if ( 2 === wp_verify_nonce( $received['post_nonce'], 'update-post_' . $post_id ) ) { 651 651 $response['wp-refresh-post-nonces'] = array( 652 652 'replace' => array( 653 'autosavenonce' => wp_create_nonce('autosave'),654 653 'getpermalinknonce' => wp_create_nonce('getpermalink'), 655 654 'samplepermalinknonce' => wp_create_nonce('samplepermalink'), 656 655 'closedpostboxesnonce' => wp_create_nonce('closedpostboxes'), … … 665 664 return $response; 666 665 } 667 666 add_filter( 'heartbeat_received', 'wp_refresh_post_nonces', 10, 3 ); 667 668 /** 669 * Autosave with heartbeat 670 * 671 * @since 3.7 672 */ 673 function wp_autosave( $response, $data ) { 674 if ( ! empty( $data['autosave'] ) ) { 675 $saved = autosave( $data['autosave'] ); 676 677 if ( is_wp_error( $saved ) ) { 678 $response['autosave'] = array( 'success' => false, 'message' => $saved->get_error_message() ); 679 } elseif ( empty( $saved ) ) { 680 $response['autosave'] = array( 'success' => false, 'message' => __( 'Error while saving the draft.' ) ); 681 } else { 682 /* translators: draft saved date format, see http://php.net/date */ 683 $draft_saved_date_format = __( 'g:i:s a' ); 684 /* translators: %s: date and time */ 685 $response['autosave'] = array( 'success' => true, 'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ) ); 686 } 687 } 688 689 return $response; 690 } 691 // Run later as we have to set DOING_AUTOSAVE for back-compat 692 add_filter( 'heartbeat_received', 'wp_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'; … … 181 186 $post_data = _wp_translate_postdata( true, $post_data ); 182 187 if ( is_wp_error($post_data) ) 183 188 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 }187 189 188 190 if ( isset($post_data['visibility']) ) { 189 191 switch ( $post_data['visibility'] ) { … … 1312 1314 * @uses _wp_translate_postdata() 1313 1315 * @uses _wp_post_revision_fields() 1314 1316 * 1315 * @return unknown 1317 * @param mixed $post_data Associative array containing the post data or int post ID. 1318 * @return mixed The autosave revision ID. WP_Error or 0 on error. 1316 1319 */ 1317 function wp_create_post_autosave( $post_id ) { 1318 $translated = _wp_translate_postdata( true ); 1319 if ( is_wp_error( $translated ) ) 1320 return $translated; 1320 function wp_create_post_autosave( $post_data ) { 1321 if ( is_numeric( $post_data ) ) { 1322 $post_id = $post_data; 1323 $post_data = &$_POST; 1324 } else { 1325 $post_id = (int) $post_data['post_ID']; 1326 } 1321 1327 1328 $post_data = _wp_translate_postdata( true, $post_data ); 1329 if ( is_wp_error( $post_data ) ) 1330 return $post_data; 1331 1322 1332 $post_author = get_current_user_id(); 1323 1333 1324 1334 // Store one autosave per author. If there is already an autosave, overwrite it. 1325 1335 if ( $old_autosave = wp_get_post_autosave( $post_id, $post_author ) ) { 1326 $new_autosave = _wp_post_revision_fields( $ _POST, true );1336 $new_autosave = _wp_post_revision_fields( $post_data, true ); 1327 1337 $new_autosave['ID'] = $old_autosave->ID; 1328 1338 $new_autosave['post_author'] = $post_author; 1329 1339 1330 // If the new autosave is the same content as the post, delete the oldautosave.1340 // If the new autosave has the same content as the post, delete the autosave. 1331 1341 $post = get_post( $post_id ); 1332 1342 $autosave_is_different = false; 1333 1343 foreach ( array_keys( _wp_post_revision_fields() ) as $field ) { … … 1339 1349 1340 1350 if ( ! $autosave_is_different ) { 1341 1351 wp_delete_post_revision( $old_autosave->ID ); 1342 return ;1352 return 0; 1343 1353 } 1344 1354 1345 1355 return wp_update_post( $new_autosave ); … … 1346 1356 } 1347 1357 1348 1358 // _wp_put_post_revision() expects unescaped. 1349 $post_data = wp_unslash( $ _POST);1359 $post_data = wp_unslash( $post_data ); 1350 1360 1351 1361 // Otherwise create the new autosave as a special post revision 1352 1362 return _wp_put_post_revision( $post_data, true ); … … 1372 1382 function post_preview() { 1373 1383 1374 1384 $post_ID = (int) $_POST['post_ID']; 1375 $status = get_post_status( $post_ID ); 1376 if ( 'auto-draft' == $status ) 1377 wp_die( __('Preview not available. Please save as a draft first.') ); 1385 $_POST['ID'] = $post_ID; 1378 1386 1379 if ( isset($_POST['catslist']) )1380 $_POST['post_category'] = explode(",", $_POST['catslist']);1387 if ( ! $post = get_post( $post_ID ) ) 1388 wp_die( __('You attempted to preview a non existing item.') ); 1381 1389 1382 if ( isset($_POST['tags_input']) )1383 $_POST['tags_input'] = explode(",", $_POST['tags_input']);1390 if ( ! current_user_can( 'edit_post', $post->ID ) ) 1391 wp_die( __('You are not allowed to preview this item.') ); 1384 1392 1385 if ( $_POST['post_type'] == 'page' || empty($_POST['post_category']) ) 1386 unset($_POST['post_category']); 1393 $is_autosave = false; 1387 1394 1388 $_POST['ID'] = $post_ID; 1389 $post = get_post($post_ID); 1390 1391 if ( 'page' == $post->post_type ) { 1392 if ( ! current_user_can('edit_page', $post_ID) ) 1393 wp_die( __('You are not allowed to edit this page.') ); 1395 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'draft' == $post->post_status || 'auto-draft' == $post->post_status ) ) { 1396 $saved_post_id = edit_post(); 1394 1397 } else { 1395 if ( ! current_user_can('edit_post', $post_ID) ) 1396 wp_die( __('You are not allowed to edit this post.') ); 1397 } 1398 $is_autosave = true; 1398 1399 1399 $user_id = get_current_user_id(); 1400 $locked = wp_check_post_lock( $post->ID ); 1401 if ( ! $locked && 'draft' == $post->post_status && $user_id == $post->post_author ) { 1402 $id = edit_post(); 1403 } else { // Non drafts are not overwritten. The autosave is stored in a special post revision. 1404 $id = wp_create_post_autosave( $post->ID ); 1405 if ( ! is_wp_error($id) ) 1406 $id = $post->ID; 1400 if ( 'auto-draft' == $_POST['post_status'] ) 1401 $_POST['post_status'] = 'draft'; 1402 1403 $saved_post_id = wp_create_post_autosave( $post->ID ); 1407 1404 } 1408 1405 1409 if ( is_wp_error( $id) )1410 wp_die( $ id->get_error_message() );1406 if ( is_wp_error( $saved_post_id ) ) 1407 wp_die( $saved_post_id->get_error_message() ); 1411 1408 1412 if ( ! $locked && $_POST['post_status'] == 'draft' && $user_id == $post->post_author ) { 1413 $url = add_query_arg( 'preview', 'true', get_permalink($id) ); 1414 } else { 1415 $nonce = wp_create_nonce('post_preview_' . $id); 1416 $args = array( 1417 'preview' => 'true', 1418 'preview_id' => $id, 1419 'preview_nonce' => $nonce, 1420 ); 1409 $query_args = array( 'preview' => 'true' ); 1421 1410 1411 if ( $is_autosave && $saved_post_id ) { 1412 $query_args['preview_id'] = $post->ID; 1413 $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID ); 1414 1422 1415 if ( isset( $_POST['post_format'] ) ) 1423 $args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); 1424 1425 $url = add_query_arg( $args, get_permalink($id) ); 1416 $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); 1426 1417 } 1427 1418 1419 $url = add_query_arg( $query_args, get_permalink( $post->ID ) ); 1428 1420 return apply_filters( 'preview_post_link', $url ); 1429 1421 } 1422 1423 /** 1424 * Save a post submitted with XHR 1425 * 1426 * Intended for use with heartbeat and autosave.js 1427 * 1428 * @since 3.7 1429 * 1430 * @param $post_data Associative array of the submitted post data. 1431 * @return mixed The value 0 or WP_Error on failure. The saved post ID on success. 1432 * Te ID can be either the draft post_id or the autosave revision post_id. 1433 */ 1434 function autosave( $post_data ) { 1435 // Back-compat 1436 if ( ! defined( 'DOING_AUTOSAVE' ) ) 1437 define( 'DOING_AUTOSAVE', true ); 1438 1439 $post_id = (int) $post_data['post_id']; 1440 $post_data['ID'] = $post_data['post_ID'] = $post_id; 1441 1442 if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) ) 1443 return new WP_Error( 'invalid_nonce', __('ERROR: invalid post data.') ); 1444 1445 $post = get_post( $post_id ); 1446 1447 if ( ! current_user_can( 'edit_post', $post->ID ) ) 1448 return new WP_Error( 'edit_post', __('You are not allowed to edit this item.') ); 1449 1450 if ( 'auto-draft' == $post->post_status ) 1451 $post_data['post_status'] = 'draft'; 1452 1453 if ( $post_data['post_type'] != 'page' && ! empty( $post_data['catslist'] ) ) 1454 $post_data['post_category'] = explode( ',', $post_data['catslist'] ); 1455 1456 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) { 1457 // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked 1458 return edit_post( $post_data ); 1459 } else { 1460 // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user. 1461 return wp_create_post_autosave( $post_data ); 1462 } 1463 } -
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.remote.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.remote.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; 355 370 356 371 postboxes.add_postbox_toggles(pagenow); 357 372 … … 680 695 } // end submitdiv 681 696 682 697 // 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(); 698 if ( $editSlugWrap.length ) { 699 function editPermalink() { 700 var i, c = 0, e = $('#editable-post-name'), revert_e = e.html(), real_slug = $('#post_name'), 701 revert_slug = real_slug.val(), b = $('#edit-slug-buttons'), revert_b = b.html(), 702 full = $('#editable-post-name-full').html(); 686 703 687 704 $('#view-post-btn').hide(); 688 705 b.html('<a href="#" class="save button button-small">'+postL10n.ok+'</a> <a class="cancel" href="#">'+postL10n.cancel+'</a>'); … … 689 706 b.children('.save').click(function() { 690 707 var new_slug = e.children('input').val(); 691 708 if ( new_slug == $('#editable-post-name-full').text() ) { 692 return $(' .cancel', '#edit-slug-buttons').click();709 return $('#edit-slug-buttons .cancel').click(); 693 710 } 694 711 $.post(ajaxurl, { 695 712 action: 'sample-permalink', 696 post_id: post _id,713 post_id: postId, 697 714 new_slug: new_slug, 698 715 new_title: $('#title').val(), 699 716 samplepermalinknonce: $('#samplepermalinknonce').val() … … 707 724 } 708 725 b.html(revert_b); 709 726 real_slug.val(new_slug); 710 makeSlugeditClickable();711 727 $('#view-post-btn').show(); 712 728 }); 713 729 return false; 714 730 }); 715 731 716 $(' .cancel', '#edit-slug-buttons').click(function() {732 $('#edit-slug-buttons .cancel').click(function() { 717 733 $('#view-post-btn').show(); 718 734 e.html(revert_e); 719 735 b.html(revert_b); … … 743 759 }).focus(); 744 760 } 745 761 746 makeSlugeditClickable = function() { 747 $('#editable-post-name').click(function() { 748 $('#edit-slug-buttons').children('.edit-slug').click(); 749 }); 750 } 751 makeSlugeditClickable(); 762 $editSlugWrap.on( 'click', function( event ) { 763 var $target = $( event.target ); 764 765 if ( $target.is('#editable-post-name') || $target.hasClass('edit-slug') ) { 766 editPermalink(); 767 } 768 }); 752 769 } 753 770 754 771 // word count -
src/wp-admin/post.php
296 296 break; 297 297 298 298 case 'preview': 299 check_admin_referer( ' autosave', 'autosavenonce');299 check_admin_referer( 'update-post_' . $post_id ); 300 300 301 301 $url = post_preview(); 302 302 -
src/wp-includes/js/autosave.js
1 var autosave, autosaveLast = '', autosavePeriodical, autosaveDelayPreview = false, notSaved = true, blockSave = false, fullscreen, autosaveLockRelease = true;1 window.wp = window.wp || {}; 2 2 3 jQuery(document).ready( function($) { 3 ( function( $, window ) { 4 var $document = $(document), _disabled, _blockSave, _blockSaveTimeout; 4 5 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 */ 11 wp.autosave = { 12 getPostData: function( type ) { 13 var post_name, parent_id, cats = [], data, 14 editor = typeof tinymce != 'undefined' && tinymce.get('content'); 14 15 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 16 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; 52 20 } else { 53 compareString = wp.autosave.getCompareString();21 tinymce.triggerSave(); 54 22 } 55 56 if ( compareString != autosaveLast )57 return autosaveL10n.saveAlert;58 23 } 59 };60 24 61 $(window).unload( function(e) { 62 if ( ! autosaveLockRelease ) 63 return; 25 data = { 26 post_id: $('#post_ID').val() || 0, 27 post_type: $('#post_type').val() || '', 28 post_author: $('#post_author').val() || '', 29 excerpt: $('#excerpt').val() || '' 30 }; 64 31 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; 32 if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) { 33 data['post_title'] = $('#wp-fullscreen-title').val() || ''; 34 data['content'] = $('#wp_mce_fullscreen').val() || ''; 35 } else { 36 data['post_title'] = $('#title').val() || ''; 37 data['content'] = $('#content').val() || ''; 88 38 } 89 doPreview();90 return false;91 });92 39 93 doPreview = function() { 94 $('input#wp-preview').val('dopreview'); 95 $('form#post').attr('target', 'wp-preview').submit().attr('target', ''); 40 if ( type == 'local' ) 41 return data; 96 42 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(); 43 $('input[id^="in-category-"]:checked').each( function() { 44 cats.push( this.value ); 140 45 }); 141 }46 data['catslist'] = cats.join(','); 142 47 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 }); 48 if ( post_name = $('#post_name').val() ) 49 data['post_name'] = post_name; 158 50 159 function autosave_parse_response( response ) { 160 var res = wpAjax.parseAjaxResponse(response, 'autosave'), post_id, sup;51 if ( parent_id = $('#parent_id').val() ) 52 data['parent_id'] = parent_id; 161 53 162 if ( res && res.responses && res.responses.length ) { 163 if ( res.responses[0].supplemental ) { 164 sup = res.responses[0].supplemental; 54 if ( $('#comment_status').prop('checked') ) 55 data['comment_status'] = 'open'; 165 56 166 jQuery.each( sup, function( selector, value ) { 167 if ( selector.match(/^replace-/) ) 168 jQuery( '#' + selector.replace('replace-', '') ).val( value ); 169 }); 170 } 57 if ( $('#ping_status').prop('checked') ) 58 data['ping_status'] = 'open'; 171 59 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 ); 60 if ( $('#auto_draft').val() == '1' ) 61 data['auto_draft'] = '1'; 176 62 177 if ( res.responses[0].data ) // update autosave message 178 jQuery('.autosave-message').text( res.responses[0].data ); 179 } 180 } 63 return data; 64 }, 181 65 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 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 || '' ); 204 70 } 205 71 206 autosave_enable_buttons(); 72 return ( $('#title').val() || '' ) + '::' + ( $('#content').val() || '' ) + '::' + ( $('#excerpt').val() || '' ); 73 }, 207 74 208 if ( autosaveDelayPreview ) { 209 autosaveDelayPreview = false; 210 doPreview(); 211 } 212 } else { 213 autosave_enable_buttons(); // re-enable disabled form buttons 214 } 215 } 75 enableButtons: function() { 76 $document.trigger('autosave-enable-buttons'); 216 77 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'); 78 if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) { 79 var parent = $('#submitpost'); 80 parent.find(':button, :submit, a.submitdelete, #post-preview').prop('disabled', false).removeClass('button-disabled'); 253 81 parent.find('.spinner').hide(); 254 }, 500); 255 } 256 } 82 } 83 }, 257 84 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 } 85 disableButtons: function() { 86 $document.trigger('autosave-disable-buttons'); 264 87 265 function delayed_autosave() { 266 setTimeout(function(){ 267 if ( blockSave ) 268 return; 269 autosave(); 270 }, 200); 271 } 88 var parent = $('#submitpost'); 89 parent.find(':button, :submit, a.submitdelete, #post-preview').prop('disabled', true).addClass('button-disabled'); 90 parent.find('#save-action .spinner').show(); 272 91 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 92 // Re-enable 5 sec later. Just gives autosave a head start to avoid collisions. 93 setTimeout( wp.autosave.enableButtons, 5000 ); 304 94 } 95 }; 305 96 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 97 // Autosave in localStorage 318 98 // 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 99 wp.autosave.local = { 397 398 100 lastSavedData: '', 399 101 blog_id: 0, 400 102 hasStorage: false, … … 502 204 save: function( data ) { 503 205 var result = false, post_data, compareString; 504 206 505 if ( ! data ) { 506 post_data = wp.autosave.getPostData(); 507 } else { 207 if ( data ) { 508 208 post_data = this.getData() || {}; 509 209 $.extend( post_data, data ); 510 post_data.autosave = true; 210 } else { 211 post_data = wp.autosave.getPostData('local'); 212 // Cannot get the post data at the moment 213 if ( ! post_data ) 214 return false; 511 215 } 512 216 513 // Cannot get the post data at the moment514 if ( ! post_data.autosave )515 return false;516 517 217 compareString = wp.autosave.getCompareString( post_data ); 518 218 519 219 // If the content, title and excerpt did not change since the last save, don't save again … … 697 397 } 698 398 }; 699 399 400 // Autosave on the server 401 wp.autosave.remote = { 402 releaseLock: true, 403 autosaveLast: '', 404 nextTime: 0, 405 406 init: function() { 407 var self = this, $submitpost = $('#submitpost'); 408 409 $document.on( 'heartbeat-send.autosave', function( event, data ) { 410 var autosaveData = self.getData(); 411 412 if ( autosaveData ) { 413 data.autosave = autosaveData; 414 } 415 }).on( 'heartbeat-tick.autosave', function( event, data ) { 416 if ( data.autosave ) { 417 self.response( data.autosave ); 418 } 419 }).on('heartbeat-connection-lost.autosave', function( event, error ) { 420 // When connection is lost, keep user from submitting changes. 421 if ( 'timeout' === error ) { 422 var notice = $('#lost-connection-notice'); 423 if ( ! wp.autosave.local.hasStorage ) { 424 notice.find('.hide-if-no-sessionstorage').hide(); 425 } 426 notice.show(); 427 wp.autosave.disableButtons(); 428 } 429 }).on('heartbeat-connection-restored.autosave', function() { 430 $('#lost-connection-notice').hide(); 431 wp.autosave.enableButtons(); 432 }); 433 434 $(window).on( 'beforeunload.autosave', function() { 435 var editor = typeof(tinymce) != 'undefined' && tinymce.activeEditor, compareString; 436 437 if ( editor && ! editor.isHidden() ) { 438 if ( editor.isDirty() ) 439 return autosaveL10n.saveAlert; 440 } else { 441 if ( fullscreen && fullscreen.settings.visible ) { 442 compareString = wp.autosave.getCompareString({ 443 post_title: $('#wp-fullscreen-title').val() || '', 444 content: $('#wp_mce_fullscreen').val() || '', 445 excerpt: $('#excerpt').val() || '' 446 }); 447 } else { 448 compareString = wp.autosave.getCompareString(); 449 } 450 451 if ( compareString != self.autosaveLast ) 452 return autosaveL10n.saveAlert; 453 } 454 }).on( 'unload.autosave', function( event ) { 455 if ( ! self.releaseLock ) 456 return; 457 458 // unload fires (twice) on removing the Thickbox iframe. Make sure we process only the main document unload. 459 if ( event.target && event.target.nodeName != '#document' ) 460 return; 461 462 $.ajax({ 463 type: 'POST', 464 url: ajaxurl, 465 async: false, 466 data: { 467 action: 'wp-remove-post-lock', 468 _wpnonce: $('#_wpnonce').val(), 469 post_ID: $('#post_ID').val(), 470 active_post_lock: $('#active_post_lock').val() 471 } 472 }); 473 }); 474 475 $document.ready( function() { 476 self._schedule(); 477 478 if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) { 479 self.autosaveLast = wp.autosave.getCompareString({ 480 post_title : $('#title').val() || '', 481 content : switchEditors.pre_wpautop( $('#content').val() ) || '', 482 excerpt : $('#excerpt').val() || '' 483 }); 484 } else { 485 self.autosaveLast = wp.autosave.getCompareString(); 486 } 487 488 // The form is being submitted by the user 489 $submitpost.find( 'input[type="submit"], a.submitdelete' ).on( 'click.autosave', function( event ) { 490 var $this = $(this); 491 492 if ( $this.prop('disabled') ) { 493 event.preventDefault(); // For the "Move to Trash" link 494 return; 495 } 496 497 self.blockSave(); 498 self.releaseLock = false; 499 $(window).off( 'beforeunload.autosave' ); 500 501 $submitpost.find('a.submitdelete, #post-preview').addClass('button-disabled'); 502 503 if ( $this.attr('id') == 'publish' ) 504 $submitpost.find('#major-publishing-actions .spinner').show(); 505 else 506 $submitpost.find('#minor-publishing .spinner').show(); 507 }); 508 509 $('#post-preview').on( 'click.post-preview', function( event ) { 510 if ( ! $(this).prop('disabled') ) { 511 self.doPreview(); 512 } 513 event.preventDefault(); 514 }); 515 516 // This code is meant to allow tabbing from Title to Post content. 517 $('#title').on( 'keydown.editor-focus', function( event ) { 518 var editor; 519 520 if ( event.which == 9 && ! event.ctrlKey && ! event.altKey && ! event.shiftKey ) { 521 if ( typeof(tinymce) != 'undefined' ) 522 editor = tinymce.get('content'); 523 524 if ( editor && ! editor.isHidden() ) { 525 $(this).one( 'keyup', function() { 526 $('#content_tbl td.mceToolbar > a').focus(); 527 }); 528 } else { 529 $('#content').focus(); 530 } 531 532 event.preventDefault(); 533 } 534 }); 535 536 // Autosave new posts after a title is typed 537 if ( $('#auto_draft').val() ) { 538 $('#title').blur( function() { 539 if ( ! this.value || $('#auto_draft').val() != '1' ) 540 return; 541 542 self.triggerSave(); 543 }); 544 } 545 }); 546 }, 547 548 // Block saving for the next 15 sec. 549 blockSave: function() { 550 _blockSave = true; 551 window.clearTimeout( _blockSaveTimeout ); 552 _blockSaveTimeout = window.setTimeout( function() { 553 _blockSave = false; 554 }, 15000 ); 555 }, 556 557 // Submit the form saving a draft or an autosave, and show a preview in a new tab 558 doPreview: function() { 559 this.blockSave(); 560 $('input#wp-preview').val('dopreview'); 561 $('form#post').attr('target', 'wp-preview').submit().attr('target', ''); 562 563 // Workaround for WebKit bug preventing a form submitting twice to the same action. 564 // https://bugs.webkit.org/show_bug.cgi?id=28633 565 var ua = navigator.userAgent.toLowerCase(); 566 if ( ua.indexOf('safari') != -1 && ua.indexOf('chrome') == -1 ) { 567 $('form#post').attr( 'action', function( index, value ) { 568 return value + '?t=' + new Date().getTime(); 569 }); 570 } 571 572 $('input#wp-preview').val(''); 573 }, 574 575 // Runs on heartbeat-response 576 response: function( data ) { 577 this._schedule(); 578 _blockSave = false; 579 this.autosaveLast = this.lastCompareString; 580 this.lastCompareString = ''; 581 582 $(document).trigger( 'after-autosave', [data] ); 583 $('#auto_draft').val(''); // No longer an auto-draft 584 $('.autosave-message').text( data.message ); 585 586 wp.autosave.enableButtons(); // re-enable disabled form buttons 587 }, 588 589 disable: function() { 590 _disabled = true; 591 }, 592 593 _schedule: function() { 594 this.nextTime = ( new Date() ).getTime() + ( autosaveL10n.autosaveInterval * 1000 ) || 60000; 595 }, 596 597 // Set heartbeat to connect fast (once) and save on the next tick. 598 triggerSave: function() { 599 this.nextTime = 0; 600 wp.heartbeat.interval( 'fast', 1 ); 601 }, 602 603 // Runs on 'heartbeat-send' 604 getData: function() { 605 var compareString, postData; 606 607 if ( _disabled || _blockSave ) 608 return false; 609 610 if ( ( new Date() ).getTime() < this.nextTime ) 611 return false; 612 613 postData = wp.autosave.getPostData(); 614 615 // post_data.content cannot be retrieved at the moment 616 if ( ! postData ) 617 return false; 618 619 compareString = wp.autosave.getCompareString( postData ); 620 621 // Nothing to save or no change. 622 if ( compareString == this.autosaveLast ) 623 return false; 624 625 this.blockSave(); 626 wp.autosave.disableButtons(); 627 this.lastCompareString = compareString; 628 629 $(document).trigger( 'wpcountwords', [ postData['content'] ] ); 630 $(document).trigger( 'before-autosave', [ postData ] ); 631 632 $('.autosave-message').text( autosaveL10n.savingText ); 633 postData._wpnonce = $('#_wpnonce').val() || ''; 634 635 return postData; 636 } 637 }; 638 700 639 wp.autosave.local.init(); 640 wp.autosave.remote.init(); 701 641 702 }( jQuery));642 }( jQuery, window )); -
src/wp-includes/js/heartbeat.js
183 183 timeout: 30000, // throw an error if not completed after 30 sec. 184 184 data: send, 185 185 dataType: 'json' 186 }).always( function() { 187 connecting = false; 188 next(); 186 189 }).done( function( response, textStatus, jqXHR ) { 187 190 var new_interval; 188 191 … … 209 212 // do this last, can trigger the next XHR if connection time > 5 sec. and new_interval == 'fast' 210 213 if ( new_interval ) 211 214 self.interval.call( self, new_interval ); 212 }).always( function() {213 connecting = false;214 next();215 215 }).fail( function( jqXHR, textStatus, error ) { 216 216 errorstate( textStatus || 'unknown' ); 217 217 self.error( jqXHR, textStatus, error ); -
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' ) );