From 4ad27d6d144ff29000f043e84b02bb2229a619c5 Mon Sep 17 00:00:00 2001 From: Andrew Ozz Date: Wed, 13 Mar 2013 10:08:16 +0000 Subject: [PATCH] Autosave to the browser's sessionStorage, compare this autosave to the post content on page load and let the user restore it when the data is not the same. First run, see #23220 git-svn-id: https://develop.svn.wordpress.org/trunk@23683 602fd350-edb4-49c9-b593-d223f7449a82 --- wp-admin/edit-form-advanced.php | 2 +- wp-admin/includes/ajax-actions.php | 10 +- wp-admin/includes/misc.php | 26 ++ wp-includes/js/admin-bar.js | 23 ++ wp-includes/js/autosave.js | 440 +++++++++++++++++++++++++---- wp-includes/script-loader.php | 5 +- wp-login.php | 9 + 7 files changed, 449 insertions(+), 66 deletions(-) diff --git a/wp-admin/edit-form-advanced.php b/wp-admin/edit-form-advanced.php index 744f3b2978..6df923493e 100644 --- a/wp-admin/edit-form-advanced.php +++ b/wp-admin/edit-form-advanced.php @@ -301,7 +301,7 @@ if ( isset( $post_new_file ) && current_user_can( $post_type_object->cap->create echo ' ' . esc_html( $post_type_object->labels->add_new ) . ''; ?> -

+

