WordPress.org

Make WordPress Core

Ticket #23220: 23220-5.patch

File 23220-5.patch, 20.3 KB (added by azaozz, 5 years ago)
  • wp-admin/admin-header.php

     
    4545wp_enqueue_style( 'colors' );
    4646wp_enqueue_style( 'ie' );
    4747wp_enqueue_script('utils');
     48wp_enqueue_script('autosave-local');
    4849
    4950$admin_body_class = preg_replace('/[^a-z0-9_-]+/i', '-', $hook_suffix);
    5051?>
  • wp-admin/css/wp-admin.css

     
    34533453        text-decoration: none;
    34543454}
    34553455
     3456#revisionsdiv h4.revisions-title {
     3457        margin-bottom: 0.7em;
     3458}
     3459
     3460#revisionsdiv ul.post-revisions,
     3461#revisionsdiv p.no-revisions,
     3462#localsave-items .localsave-text {
     3463        margin: 0 10px;
     3464}
     3465
     3466#localsave-items .localsave-item,
     3467#localsave-items .localsave-preview-wrap {
     3468        display: none;
     3469}
     3470
     3471#localsave-items {
     3472        margin-bottom: 15px;
     3473}
     3474
     3475#localsave-items .localsave-item {
     3476        margin-bottom: 6px;
     3477}
     3478
     3479#localsave-items .localsave-preview-wrap {
     3480        background-color: #eee;
     3481        border-bottom: 1px solid #dfdfdf;
     3482        border-top: 1px solid #dfdfdf;
     3483        margin: 10px -12px 10px -10px;
     3484}
     3485
     3486#localsave-items .localsave-preview {
     3487        padding: 10px;
     3488        margin: 15px 15px 10px;
     3489        height: auto;
     3490        min-height: 200px;
     3491        max-height: 500px;
     3492        overflow: auto;
     3493        background: #fff;
     3494        border: 1px solid #dfdfdf;
     3495        border-radius: 3px;
     3496}
     3497
     3498#localsave-items .localsave-preview > div {
     3499        padding: 0 10px;
     3500}
     3501
     3502#localsave-items .localsave-preview > textarea {
     3503        width: 100%;
     3504        max-width: 100%;
     3505        height: 250px;
     3506        border: 0 none;
     3507        box-shadow: none;
     3508}
     3509
     3510#localsave-items .localsave-preview-wrap > p {
     3511        padding: 0 15px;
     3512}
     3513
     3514#localsave-items .localsave-preview-wrap > p strong {
     3515        color: #c00;
     3516}
     3517
     3518#localsave-items .localsave-time {
     3519        font-size: 1.2em;
     3520}
     3521
    34563522/*------------------------------------------------------------------------------
    34573523  11.3 - Featured Images
    34583524------------------------------------------------------------------------------*/
  • wp-admin/edit-form-advanced.php

     
    159159                add_meta_box('authordiv', __('Author'), 'post_author_meta_box', null, 'normal', 'core');
    160160}
    161161
    162 if ( post_type_supports($post_type, 'revisions') && 0 < $post_ID && wp_get_post_revisions( $post_ID ) )
     162if ( post_type_supports($post_type, 'revisions') && 0 < $post_ID )
    163163        add_meta_box('revisionsdiv', __('Revisions'), 'post_revisions_meta_box', null, 'normal', 'core');
    164164
    165165do_action('add_meta_boxes', $post_type, $post);
  • wp-admin/includes/meta-boxes.php

     
    603603 * @param object $post
    604604 */
    605605function post_revisions_meta_box($post) {
    606         wp_list_post_revisions();
     606        if ( wp_get_post_revisions( $post->ID ) ) {
     607                wp_list_post_revisions();
     608        } else {
     609                ?>
     610                <h4 class="revisions-title"><?php _e('Post revisions'); ?></h4>
     611                <p class="no-revisions"><?php _e('(no revisions yet)'); ?></p>
     612                <?php
     613        }
     614
     615        ?>
     616        <h4 class="revisions-title"><?php _e('Local backups'); ?></h4>
     617        <div id="localsave-items">
     618                <p class="no-revisions localsave-no-revisions"><?php _e('(no backups yet)'); ?></p>
     619
     620                <div class="localsave-item localsave-item-main">
     621                        <a href="#" class="localsave-text"><?php _e('Saved %s hours ago'); ?></a>
     622                </div>
     623
     624                <div class="localsave-preview-wrap">
     625                        <div class="localsave-preview"></div>
     626                        <p class="warning"><?php _e('<strong>Warning:</strong> restoring a backup will replace the current content in the editor.') ?></p>
     627                        <p class="localsave-preview-actions">
     628                                <a href="#" class="button localsave-do-close"><?php _e('Close'); ?></a>
     629                                <a href="#" class="alignright button button-primary localsave-do-restore"><?php _e('Restore'); ?></a>
     630                        </p>
     631                </div>
     632        </div>
     633        <?php
    607634}
    608635
    609636// -- Page related Meta Boxes
     
    9791006function post_thumbnail_meta_box( $post ) {
    9801007        $thumbnail_id = get_post_meta( $post->ID, '_thumbnail_id', true );
    9811008        echo _wp_post_thumbnail_html( $thumbnail_id, $post->ID );
    982 }
    983  No newline at end of file
     1009}
  • wp-admin/js/common.js

     
    424424        return true;
    425425});
    426426
    427 })(jQuery);
     427}(jQuery));
  • wp-includes/js/autosave-local.js

     
     1// Autosave in localStorage
     2// set as simple object/mixin for now
     3window.wp = window.wp || {};
     4wp.autosave = wp.autosave || {};
     5
     6(function($){
     7// Returns only post ID, title, content and excerpt for local autosaves, the rest of the fields for remote
     8wp.autosave.getPostData = function( local ) {
     9        var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [],
     10                data = {
     11                        autosave: true,
     12                        post_id: $('#post_ID').val() || 0
     13                };
     14
     15        if ( ed && !ed.isHidden() ) {
     16                // Don't run while the tinymce spellcheck is on. It resets all found words.
     17                if ( ed.plugins.spellchecker && ed.plugins.spellchecker.active ) {
     18                        data.autosave = false;
     19                        return data;
     20                } else {
     21                        if ( 'mce_fullscreen' == ed.id )
     22                                tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
     23
     24                        tinymce.triggerSave();
     25                }
     26        }
     27
     28        if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) {
     29                data['post_title'] = $('#wp-fullscreen-title').val() || '';
     30                data['content'] = $('#wp_mce_fullscreen').val() || '';
     31        } else {
     32                data['post_title'] = $('#title').val() || '';
     33                data['content'] = $('#content').val() || '';
     34        }
     35
     36        data['excerpt'] = $('#excerpt').val() || '';
     37
     38        if ( local )
     39                return data;
     40
     41        $.extend( data, {
     42                action: 'autosave',
     43                autosavenonce: $('#autosavenonce').val() || '',
     44                post_type: $('#post_type').val() || '',
     45                post_author: $('#post_author').val() || ''
     46        });
     47
     48        $('.tags-input').each( function() {
     49                data[this.name] = this.value;
     50        });
     51
     52        $('input[id^="in-category-"]:checked').each( function() {
     53                cats.push(this.value);
     54        });
     55        data['catslist'] = cats.join(',');
     56
     57        if ( post_name = $('#post_name').val() )
     58                data['post_name'] = post_name;
     59
     60        if ( parent_id = $('#parent_id').val() )
     61                data['parent_id'] = parent_id;
     62
     63        if ( $('#comment_status').prop('checked') )
     64                data['comment_status'] = 'open';
     65
     66        if ( $('#ping_status').prop('checked') )
     67                data['ping_status'] = 'open';
     68
     69        if ( $('#auto_draft').val() == '1' )
     70                data['auto_draft'] = '1';
     71
     72        return data;
     73}
     74
     75wp.autosave.local = {
     76
     77        lastsaveddata: '',
     78        unexpired: {},
     79        blog_id: typeof window.autosaveLocal != 'undefined' ? window.autosaveLocal.blog_id : 0,
     80
     81        // Check if the browser supports localStorage and it's not disabled
     82        hasStorage: (function() {
     83                var test = Math.random(), result;
     84
     85                try {
     86                        localStorage.setItem('wp-test', test);
     87                        result = localStorage.getItem('wp-test') == test;
     88                        localStorage.removeItem('wp-test');
     89                        return result;
     90                } catch(e) {
     91                        return false;
     92                }
     93    }()),
     94
     95        /**
     96         * Initialize the local storage
     97         * @return mixed simple object or false if no localStorage in the browser
     98         */
     99        getStorage: function() {
     100                var storage = false;
     101
     102                // separate local storage containers for each blog_id
     103                if ( this.hasStorage && this.blog_id ) {
     104                        storage = localStorage.getItem( 'wp_autosave_' + this.blog_id );
     105
     106                        if ( storage )
     107                                storage = JSON.parse( storage );
     108                        else
     109                                storage = {};
     110                }
     111
     112                return storage;
     113        },
     114
     115        setStorage: function( storage_obj ) {
     116                if ( this.hasStorage )
     117                        return localStorage.setItem( 'wp_autosave_' + this.blog_id, JSON.stringify( storage_obj ) );
     118        },
     119
     120        getData: function( post_id ) {
     121                var storage = this.getStorage();
     122
     123                post_id = post_id || $('#post_ID').val();
     124
     125                if ( !storage || !post_id )
     126                        return false;
     127
     128                return storage[ 'post_' + post_id ] || [];
     129        },
     130
     131        setData: function( stored_data ) {
     132                var storage = this.getStorage(), post_id = $('#post_ID').val(), result;
     133
     134                if ( !storage || !post_id )
     135                        return false;
     136
     137                storage[ 'post_' + post_id ] = stored_data;
     138                result = this.setStorage(storage);
     139
     140                if ( result === false ) {
     141                        // localStorage is full, try running the cleanup to free some space
     142                        this.cleanup();
     143                        result = this.setStorage(storage);
     144                }
     145
     146                if ( result === false ) {
     147                        // localStorage is still full, show a warning?
     148                        // A warning will need a good explanation how the user can empty the storage or give more space, different for each browser...
     149                        return false;
     150                }
     151
     152                return true;
     153        },
     154
     155        /**
     156         * Set post data for particular post id
     157         */
     158        save: function() {
     159                var stored_data, post_data, result = false;
     160
     161                // Prepares data for saving in local storage.
     162                post_data = wp.autosave.getPostData( true );
     163
     164                // If the content and title are empty or did not change since the last save, don't save again
     165                if ( ( ! post_data.post_title && ! post_data.content ) || post_data.post_title + post_data.content + post_data.excerpt == this.lastsaveddata )
     166                        return false;
     167
     168                // Cannot get the post data at this moment
     169                if ( !post_data.autosave )
     170                        return false;
     171
     172                $.extend( post_data, {
     173                        save_time: (new Date()).getTime(),
     174                        storage_status: 'fresh'
     175                });
     176
     177                stored_data = this.getData();
     178
     179                if ( stored_data ) {
     180                        if ( stored_data.length > 4 ) {
     181                                // Store only the 5 newest revisions
     182                                stored_data.pop();
     183                        }
     184
     185                        stored_data.unshift( post_data );
     186                        result = this.setData( stored_data );
     187
     188                        if ( result ) {
     189                                this.lastsaveddata = post_data.post_title + post_data.content;
     190                                // Update the display of local backups as they are added
     191                                this.showItems();
     192                        }
     193                }
     194
     195                return result;
     196        },
     197
     198        /**
     199         * Checks if all revisions have expired. Removes expired revisions from local storage after 24 hours.
     200         */
     201        cleanup: function() { 
     202                var self = this, storage = this.getStorage(), current_time = (new Date()).getTime(), deleted = false, has_unexpired = false; 
     203
     204                if ( !storage )
     205                        return;
     206
     207                $.each( storage, function( key, value ) {
     208                        var new_arr;
     209
     210                        if ( !value )
     211                                return;
     212
     213                        new_arr = $.grep( value, function( post_data ) {
     214                                if ( post_data.storage_status ) {
     215                                        if ( !self.unexpired[key] )
     216                                                self.unexpired[key] = post_data;
     217
     218                                        has_unexpired = true;
     219                                        return true;
     220                                }
     221                                // keep if less than 24 hr
     222                                if ( ( current_time - post_data.save_time ) < 86400000 )
     223                                        return true;
     224
     225                                return false;
     226                        });
     227
     228                        if ( new_arr.length != value.length )
     229                                deleted = true;
     230
     231                        if ( new_arr.length )
     232                                storage[key] = new_arr;
     233                        else
     234                                delete storage[key];
     235                });
     236
     237                if ( deleted )
     238                        this.setStorage( storage );
     239
     240                if ( has_unexpired && !this.warning_done ) {
     241                        // show warning about unexpired post data
     242                        this.showWarning();
     243                        this.warning_done = true;
     244                }
     245        },
     246
     247        /**
     248         * Marks post data as expired
     249         *
     250         * For use after remote autosave has completed
     251         */
     252        expire: function( key ) {
     253                var stored_data = this.getData();
     254
     255                if ( ! isNaN( key ) ) {
     256                        delete stored_data[key].storage_status;
     257                } else {
     258                        $.each( stored_data, function( key, post_data ) {
     259                                delete post_data.storage_status;
     260                        });
     261                }
     262
     263                return this.setData( stored_data );
     264        },
     265
     266        showItems: function() {
     267                var self = this, data;
     268
     269                function formatTime( milisec ) {
     270                        var sec = parseInt( milisec / 1000, 10 ) || 0,
     271                                h = Math.floor( sec / 3600 ),
     272                                m = Math.floor( (sec % 3600) / 60 ),
     273                                s = sec - ( (h * 3600) + (m * 60) );
     274
     275                        function zeroize(n) {
     276                                if ( !n )
     277                                        return '00';
     278
     279                                if ( n < 10 )
     280                                        return '0' + n.toString();
     281
     282                                return n.toString();
     283                        }
     284
     285                        return {
     286                                h: zeroize(h),
     287                                m: zeroize(m),
     288                                s: zeroize(s)
     289                        };
     290                }
     291
     292                if ( window.pagenow && window.pagenow == 'post' ) {
     293                        data = this.getData(), current_time = (new Date()).getTime();
     294
     295                        if ( data && data.length ) {
     296                                $.each( data, function( key, post_data ) {
     297                                        var timeSaved = formatTime( current_time - post_data.save_time ),
     298                                                element = $('#localsave-items #localsave-item-' + key);
     299
     300                                        $('.localsave-no-revisions').remove();
     301
     302                                        // can do something here with post_data.storage_status to show non-expired
     303                                        // (not saved to the server) backups with different color, etc.
     304
     305                                        if ( ! element.length ) {
     306                                                element = $('.localsave-item-main').clone().removeClass('localsave-item-main').attr( 'id', 'localsave-item-' + key );
     307
     308                                                $('.localsave-text', element).html( $('.localsave-text', element).text().replace( /%s/, function(){
     309                                                        return '<span class="localsave-h">' + timeSaved.h + '</span>:' +
     310                                                                '<span class="localsave-m">' + timeSaved.m + '</span>:' +
     311                                                                '<span class="localsave-s">' + timeSaved.s + '</span>';
     312                                                } ) );
     313
     314                                                $('#localsave-items').append( element.data('key', key).show() );
     315                                        } else {
     316                                                element.data('key', key);
     317                                                $('.localsave-h', element).text( timeSaved.h );
     318                                                $('.localsave-m', element).text( timeSaved.m );
     319                                                $('.localsave-s', element).text( timeSaved.s );
     320                                        }
     321                                });
     322                        }
     323                }
     324        },
     325
     326        preview: function( key ) {
     327                var data = this.getData(), content, element;
     328
     329                if ( !data[key] )
     330                        return; // error message?
     331
     332                this.expire( key );
     333                content = data[key].content;
     334
     335                if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) {
     336                        content = '<div>' + switchEditors.wpautop( content ) + '</div>';
     337                } else if ( $('#wp-content-wrap').hasClass('html-active') )
     338                        content = '<textarea readonly>' + this.esc_html( content ) + '</textarea>';
     339
     340                element = $('.localsave-preview-wrap').show();
     341                element.find('div.localsave-preview').html( content );
     342                element.find('a.localsave-do-restore').data('key', key);
     343                $('#localsave-item-' + key).append( element );
     344        },
     345
     346        closePreview: function() {
     347                var element = $('.localsave-preview-wrap').css('display', '');
     348
     349                element.find('div.localsave-preview').empty();
     350                element.find('a.localsave-do-restore').removeData('key');
     351                $('#localsave-items').append( element );
     352        },
     353
     354        restore: function( key ) {
     355                var data = this.getData(), content, editor;
     356
     357                if ( !data[key] )
     358                        return; // error message?
     359
     360                content = data[key].content;
     361
     362                if ( typeof tinymce != 'undefined' )
     363                        editor = tinymce.get('content');
     364
     365                if ( typeof switchEditors != 'undefined' && editor && ! editor.isHidden() ) {
     366                        editor.setContent( switchEditors.wpautop( content ) );
     367                } else if ( ( editor = $('textarea#content') ) && editor.length ) {
     368                        editor.val( content );
     369                }
     370
     371                this.closePreview();
     372        },
     373
     374        init: function() {
     375                var self = this, post_data;
     376
     377                $('#localsave-items').on( 'click.localsave-items', function(e) {
     378                        var target = $(e.target);
     379
     380                        if ( target.hasClass('localsave-text') ) {
     381                                self.preview( target.parent().data('key') );
     382                        } else if ( target.hasClass('localsave-do-close') ) {
     383                                self.closePreview();
     384                        } else if ( target.hasClass('localsave-do-restore') ) {
     385                                self.restore( target.data('key') );
     386                        }
     387                        // Stop all links, including links in the preview
     388                        e.preventDefault();
     389                });
     390
     391                post_data = wp.autosave.getPostData( true );
     392                // Set the comparison string
     393                if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' )
     394                        this.lastsaveddata = post_data.post_title + switchEditors.pre_wpautop(post_data.content);
     395                else
     396                        this.lastsaveddata = post_data.post_title + post_data.content;
     397
     398                // Set the schedule
     399                this.schedule = $.schedule({
     400                        time: 15 * 1000,
     401                        func: function() { wp.autosave.local.save(); },
     402                        repeat: true,
     403                        protect: true
     404                });
     405
     406                $('form#post').on( 'submit.localsave-submit', function() {
     407                self.expire();
     408                        $.cancel( self.schedule );
     409                });
     410
     411                // Show the local revisions
     412                this.showItems();
     413        },
     414
     415        /**
     416         * Escape html so we can show in div
     417         * @param string str
     418         * @return string
     419         */
     420        esc_html: function( str ) {
     421                return $('<div/>').text(str).html();
     422        },
     423
     424        /**
     425         * Show warning that one has historical saved data available for recovery in case one wants.
     426         */
     427        showWarning: function() {
     428                var self = this, current_post_id = $('#post_ID').val() || '', html = '', same_screen = false;
     429
     430                $.each( this.unexpired, function( key, post_data ) {
     431                        var title = post_data.post_title || '#' + post_data.post_id;
     432
     433                        if ( window.pagenow && window.pagenow == 'post' && current_post_id == post_data.post_id ) {
     434                                html += ' <span class="localsave-recover"> <a class="localsave-recover-local" href="#revisionsdiv">' + self.esc_html(title) + '</a> </span> ';
     435                                same_screen = true;
     436                        } else {
     437                                html += ' <span class="localsave-recover"> <a href="post.php?post='+post_data.post_id+'&action=edit&localsave-recover=1#revisionsdiv">' +
     438                                        self.esc_html(title) + '</a> </span> ';
     439                        }
     440                });
     441
     442                if ( html && window.autosaveLocal ) {
     443                        $('#wpbody .wrap h2:first').after(
     444                                '<div class="error" id="localsave-warning">' +
     445                                '<p>' + window.autosaveLocal.warning + html +
     446                                '</p></div>'
     447                        );
     448
     449                        if ( same_screen ) {
     450                                $('#localsave-warning a.localsave-recover-local').click( function(){
     451                                        // Make sure the Revisions postbox is open and not hidden
     452                                        $('#revisionsdiv').removeClass('closed').show();
     453                                        // Hide the warning
     454                                        $('#localsave-warning').slideUp('fast', function(){ $(this).remove(); });
     455                                });
     456                        }
     457                }
     458        }
     459}
     460
     461$(document).ready( function() {
     462        wp.autosave.local.cleanup();
     463        if ( window.pagenow && 'post' == window.pagenow )
     464                wp.autosave.local.init();
     465});
     466
     467}(jQuery));
  • wp-includes/js/autosave-local.js

  • wp-includes/js/autosave.js

    Property changes on: wp-includes/js/autosave-local.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    177177
    178178// called when autosaving pre-existing post
    179179function autosave_saved(response) {
     180
     181        if ( window.wp && window.wp.autosave )
     182                window.wp.autosave.local.expire();
     183
    180184        blockSave = false;
    181185        autosave_parse_response(response); // parse the ajax response
    182186        autosave_enable_buttons(); // re-enable disabled form buttons
     
    186190function autosave_saved_new(response) {
    187191        blockSave = false;
    188192        var res = autosave_parse_response(response), postID;
     193
     194        if ( window.wp && window.wp.autosave )
     195                window.wp.autosave.local.expire();
     196
    189197        if ( res && res.responses.length && !res.errors ) {
    190198                // An ID is sent only for real auto-saves, not for autosave=0 "keepalive" saves
    191199                postID = parseInt( res.responses[0].id, 10 );
  • wp-includes/post-template.php

     
    14551455
    14561456<?php
    14571457        else :
     1458                echo '<h4 class="revisions-title">' . __('Post revisions') . "</h4>\n";
    14581459                echo "<ul class='post-revisions'>\n";
    14591460                echo $rows;
    14601461                echo "</ul>";
  • wp-includes/script-loader.php

     
    108108
    109109        $scripts->add( 'autosave', "/wp-includes/js/autosave$suffix.js", array('schedule', 'wp-ajax-response'), false, 1 );
    110110       
     111        $scripts->add( 'autosave-local', "/wp-includes/js/autosave-local$suffix.js", array('schedule'), false, 1 );
     112        did_action( 'init' ) && $scripts->localize( 'autosave-local', 'autosaveLocal', array(
     113                'blog_id' => get_current_blog_id(),
     114                'warning' => __('<strong>Warning:</strong> It appears there is some data saved in your browser that is not saved to your site. Please review:')
     115        ) );
     116
    111117        $scripts->add( 'heartbeat', "/wp-includes/js/heartbeat$suffix.js", array('jquery'), false, 1 );
    112118        did_action( 'init' ) && $scripts->localize( 'heartbeat', 'heartbeatSettings',
    113119                apply_filters( 'heartbeat_settings', array() )