WordPress.org

Make WordPress Core

Ticket #23220: 23220-4.patch

File 23220-4.patch, 19.0 KB (added by azaozz, 5 years ago)
  • 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

     
    11var showNotice, adminMenu, columns, validateForm, screenMeta;
     2window.wp = window.wp || {};
     3
    24(function($){
    35// Removed in 3.3.
    46// (perhaps) needed for back-compat
     
    424426        return true;
    425427});
    426428
    427 })(jQuery);
     429// Autosave in localStorage
     430// set as simple object/mixin for now
     431wp.autosave = wp.autosave || {};
     432
     433// Returns only post ID, title, content and excerpt for local autosaves, the rest of the fields for remote
     434wp.autosave.getPostData = function( local ) {
     435        var ed = typeof tinymce != 'undefined' ? tinymce.activeEditor : null, post_name, parent_id, cats = [],
     436                data = {
     437                        autosave: true,
     438                        post_id: $('#post_ID').val() || 0
     439                };
     440
     441        if ( ed && !ed.isHidden() ) {
     442                // Don't run while the tinymce spellcheck is on. It resets all found words.
     443                if ( ed.plugins.spellchecker && ed.plugins.spellchecker.active ) {
     444                        data.autosave = false;
     445                        return data;
     446                } else {
     447                        if ( 'mce_fullscreen' == ed.id )
     448                                tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
     449
     450                        tinymce.triggerSave();
     451                }
     452        }
     453
     454        if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) {
     455                data['post_title'] = $('#wp-fullscreen-title').val() || '';
     456                data['content'] = $('#wp_mce_fullscreen').val() || '';
     457        } else {
     458                data['post_title'] = $('#title').val() || '';
     459                data['content'] = $('#content').val() || '';
     460        }
     461
     462        data['excerpt'] = $('#excerpt').val() || '';
     463
     464        if ( local )
     465                return data;
     466
     467        $.extend( data, {
     468                action: 'autosave',
     469                autosavenonce: $('#autosavenonce').val() || '',
     470                post_type: $('#post_type').val() || '',
     471                post_author: $('#post_author').val() || ''
     472        });
     473
     474        $('.tags-input').each( function() {
     475                data[this.name] = this.value;
     476        });
     477
     478        $('input[id^="in-category-"]:checked').each( function() {
     479                cats.push(this.value);
     480        });
     481        data['catslist'] = cats.join(',');
     482
     483        if ( post_name = $('#post_name').val() )
     484                data['post_name'] = post_name;
     485
     486        if ( parent_id = $('#parent_id').val() )
     487                data['parent_id'] = parent_id;
     488
     489        if ( $('#comment_status').prop('checked') )
     490                data['comment_status'] = 'open';
     491
     492        if ( $('#ping_status').prop('checked') )
     493                data['ping_status'] = 'open';
     494
     495        if ( $('#auto_draft').val() == '1' )
     496                data['auto_draft'] = '1';
     497
     498        return data;
     499}
     500
     501// simple object/mixin for now
     502wp.autosave.local = {
     503
     504        lastsaveddata: '',
     505        unexpired: {},
     506        blog_id: 0,
     507        hasStorage: (function() {
     508                var test = Math.random(), result;
     509                // Check if the browser supports localStorage and it's not disabled
     510                try {
     511                        localStorage.setItem('wp-test', test);
     512                        result = localStorage.getItem('wp-test') == test;
     513                        localStorage.removeItem('wp-test');
     514                        return result;
     515                } catch(e) {
     516                        return false;
     517                }
     518    }()),
     519
     520        /**
     521         * Initialize the local storage
     522         * @return mixed simple object or false if no localStorage in the browser
     523         */
     524        getStorage: function() {
     525                var storage = false;
     526               
     527                // separate local storage containers for each blog_id
     528                if ( this.hasStorage && this.blog_id ) {
     529                        storage = localStorage.getItem( 'wp_autosave_' + this.blog_id );
     530
     531                        if ( storage )
     532                                storage = JSON.parse( storage );
     533                        else
     534                                storage = {};
     535                }
     536
     537                return storage;
     538        },
     539
     540        setStorage: function( storage_obj ) {
     541                if ( this.hasStorage )
     542                        return localStorage.setItem( 'wp_autosave_' + this.blog_id, JSON.stringify( storage_obj ) );
     543        },
     544       
     545        getData: function( post_id ) {
     546                var storage = this.getStorage();
     547
     548                post_id = post_id || $('#post_ID').val();
     549
     550                if ( !storage || !post_id )
     551                        return false;
     552
     553                return storage[ 'post_' + post_id ] || [];
     554        },
     555
     556        setData: function( stored_data ) {
     557                var storage = this.getStorage(), post_id = $('#post_ID').val(), result;
     558
     559                if ( !storage || !post_id )
     560                        return false;
     561
     562                storage[ 'post_' + post_id ] = stored_data;
     563                result = this.setStorage(storage);
     564
     565                // TODO do we need this?
     566                /*
     567                if ( result === false && stored_data.length > 1 ) {
     568                        // localStorage is full, pop the oldest revision and try again
     569                        while ( result === false && stored_data.length > 1 ) {
     570                                stored_data.pop();
     571                                storage[ 'post_' + post_id ] = stored_data;
     572                                result = this.setStorage(storage);
     573                        }
     574                }
     575                */
     576
     577                if ( result === false ) {
     578                        // localStorage is still full, throw error?
     579                        return false;
     580                }
     581
     582                return true;
     583        },
     584
     585        /**
     586         * Set post data for particular post id
     587         */
     588        save: function() {
     589                var stored_data, post_data, result = false;
     590
     591                // Prepares data for saving in local storage.
     592                post_data = wp.autosave.getPostData( true );
     593
     594                // If the content and title are empty or did not change since the last save, don't save again
     595                if ( ( ! post_data.post_title && ! post_data.content ) || post_data.post_title + post_data.content + post_data.excerpt == this.lastsaveddata )
     596                        return false;
     597
     598                // cannot save at this moment
     599                if ( !post_data.autosave )
     600                        return false;
     601
     602                $.extend( post_data, {
     603                        save_time: (new Date()).getTime(),
     604                        storage_status: 'fresh'
     605                });
     606
     607                stored_data = this.getData();
     608               
     609                if ( stored_data ) {
     610                        stored_data.unshift( post_data );
     611                       
     612                        if ( stored_data.length > 5 ) {
     613                                // Store only the 5 newest revisions
     614                                stored_data.pop();
     615                        }
     616console.log('saved at: '+post_data.save_time)                   
     617                        result = this.setData( stored_data );
     618                       
     619                        if ( result ) {
     620                                this.lastsaveddata = post_data.post_title + post_data.content;
     621                                // Update the local revisions as they are added
     622                                this.showItems();
     623                        }
     624                }
     625
     626                return result;
     627        },
     628
     629        /**
     630         * Checks if all revisions have expired. Removes expired revisions from local storage after 24 hours.
     631         */
     632        cleanup: function() { 
     633                var self = this, storage = this.getStorage(), current_time = (new Date()).getTime(), deleted = false, has_unexpired = false; 
     634
     635                if ( !storage )
     636                        return;
     637
     638                $.each( storage, function( key, value ) {
     639                        var new_arr;
     640
     641                        if ( !value )
     642                                return;
     643
     644                        new_arr = $.grep( value, function( post_data ) {
     645                                if ( post_data.storage_status ) {
     646                                        if ( !self.unexpired[key] )
     647                                                self.unexpired[key] = post_data;
     648
     649                                        has_unexpired = true;
     650                                        return true;
     651                                }
     652                                // keep if less than 24 hr
     653                                if ( ( current_time - post_data.save_time ) < 86400000 )
     654                                        return true;
     655
     656                                return false;
     657                        });
     658
     659                        if ( new_arr.length != value.length )
     660                                deleted = true;
     661
     662                        if ( new_arr.length )
     663                                storage[key] = new_arr;
     664                        else
     665                                delete storage[key];
     666                });
     667
     668                if ( deleted )
     669                        this.setStorage( storage );
     670
     671                if ( has_unexpired ) {
     672                        // show warning about unexpired post data
     673                        this.showWarning();
     674                }
     675        },
     676
     677        /**
     678         * Marks post data as expired
     679         *
     680         * For use after remote autosave has completed
     681         */
     682        expire: function() {
     683                var stored_data = this.getData();
     684
     685                $.each( stored_data, function( key, post_data ) {
     686                        delete post_data.storage_status;
     687                });
     688
     689                return this.setData( stored_data );
     690        },
     691
     692        showItems: function() {
     693                var self = this, data;
     694
     695                function formatTime( milisec ) {
     696                        var sec = parseInt( milisec / 1000, 10 ) || 0,
     697                                h = Math.floor( sec / 3600 ),
     698                                m = Math.floor( (sec % 3600) / 60 ),
     699                                s = sec - ( (h * 3600) + (m * 60) );
     700
     701                        function zeroize(n) {
     702                                if ( !n )
     703                                        return '00';
     704
     705                                if ( n < 10 )
     706                                        return '0' + n.toString();
     707
     708                                return n.toString();
     709                        }
     710
     711                        return {
     712                                h: zeroize(h),
     713                                m: zeroize(m),
     714                                s: zeroize(s)
     715                        };
     716                }
     717
     718                if ( window.pagenow && window.pagenow == 'post' ) {
     719                        data = this.getData(), current_time = (new Date()).getTime();
     720
     721                        if ( data && data.length ) {
     722                                $.each( data, function( key, post_data ) {
     723                                        var timeSaved = formatTime( current_time - post_data.save_time ),
     724                                                element = $('#localsave-items #localsave-item-' + key);
     725console.log(element)
     726                                        $('.localsave-no-revisions').remove();
     727
     728                                        // can do something here with post_data.storage_status to show non-expired
     729                                        // (not saved to the server) backups with different color
     730
     731                                        if ( ! element.length ) {
     732                                                element = $('.localsave-item-main').clone().removeClass('localsave-item-main').attr( 'id', 'localsave-item-' + key ).data('key', key);
     733
     734                                                $('.localsave-text', element).html( $('.localsave-text', element).text().replace( /%s/, function(){
     735                                                        return '<span class="localsave-h">' + timeSaved.h + '</span>:' +
     736                                                                '<span class="localsave-m">' + timeSaved.m + '</span>:' +
     737                                                                '<span class="localsave-s">' + timeSaved.s + '</span>';
     738                                                } ) );
     739
     740                                                $('#localsave-items').append( element.data('key', key).show() );
     741                                        } else {
     742                                                element.data('key', key);
     743                                                $('.localsave-h', element).text( timeSaved.h );
     744                                                $('.localsave-m', element).text( timeSaved.m );
     745                                                $('.localsave-s', element).text( timeSaved.s );
     746                                        }
     747                                });
     748                        }
     749                }
     750        },
     751
     752        preview: function( key ) {
     753                var data = this.getData(), content, element;
     754
     755                if ( !data[key] )
     756                        return; // error message?
     757
     758                content = data[key].content;
     759
     760                if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' ) {
     761                        content = '<div>' + switchEditors.wpautop( content ) + '</div>';
     762                } else if ( $('#wp-content-wrap').hasClass('html-active') )
     763                        content = '<textarea readonly>' + this.esc_html( content ) + '</textarea>';
     764
     765                element = $('.localsave-preview-wrap').show();
     766                element.find('div.localsave-preview').html( content );
     767                element.find('a.localsave-do-restore').data('key', key);
     768                $('#localsave-item-' + key).append( element );
     769        },
     770
     771        closePreview: function() {
     772                var element = $('.localsave-preview-wrap').css('display', '');
     773
     774                element.find('div.localsave-preview').empty();
     775                element.find('a.localsave-do-restore').removeData('key');
     776                $('#localsave-items').append( element );
     777        },
     778
     779        restore: function( key ) {
     780                var data = this.getData(), content, editor;
     781
     782                if ( !data[key] )
     783                        return; // error message?
     784
     785                content = data[key].content;
     786
     787                if ( typeof tinymce != 'undefined' )
     788                        editor = tinymce.get('content');
     789
     790                if ( typeof switchEditors != 'undefined' && editor && ! editor.isHidden() ) {
     791                        editor.setContent( switchEditors.wpautop( content ) );
     792                } else if ( ( editor = $('textarea#content') ) && editor.length ) {
     793                        editor.val( content );
     794                }
     795
     796                this.closePreview();
     797        },
     798
     799        init: function() {
     800                var self = this, post_data = wp.autosave.getPostData( true );
     801
     802                this.blog_id = typeof window.autosaveL10n != 'undefined' ? window.autosaveL10n.blog_id : 0;
     803               
     804                $('#localsave-items').on( 'click.localsave-items', function(e) {
     805                        var target = $(e.target);
     806
     807                        if ( target.hasClass('localsave-text') ) {
     808                                self.preview( target.parent().data('key') );
     809                        } else if ( target.hasClass('localsave-do-close') ) {
     810                                self.closePreview();
     811                        } else if ( target.hasClass('localsave-do-restore') ) {
     812                                self.restore( target.data('key') );
     813                        }
     814                        // Stop all links, including links in the preview
     815                        e.preventDefault();
     816                });
     817               
     818                // Set the comparison string
     819                if ( $('#wp-content-wrap').hasClass('tmce-active') && typeof switchEditors != 'undefined' )
     820                        this.lastsaveddata = post_data.post_title + switchEditors.pre_wpautop(post_data.content);
     821                else
     822                        this.lastsaveddata = post_data.post_title + post_data.content;
     823
     824                // Set the schedule
     825                this.schedule = $.schedule({
     826                        time: 15 * 1000,
     827                        func: function() { wp.autosave.local.save(); },
     828                        repeat: true,
     829                        protect: true
     830                });
     831               
     832                $('form#post').on( 'submit.localsave-cancel', function() {
     833                $.cancel( self.schedule );
     834                });
     835               
     836                // Show the local revisions
     837                this.showItems();
     838        },
     839
     840        /**
     841         * Escape html so we can show in div
     842         * @param string str
     843         * @return string
     844         */
     845        esc_html: function( str ) {
     846                return $('<div/>').text(str).html();
     847        },
     848
     849        /**
     850         * Show warning that one has historical saved data available for recovery in case one wants.
     851         */
     852        showWarning: function() {
     853                var self = this, current_post_id = $('#post_ID').val() || '', html = '', same_screen = false;
     854
     855                $.each( this.unexpired, function( key, post_data ) {
     856                        var title = post_data.post_title || '#' + post_data.post_id;
     857
     858                        if ( window.pagenow && window.pagenow == 'post' && current_post_id == post_data.post_id ) {
     859                                html += '<span class="localsave-recover"> <a class="localsave-recover-local" href="#revisionsdiv">' + self.esc_html(title) + '</a> </span> ';
     860                                same_screen = true;
     861                        } else {
     862                                html += '<span class="localsave-recover"> <a href="post.php?post='+post_data.post_id+'&action=edit&localsave-recover=1#revisionsdiv">' +
     863                                        self.esc_html(title) + '</a> </span> ';
     864                        }
     865                });
     866
     867                if ( html ) {
     868                        $('#wpbody .wrap h2:first').after(
     869                                '<div class="error" id="localsave-warning">' +
     870                                '<p><strong>Data saved in your browser</strong></p>' +
     871                                '<p>' + html + '</p>' +
     872                                '</div>'
     873                        );
     874
     875                        if ( same_screen ) {
     876                                $('#localsave-warning a.localsave-recover-local').click( function(){
     877                                        // Make sure the Revisions postbox is open and not hidden
     878                                        $('#revisionsdiv').removeClass('closed').show();
     879                                        // Hide the warning
     880                                        $('#localsave-warning').slideUp('fast', function(){ $(this).remove(); });
     881                                });
     882                        }
     883                }
     884        }
     885}
     886
     887$(document).ready( function() {
     888        wp.autosave.local.cleanup();
     889        // run on post.php and post-new.php
     890        if ( 'post' == window.pagenow )
     891                wp.autosave.local.init();
     892});
     893
     894}(jQuery));
     895
  • wp-includes/js/autosave.js

     
    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

     
    582582        wp_localize_script( 'autosave', 'autosaveL10n', array(
    583583                'autosaveInterval' => AUTOSAVE_INTERVAL,
    584584                'savingText' => __('Saving Draft&#8230;'),
    585                 'saveAlert' => __('The changes you made will be lost if you navigate away from this page.')
     585                'saveAlert' => __('The changes you made will be lost if you navigate away from this page.'),
     586                'blog_id' => get_current_blog_id()
    586587        ) );
    587588
    588589}