diff --git a/wp-admin/includes/ajax-actions.php b/wp-admin/includes/ajax-actions.php index 6d587657f4..3724bfe9ef 100644 --- a/wp-admin/includes/ajax-actions.php +++ b/wp-admin/includes/ajax-actions.php @@ -1050,9 +1050,9 @@ function wp_ajax_autosave() { $id = $revision_id = 0; - $post_ID = (int) $_POST['post_ID']; - $_POST['ID'] = $post_ID; - $post = get_post($post_ID); + $post_id = (int) $_POST['post_id']; + $_POST['ID'] = $_POST['post_ID'] = $post_id; + $post = get_post($post_id); if ( 'auto-draft' == $post->post_status ) $_POST['post_status'] = 'draft'; @@ -1068,10 +1068,10 @@ function wp_ajax_autosave() { } if ( 'page' == $post->post_type ) { - if ( !current_user_can('edit_page', $post_ID) ) + if ( !current_user_can('edit_page', $post->ID) ) wp_die( __( 'You are not allowed to edit this page.' ) ); } else { - if ( !current_user_can('edit_post', $post_ID) ) + if ( !current_user_can('edit_post', $post->ID) ) wp_die( __( 'You are not allowed to edit this post.' ) ); } diff --git a/wp-admin/includes/misc.php b/wp-admin/includes/misc.php index 8f7aa2200c..f954c5b6fa 100644 --- a/wp-admin/includes/misc.php +++ b/wp-admin/includes/misc.php @@ -632,3 +632,29 @@ function wp_refresh_post_lock( $response, $data, $screen_id ) { return $response; } add_filter( 'heartbeat_received', 'wp_refresh_post_lock', 10, 3 ); + +/** + * Output the HTML for restoring the post data from DOM storage + * + * @since 3.6 + * @access private + */ +function _local_storage_notice() { + $screen = get_current_screen(); + if ( ! $screen || 'post' != $screen->id ) + return; + + ?> + + ]+>/g, ''); + + return string.replace(/[\x20\t\r\n\f]+/g, ''); + } + + return ( remove( str1 || '', strip_tags ) == remove( str2 || '', strip_tags ) ); + }, + + /** + * Check if the saved data for the current post (if any) is different than the loaded post data on the screen + * + * Shows a standard message letting the user restore the post data if different. + * + * @return void + */ + checkPost: function() { + var self = this, post_data = this.getData(), content, check_data, strip_tags = false, notice; + + if ( ! post_data ) + return; + + // There is a newer autosave. Don't show two "restore" notices at the same time. + if ( $('#has-newer-autosave').length ) + return; + + content = $('#content').val(); + check_data = $.extend( {}, post_data ); + + if ( $('#wp-content-wrap').hasClass('tmce-active') ) + content = switchEditors.pre_wpautop( content ); + + // The post has just been published, only compare text + if ( $('#post_status').val() == 'publish' && check_data.status != 'publish' ) + strip_tags = true; + + if ( this.compare( content, check_data.content, strip_tags ) && this.compare( $('#title').val(), check_data.post_title, strip_tags ) && this.compare( $('#excerpt').val(), check_data.excerpt, strip_tags ) ) + return; + + // We have three choices here: + // - Do an autosave and then show the standard notice "There is an autosave newer than...". + // - Offer to load/restore the backed up post data. + // - Restore the post_data without asking, then show a notice with an Undo link/button. + // Doing an autosave will take few seconds and may take up to 30 and fail if network connectivity is bad + // Restoring the post will leave the user with the proper content, but it won't be saved to the server until the next autosave. + + this.restore_post_data = post_data; + this.undo_post_data = wp.autosave.getPostData(); + + /* + if ( $('#post_status').val() == 'publish' ) { + // Different message when a post is published? + // Comparing the current and saved post data may fail (false positive) when the post is published + // as in some cases there are changes to post_content on publishing and updating before saving to the DB. + } + */ + + notice = $('#local-storage-notice'); + $('form#post').before( notice.addClass('updated').show() ); + + notice.on( 'click', function(e) { + var target = $( e.target ); + + if ( target.hasClass('restore-backup') ) { + self.restorePost( self.restore_post_data ); + target.parent().hide(); + $(this).find('p.undo-restore').show(); + } else if ( target.hasClass('undo-restore-backup') ) { + self.restorePost( self.undo_post_data ); + target.parent().hide(); + $(this).find('p.local-restore').show(); + } + + e.preventDefault(); + }); + }, + + // Restore the current title, content and excerpt from post_data. + restorePost: function( post_data ) { + var editor; + + if ( post_data ) { + // Set the last saved data + this.lastsaveddata = post_data.post_title + ': ' + post_data.content; + + if ( $('#title').val() != post_data.post_title ) + $('#title').focus().val( post_data.post_title || '' ); + + $('#excerpt').val( post_data.excerpt || '' ); + editor = typeof tinymce != 'undefined' && tinymce.get('content'); + + if ( editor && ! editor.isHidden() ) { + // Make sure there's an undo level in the editor + editor.undoManager.add(); + editor.setContent( post_data.content ? switchEditors.wpautop( post_data.content ) : '' ); + } else { + // Make sure the Text editor is selected + $('#content-html').click(); + $('#content').val( post_data.content ); + } + + return true; + } + + return false; + } +} + +wp.autosave.local.init(); + +}(jQuery)); diff --git a/wp-includes/script-loader.php b/wp-includes/script-loader.php index 38c44889a1..e8c0cdd611 100644 --- a/wp-includes/script-loader.php +++ b/wp-includes/script-loader.php @@ -106,7 +106,7 @@ function wp_default_scripts( &$scripts ) { 'dismiss' => __('Dismiss'), ) ); - $scripts->add( 'autosave', "/wp-includes/js/autosave$suffix.js", array('schedule', 'wp-ajax-response'), false, 1 ); + $scripts->add( 'autosave', "/wp-includes/js/autosave$suffix.js", array('schedule', 'wp-ajax-response', 'editor'), false, 1 ); $scripts->add( 'heartbeat', "/wp-includes/js/heartbeat$suffix.js", array('jquery'), false, 1 ); did_action( 'init' ) && $scripts->localize( 'heartbeat', 'heartbeatSettings', @@ -585,7 +585,8 @@ function wp_just_in_time_script_localization() { wp_localize_script( 'autosave', 'autosaveL10n', array( 'autosaveInterval' => AUTOSAVE_INTERVAL, 'savingText' => __('Saving Draft…'), - 'saveAlert' => __('The changes you made will be lost if you navigate away from this page.') + 'saveAlert' => __('The changes you made will be lost if you navigate away from this page.'), + 'blog_id' => get_current_blog_id(), ) ); } diff --git a/wp-login.php b/wp-login.php index 10ff47bd39..f26731eab1 100644 --- a/wp-login.php +++ b/wp-login.php @@ -68,6 +68,15 @@ function login_header($title = 'Log In', $message = '', $wp_error = '') { get_error_code() ) { + ?> + +